LCOV - code coverage report
Current view: top level - src/backend/utils/adt - array_userfuncs.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 87.7 % 682 598
Test Date: 2026-04-07 14:16:30 Functions: 100.0 % 29 29
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * array_userfuncs.c
       4              :  *    Misc user-visible array support functions
       5              :  *
       6              :  * Copyright (c) 2003-2026, PostgreSQL Global Development Group
       7              :  *
       8              :  * IDENTIFICATION
       9              :  *    src/backend/utils/adt/array_userfuncs.c
      10              :  *
      11              :  *-------------------------------------------------------------------------
      12              :  */
      13              : #include "postgres.h"
      14              : 
      15              : #include "catalog/pg_operator_d.h"
      16              : #include "catalog/pg_type.h"
      17              : #include "common/int.h"
      18              : #include "common/pg_prng.h"
      19              : #include "libpq/pqformat.h"
      20              : #include "miscadmin.h"
      21              : #include "nodes/supportnodes.h"
      22              : #include "port/pg_bitutils.h"
      23              : #include "utils/array.h"
      24              : #include "utils/builtins.h"
      25              : #include "utils/datum.h"
      26              : #include "utils/lsyscache.h"
      27              : #include "utils/memutils.h"
      28              : #include "utils/tuplesort.h"
      29              : #include "utils/typcache.h"
      30              : 
      31              : /*
      32              :  * SerialIOData
      33              :  *      Used for caching element-type data in array_agg_serialize
      34              :  */
      35              : typedef struct SerialIOData
      36              : {
      37              :     FmgrInfo    typsend;
      38              : } SerialIOData;
      39              : 
      40              : /*
      41              :  * DeserialIOData
      42              :  *      Used for caching element-type data in array_agg_deserialize
      43              :  */
      44              : typedef struct DeserialIOData
      45              : {
      46              :     FmgrInfo    typreceive;
      47              :     Oid         typioparam;
      48              : } DeserialIOData;
      49              : 
      50              : /*
      51              :  * ArraySortCachedInfo
      52              :  *      Used for caching catalog data in array_sort
      53              :  */
      54              : typedef struct ArraySortCachedInfo
      55              : {
      56              :     ArrayMetaState array_meta;  /* metadata for array_create_iterator */
      57              :     Oid         elem_lt_opr;    /* "<" operator for element type */
      58              :     Oid         elem_gt_opr;    /* ">" operator for element type */
      59              :     Oid         array_type;     /* pg_type OID of array type */
      60              : } ArraySortCachedInfo;
      61              : 
      62              : static Datum array_position_common(FunctionCallInfo fcinfo);
      63              : 
      64              : 
      65              : /*
      66              :  * fetch_array_arg_replace_nulls
      67              :  *
      68              :  * Fetch an array-valued argument in expanded form; if it's null, construct an
      69              :  * empty array value of the proper data type.  Also cache basic element type
      70              :  * information in fn_extra.
      71              :  *
      72              :  * Caution: if the input is a read/write pointer, this returns the input
      73              :  * argument; so callers must be sure that their changes are "safe", that is
      74              :  * they cannot leave the array in a corrupt state.
      75              :  *
      76              :  * If we're being called as an aggregate function, make sure any newly-made
      77              :  * expanded array is allocated in the aggregate state context, so as to save
      78              :  * copying operations.
      79              :  */
      80              : static ExpandedArrayHeader *
      81         1316 : fetch_array_arg_replace_nulls(FunctionCallInfo fcinfo, int argno)
      82              : {
      83              :     ExpandedArrayHeader *eah;
      84              :     Oid         element_type;
      85              :     ArrayMetaState *my_extra;
      86              :     MemoryContext resultcxt;
      87              : 
      88              :     /* If first time through, create datatype cache struct */
      89         1316 :     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
      90         1316 :     if (my_extra == NULL)
      91              :     {
      92              :         my_extra = (ArrayMetaState *)
      93          833 :             MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
      94              :                                sizeof(ArrayMetaState));
      95          833 :         my_extra->element_type = InvalidOid;
      96          833 :         fcinfo->flinfo->fn_extra = my_extra;
      97              :     }
      98              : 
      99              :     /* Figure out which context we want the result in */
     100         1316 :     if (!AggCheckCallContext(fcinfo, &resultcxt))
     101         1236 :         resultcxt = CurrentMemoryContext;
     102              : 
     103              :     /* Now collect the array value */
     104         1316 :     if (!PG_ARGISNULL(argno))
     105              :     {
     106         1299 :         MemoryContext oldcxt = MemoryContextSwitchTo(resultcxt);
     107              : 
     108         1299 :         eah = PG_GETARG_EXPANDED_ARRAYX(argno, my_extra);
     109         1299 :         MemoryContextSwitchTo(oldcxt);
     110              :     }
     111              :     else
     112              :     {
     113              :         /* We have to look up the array type and element type */
     114           17 :         Oid         arr_typeid = get_fn_expr_argtype(fcinfo->flinfo, argno);
     115              : 
     116           17 :         if (!OidIsValid(arr_typeid))
     117            0 :             ereport(ERROR,
     118              :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     119              :                      errmsg("could not determine input data type")));
     120           17 :         element_type = get_element_type(arr_typeid);
     121           17 :         if (!OidIsValid(element_type))
     122            0 :             ereport(ERROR,
     123              :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
     124              :                      errmsg("input data type is not an array")));
     125              : 
     126           17 :         eah = construct_empty_expanded_array(element_type,
     127              :                                              resultcxt,
     128              :                                              my_extra);
     129              :     }
     130              : 
     131         1316 :     return eah;
     132              : }
     133              : 
     134              : /*-----------------------------------------------------------------------------
     135              :  * array_append :
     136              :  *      push an element onto the end of a one-dimensional array
     137              :  *----------------------------------------------------------------------------
     138              :  */
     139              : Datum
     140         1253 : array_append(PG_FUNCTION_ARGS)
     141              : {
     142              :     ExpandedArrayHeader *eah;
     143              :     Datum       newelem;
     144              :     bool        isNull;
     145              :     Datum       result;
     146              :     int        *dimv,
     147              :                *lb;
     148              :     int         indx;
     149              :     ArrayMetaState *my_extra;
     150              : 
     151         1253 :     eah = fetch_array_arg_replace_nulls(fcinfo, 0);
     152         1253 :     isNull = PG_ARGISNULL(1);
     153         1253 :     if (isNull)
     154            0 :         newelem = (Datum) 0;
     155              :     else
     156         1253 :         newelem = PG_GETARG_DATUM(1);
     157              : 
     158         1253 :     if (eah->ndims == 1)
     159              :     {
     160              :         /* append newelem */
     161          982 :         lb = eah->lbound;
     162          982 :         dimv = eah->dims;
     163              : 
     164              :         /* index of added elem is at lb[0] + (dimv[0] - 1) + 1 */
     165          982 :         if (pg_add_s32_overflow(lb[0], dimv[0], &indx))
     166            0 :             ereport(ERROR,
     167              :                     (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     168              :                      errmsg("integer out of range")));
     169              :     }
     170          271 :     else if (eah->ndims == 0)
     171          271 :         indx = 1;
     172              :     else
     173            0 :         ereport(ERROR,
     174              :                 (errcode(ERRCODE_DATA_EXCEPTION),
     175              :                  errmsg("argument must be empty or one-dimensional array")));
     176              : 
     177              :     /* Perform element insertion */
     178         1253 :     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
     179              : 
     180         1253 :     result = array_set_element(EOHPGetRWDatum(&eah->hdr),
     181              :                                1, &indx, newelem, isNull,
     182         1253 :                                -1, my_extra->typlen, my_extra->typbyval, my_extra->typalign);
     183              : 
     184         1253 :     PG_RETURN_DATUM(result);
     185              : }
     186              : 
     187              : /*
     188              :  * array_append_support()
     189              :  *
     190              :  * Planner support function for array_append()
     191              :  */
     192              : Datum
     193          226 : array_append_support(PG_FUNCTION_ARGS)
     194              : {
     195          226 :     Node       *rawreq = (Node *) PG_GETARG_POINTER(0);
     196          226 :     Node       *ret = NULL;
     197              : 
     198          226 :     if (IsA(rawreq, SupportRequestModifyInPlace))
     199              :     {
     200              :         /*
     201              :          * We can optimize in-place appends if the function's array argument
     202              :          * is the array being assigned to.  We don't need to worry about array
     203              :          * references within the other argument.
     204              :          */
     205            1 :         SupportRequestModifyInPlace *req = (SupportRequestModifyInPlace *) rawreq;
     206            1 :         Param      *arg = (Param *) linitial(req->args);
     207              : 
     208            1 :         if (arg && IsA(arg, Param) &&
     209            1 :             arg->paramkind == PARAM_EXTERN &&
     210            1 :             arg->paramid == req->paramid)
     211            1 :             ret = (Node *) arg;
     212              :     }
     213              : 
     214          226 :     PG_RETURN_POINTER(ret);
     215              : }
     216              : 
     217              : /*-----------------------------------------------------------------------------
     218              :  * array_prepend :
     219              :  *      push an element onto the front of a one-dimensional array
     220              :  *----------------------------------------------------------------------------
     221              :  */
     222              : Datum
     223           63 : array_prepend(PG_FUNCTION_ARGS)
     224              : {
     225              :     ExpandedArrayHeader *eah;
     226              :     Datum       newelem;
     227              :     bool        isNull;
     228              :     Datum       result;
     229              :     int        *lb;
     230              :     int         indx;
     231              :     int         lb0;
     232              :     ArrayMetaState *my_extra;
     233              : 
     234           63 :     isNull = PG_ARGISNULL(0);
     235           63 :     if (isNull)
     236            0 :         newelem = (Datum) 0;
     237              :     else
     238           63 :         newelem = PG_GETARG_DATUM(0);
     239           63 :     eah = fetch_array_arg_replace_nulls(fcinfo, 1);
     240              : 
     241           63 :     if (eah->ndims == 1)
     242              :     {
     243              :         /* prepend newelem */
     244           63 :         lb = eah->lbound;
     245           63 :         lb0 = lb[0];
     246              : 
     247           63 :         if (pg_sub_s32_overflow(lb0, 1, &indx))
     248            0 :             ereport(ERROR,
     249              :                     (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     250              :                      errmsg("integer out of range")));
     251              :     }
     252            0 :     else if (eah->ndims == 0)
     253              :     {
     254            0 :         indx = 1;
     255            0 :         lb0 = 1;
     256              :     }
     257              :     else
     258            0 :         ereport(ERROR,
     259              :                 (errcode(ERRCODE_DATA_EXCEPTION),
     260              :                  errmsg("argument must be empty or one-dimensional array")));
     261              : 
     262              :     /* Perform element insertion */
     263           63 :     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
     264              : 
     265           63 :     result = array_set_element(EOHPGetRWDatum(&eah->hdr),
     266              :                                1, &indx, newelem, isNull,
     267           63 :                                -1, my_extra->typlen, my_extra->typbyval, my_extra->typalign);
     268              : 
     269              :     /* Readjust result's LB to match the input's, as expected for prepend */
     270              :     Assert(result == EOHPGetRWDatum(&eah->hdr));
     271           63 :     if (eah->ndims == 1)
     272              :     {
     273              :         /* This is ok whether we've deconstructed or not */
     274           63 :         eah->lbound[0] = lb0;
     275              :     }
     276              : 
     277           63 :     PG_RETURN_DATUM(result);
     278              : }
     279              : 
     280              : /*
     281              :  * array_prepend_support()
     282              :  *
     283              :  * Planner support function for array_prepend()
     284              :  */
     285              : Datum
     286           43 : array_prepend_support(PG_FUNCTION_ARGS)
     287              : {
     288           43 :     Node       *rawreq = (Node *) PG_GETARG_POINTER(0);
     289           43 :     Node       *ret = NULL;
     290              : 
     291           43 :     if (IsA(rawreq, SupportRequestModifyInPlace))
     292              :     {
     293              :         /*
     294              :          * We can optimize in-place prepends if the function's array argument
     295              :          * is the array being assigned to.  We don't need to worry about array
     296              :          * references within the other argument.
     297              :          */
     298            1 :         SupportRequestModifyInPlace *req = (SupportRequestModifyInPlace *) rawreq;
     299            1 :         Param      *arg = (Param *) lsecond(req->args);
     300              : 
     301            1 :         if (arg && IsA(arg, Param) &&
     302            1 :             arg->paramkind == PARAM_EXTERN &&
     303            1 :             arg->paramid == req->paramid)
     304            1 :             ret = (Node *) arg;
     305              :     }
     306              : 
     307           43 :     PG_RETURN_POINTER(ret);
     308              : }
     309              : 
     310              : /*-----------------------------------------------------------------------------
     311              :  * array_cat :
     312              :  *      concatenate two nD arrays to form an nD array, or
     313              :  *      push an (n-1)D array onto the end of an nD array
     314              :  *----------------------------------------------------------------------------
     315              :  */
     316              : Datum
     317        15560 : array_cat(PG_FUNCTION_ARGS)
     318              : {
     319              :     ArrayType  *v1,
     320              :                *v2;
     321              :     ArrayType  *result;
     322              :     int        *dims,
     323              :                *lbs,
     324              :                 ndims,
     325              :                 nitems,
     326              :                 ndatabytes,
     327              :                 nbytes;
     328              :     int        *dims1,
     329              :                *lbs1,
     330              :                 ndims1,
     331              :                 nitems1,
     332              :                 ndatabytes1;
     333              :     int        *dims2,
     334              :                *lbs2,
     335              :                 ndims2,
     336              :                 nitems2,
     337              :                 ndatabytes2;
     338              :     int         i;
     339              :     char       *dat1,
     340              :                *dat2;
     341              :     uint8      *bitmap1,
     342              :                *bitmap2;
     343              :     Oid         element_type;
     344              :     Oid         element_type1;
     345              :     Oid         element_type2;
     346              :     int32       dataoffset;
     347              : 
     348              :     /* Concatenating a null array is a no-op, just return the other input */
     349        15560 :     if (PG_ARGISNULL(0))
     350              :     {
     351         1199 :         if (PG_ARGISNULL(1))
     352            0 :             PG_RETURN_NULL();
     353         1199 :         result = PG_GETARG_ARRAYTYPE_P(1);
     354         1199 :         PG_RETURN_ARRAYTYPE_P(result);
     355              :     }
     356        14361 :     if (PG_ARGISNULL(1))
     357              :     {
     358            0 :         result = PG_GETARG_ARRAYTYPE_P(0);
     359            0 :         PG_RETURN_ARRAYTYPE_P(result);
     360              :     }
     361              : 
     362        14361 :     v1 = PG_GETARG_ARRAYTYPE_P(0);
     363        14361 :     v2 = PG_GETARG_ARRAYTYPE_P(1);
     364              : 
     365        14361 :     element_type1 = ARR_ELEMTYPE(v1);
     366        14361 :     element_type2 = ARR_ELEMTYPE(v2);
     367              : 
     368              :     /* Check we have matching element types */
     369        14361 :     if (element_type1 != element_type2)
     370            0 :         ereport(ERROR,
     371              :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
     372              :                  errmsg("cannot concatenate incompatible arrays"),
     373              :                  errdetail("Arrays with element types %s and %s are not "
     374              :                            "compatible for concatenation.",
     375              :                            format_type_be(element_type1),
     376              :                            format_type_be(element_type2))));
     377              : 
     378              :     /* OK, use it */
     379        14361 :     element_type = element_type1;
     380              : 
     381              :     /*----------
     382              :      * We must have one of the following combinations of inputs:
     383              :      * 1) one empty array, and one non-empty array
     384              :      * 2) both arrays empty
     385              :      * 3) two arrays with ndims1 == ndims2
     386              :      * 4) ndims1 == ndims2 - 1
     387              :      * 5) ndims1 == ndims2 + 1
     388              :      *----------
     389              :      */
     390        14361 :     ndims1 = ARR_NDIM(v1);
     391        14361 :     ndims2 = ARR_NDIM(v2);
     392              : 
     393              :     /*
     394              :      * short circuit - if one input array is empty, and the other is not, we
     395              :      * return the non-empty one as the result
     396              :      *
     397              :      * if both are empty, return the first one
     398              :      */
     399        14361 :     if (ndims1 == 0 && ndims2 > 0)
     400            0 :         PG_RETURN_ARRAYTYPE_P(v2);
     401              : 
     402        14361 :     if (ndims2 == 0)
     403           21 :         PG_RETURN_ARRAYTYPE_P(v1);
     404              : 
     405              :     /* the rest fall under rule 3, 4, or 5 */
     406        14340 :     if (ndims1 != ndims2 &&
     407           15 :         ndims1 != ndims2 - 1 &&
     408           10 :         ndims1 != ndims2 + 1)
     409            0 :         ereport(ERROR,
     410              :                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
     411              :                  errmsg("cannot concatenate incompatible arrays"),
     412              :                  errdetail("Arrays of %d and %d dimensions are not "
     413              :                            "compatible for concatenation.",
     414              :                            ndims1, ndims2)));
     415              : 
     416              :     /* get argument array details */
     417        14340 :     lbs1 = ARR_LBOUND(v1);
     418        14340 :     lbs2 = ARR_LBOUND(v2);
     419        14340 :     dims1 = ARR_DIMS(v1);
     420        14340 :     dims2 = ARR_DIMS(v2);
     421        14340 :     dat1 = ARR_DATA_PTR(v1);
     422        14340 :     dat2 = ARR_DATA_PTR(v2);
     423        14340 :     bitmap1 = ARR_NULLBITMAP(v1);
     424        14340 :     bitmap2 = ARR_NULLBITMAP(v2);
     425        14340 :     nitems1 = ArrayGetNItems(ndims1, dims1);
     426        14340 :     nitems2 = ArrayGetNItems(ndims2, dims2);
     427        14340 :     ndatabytes1 = ARR_SIZE(v1) - ARR_DATA_OFFSET(v1);
     428        14340 :     ndatabytes2 = ARR_SIZE(v2) - ARR_DATA_OFFSET(v2);
     429              : 
     430        14340 :     if (ndims1 == ndims2)
     431              :     {
     432              :         /*
     433              :          * resulting array is made up of the elements (possibly arrays
     434              :          * themselves) of the input argument arrays
     435              :          */
     436        14325 :         ndims = ndims1;
     437        14325 :         dims = palloc_array(int, ndims);
     438        14325 :         lbs = palloc_array(int, ndims);
     439              : 
     440        14325 :         dims[0] = dims1[0] + dims2[0];
     441        14325 :         lbs[0] = lbs1[0];
     442              : 
     443        14335 :         for (i = 1; i < ndims; i++)
     444              :         {
     445           10 :             if (dims1[i] != dims2[i] || lbs1[i] != lbs2[i])
     446            0 :                 ereport(ERROR,
     447              :                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
     448              :                          errmsg("cannot concatenate incompatible arrays"),
     449              :                          errdetail("Arrays with differing element dimensions are "
     450              :                                    "not compatible for concatenation.")));
     451              : 
     452           10 :             dims[i] = dims1[i];
     453           10 :             lbs[i] = lbs1[i];
     454              :         }
     455              :     }
     456           15 :     else if (ndims1 == ndims2 - 1)
     457              :     {
     458              :         /*
     459              :          * resulting array has the second argument as the outer array, with
     460              :          * the first argument inserted at the front of the outer dimension
     461              :          */
     462            5 :         ndims = ndims2;
     463            5 :         dims = palloc_array(int, ndims);
     464            5 :         lbs = palloc_array(int, ndims);
     465            5 :         memcpy(dims, dims2, ndims * sizeof(int));
     466            5 :         memcpy(lbs, lbs2, ndims * sizeof(int));
     467              : 
     468              :         /* increment number of elements in outer array */
     469            5 :         dims[0] += 1;
     470              : 
     471              :         /* make sure the added element matches our existing elements */
     472           10 :         for (i = 0; i < ndims1; i++)
     473              :         {
     474            5 :             if (dims1[i] != dims[i + 1] || lbs1[i] != lbs[i + 1])
     475            0 :                 ereport(ERROR,
     476              :                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
     477              :                          errmsg("cannot concatenate incompatible arrays"),
     478              :                          errdetail("Arrays with differing dimensions are not "
     479              :                                    "compatible for concatenation.")));
     480              :         }
     481              :     }
     482              :     else
     483              :     {
     484              :         /*
     485              :          * (ndims1 == ndims2 + 1)
     486              :          *
     487              :          * resulting array has the first argument as the outer array, with the
     488              :          * second argument appended to the end of the outer dimension
     489              :          */
     490           10 :         ndims = ndims1;
     491           10 :         dims = palloc_array(int, ndims);
     492           10 :         lbs = palloc_array(int, ndims);
     493           10 :         memcpy(dims, dims1, ndims * sizeof(int));
     494           10 :         memcpy(lbs, lbs1, ndims * sizeof(int));
     495              : 
     496              :         /* increment number of elements in outer array */
     497           10 :         dims[0] += 1;
     498              : 
     499              :         /* make sure the added element matches our existing elements */
     500           20 :         for (i = 0; i < ndims2; i++)
     501              :         {
     502           10 :             if (dims2[i] != dims[i + 1] || lbs2[i] != lbs[i + 1])
     503            0 :                 ereport(ERROR,
     504              :                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
     505              :                          errmsg("cannot concatenate incompatible arrays"),
     506              :                          errdetail("Arrays with differing dimensions are not "
     507              :                                    "compatible for concatenation.")));
     508              :         }
     509              :     }
     510              : 
     511              :     /* Do this mainly for overflow checking */
     512        14340 :     nitems = ArrayGetNItems(ndims, dims);
     513        14340 :     ArrayCheckBounds(ndims, dims, lbs);
     514              : 
     515              :     /* build the result array */
     516        14340 :     ndatabytes = ndatabytes1 + ndatabytes2;
     517        14340 :     if (ARR_HASNULL(v1) || ARR_HASNULL(v2))
     518              :     {
     519            0 :         dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
     520            0 :         nbytes = ndatabytes + dataoffset;
     521              :     }
     522              :     else
     523              :     {
     524        14340 :         dataoffset = 0;         /* marker for no null bitmap */
     525        14340 :         nbytes = ndatabytes + ARR_OVERHEAD_NONULLS(ndims);
     526              :     }
     527        14340 :     result = (ArrayType *) palloc0(nbytes);
     528        14340 :     SET_VARSIZE(result, nbytes);
     529        14340 :     result->ndim = ndims;
     530        14340 :     result->dataoffset = dataoffset;
     531        14340 :     result->elemtype = element_type;
     532        14340 :     memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
     533        14340 :     memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
     534              :     /* data area is arg1 then arg2 */
     535        14340 :     memcpy(ARR_DATA_PTR(result), dat1, ndatabytes1);
     536        14340 :     memcpy(ARR_DATA_PTR(result) + ndatabytes1, dat2, ndatabytes2);
     537              :     /* handle the null bitmap if needed */
     538        14340 :     if (ARR_HASNULL(result))
     539              :     {
     540            0 :         array_bitmap_copy(ARR_NULLBITMAP(result), 0,
     541              :                           bitmap1, 0,
     542              :                           nitems1);
     543            0 :         array_bitmap_copy(ARR_NULLBITMAP(result), nitems1,
     544              :                           bitmap2, 0,
     545              :                           nitems2);
     546              :     }
     547              : 
     548        14340 :     PG_RETURN_ARRAYTYPE_P(result);
     549              : }
     550              : 
     551              : 
     552              : /*
     553              :  * ARRAY_AGG(anynonarray) aggregate function
     554              :  */
     555              : Datum
     556      1482060 : array_agg_transfn(PG_FUNCTION_ARGS)
     557              : {
     558      1482060 :     Oid         arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
     559              :     MemoryContext aggcontext;
     560              :     ArrayBuildState *state;
     561              :     Datum       elem;
     562              : 
     563      1482060 :     if (arg1_typeid == InvalidOid)
     564            0 :         ereport(ERROR,
     565              :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     566              :                  errmsg("could not determine input data type")));
     567              : 
     568              :     /*
     569              :      * Note: we do not need a run-time check about whether arg1_typeid is a
     570              :      * valid array element type, because the parser would have verified that
     571              :      * while resolving the input/result types of this polymorphic aggregate.
     572              :      */
     573              : 
     574      1482060 :     if (!AggCheckCallContext(fcinfo, &aggcontext))
     575              :     {
     576              :         /* cannot be called directly because of internal-type argument */
     577            0 :         elog(ERROR, "array_agg_transfn called in non-aggregate context");
     578              :     }
     579              : 
     580      1482060 :     if (PG_ARGISNULL(0))
     581        76179 :         state = initArrayResult(arg1_typeid, aggcontext, false);
     582              :     else
     583      1405881 :         state = (ArrayBuildState *) PG_GETARG_POINTER(0);
     584              : 
     585      1482060 :     elem = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1);
     586              : 
     587      1482060 :     state = accumArrayResult(state,
     588              :                              elem,
     589      1482060 :                              PG_ARGISNULL(1),
     590              :                              arg1_typeid,
     591              :                              aggcontext);
     592              : 
     593              :     /*
     594              :      * The transition type for array_agg() is declared to be "internal", which
     595              :      * is a pass-by-value type the same size as a pointer.  So we can safely
     596              :      * pass the ArrayBuildState pointer through nodeAgg.c's machinations.
     597              :      */
     598      1482060 :     PG_RETURN_POINTER(state);
     599              : }
     600              : 
     601              : Datum
     602           70 : array_agg_combine(PG_FUNCTION_ARGS)
     603              : {
     604              :     ArrayBuildState *state1;
     605              :     ArrayBuildState *state2;
     606              :     MemoryContext agg_context;
     607              :     MemoryContext old_context;
     608              : 
     609           70 :     if (!AggCheckCallContext(fcinfo, &agg_context))
     610            0 :         elog(ERROR, "aggregate function called in non-aggregate context");
     611              : 
     612           70 :     state1 = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
     613           70 :     state2 = PG_ARGISNULL(1) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(1);
     614              : 
     615           70 :     if (state2 == NULL)
     616              :     {
     617              :         /*
     618              :          * NULL state2 is easy, just return state1, which we know is already
     619              :          * in the agg_context
     620              :          */
     621            0 :         if (state1 == NULL)
     622            0 :             PG_RETURN_NULL();
     623            0 :         PG_RETURN_POINTER(state1);
     624              :     }
     625              : 
     626           70 :     if (state1 == NULL)
     627              :     {
     628              :         /* We must copy state2's data into the agg_context */
     629           40 :         state1 = initArrayResultWithSize(state2->element_type, agg_context,
     630              :                                          false, state2->alen);
     631              : 
     632           40 :         old_context = MemoryContextSwitchTo(agg_context);
     633              : 
     634        11424 :         for (int i = 0; i < state2->nelems; i++)
     635              :         {
     636        11384 :             if (!state2->dnulls[i])
     637         8539 :                 state1->dvalues[i] = datumCopy(state2->dvalues[i],
     638         8539 :                                                state1->typbyval,
     639         8539 :                                                state1->typlen);
     640              :             else
     641         2845 :                 state1->dvalues[i] = (Datum) 0;
     642              :         }
     643              : 
     644           40 :         MemoryContextSwitchTo(old_context);
     645              : 
     646           40 :         memcpy(state1->dnulls, state2->dnulls, sizeof(bool) * state2->nelems);
     647              : 
     648           40 :         state1->nelems = state2->nelems;
     649              : 
     650           40 :         PG_RETURN_POINTER(state1);
     651              :     }
     652           30 :     else if (state2->nelems > 0)
     653              :     {
     654              :         /* We only need to combine the two states if state2 has any elements */
     655           30 :         int         reqsize = state1->nelems + state2->nelems;
     656           30 :         MemoryContext oldContext = MemoryContextSwitchTo(state1->mcontext);
     657              : 
     658              :         Assert(state1->element_type == state2->element_type);
     659              : 
     660              :         /* Enlarge state1 arrays if needed */
     661           30 :         if (state1->alen < reqsize)
     662              :         {
     663              :             /* Use a power of 2 size rather than allocating just reqsize */
     664           30 :             state1->alen = pg_nextpower2_32(reqsize);
     665           60 :             state1->dvalues = (Datum *) repalloc(state1->dvalues,
     666           30 :                                                  state1->alen * sizeof(Datum));
     667           30 :             state1->dnulls = (bool *) repalloc(state1->dnulls,
     668           30 :                                                state1->alen * sizeof(bool));
     669              :         }
     670              : 
     671              :         /* Copy in the state2 elements to the end of the state1 arrays */
     672         8646 :         for (int i = 0; i < state2->nelems; i++)
     673              :         {
     674         8616 :             if (!state2->dnulls[i])
     675         6461 :                 state1->dvalues[i + state1->nelems] =
     676         6461 :                     datumCopy(state2->dvalues[i],
     677         6461 :                               state1->typbyval,
     678         6461 :                               state1->typlen);
     679              :             else
     680         2155 :                 state1->dvalues[i + state1->nelems] = (Datum) 0;
     681              :         }
     682              : 
     683           30 :         memcpy(&state1->dnulls[state1->nelems], state2->dnulls,
     684           30 :                sizeof(bool) * state2->nelems);
     685              : 
     686           30 :         state1->nelems = reqsize;
     687              : 
     688           30 :         MemoryContextSwitchTo(oldContext);
     689              :     }
     690              : 
     691           30 :     PG_RETURN_POINTER(state1);
     692              : }
     693              : 
     694              : /*
     695              :  * array_agg_serialize
     696              :  *      Serialize ArrayBuildState into bytea.
     697              :  */
     698              : Datum
     699           70 : array_agg_serialize(PG_FUNCTION_ARGS)
     700              : {
     701              :     ArrayBuildState *state;
     702              :     StringInfoData buf;
     703              :     bytea      *result;
     704              : 
     705              :     /* cannot be called directly because of internal-type argument */
     706              :     Assert(AggCheckCallContext(fcinfo, NULL));
     707              : 
     708           70 :     state = (ArrayBuildState *) PG_GETARG_POINTER(0);
     709              : 
     710           70 :     pq_begintypsend(&buf);
     711              : 
     712              :     /*
     713              :      * element_type. Putting this first is more convenient in deserialization
     714              :      */
     715           70 :     pq_sendint32(&buf, state->element_type);
     716              : 
     717              :     /*
     718              :      * nelems -- send first so we know how large to make the dvalues and
     719              :      * dnulls array during deserialization.
     720              :      */
     721           70 :     pq_sendint64(&buf, state->nelems);
     722              : 
     723              :     /* alen can be decided during deserialization */
     724              : 
     725              :     /* typlen */
     726           70 :     pq_sendint16(&buf, state->typlen);
     727              : 
     728              :     /* typbyval */
     729           70 :     pq_sendbyte(&buf, state->typbyval);
     730              : 
     731              :     /* typalign */
     732           70 :     pq_sendbyte(&buf, state->typalign);
     733              : 
     734              :     /* dnulls */
     735           70 :     pq_sendbytes(&buf, state->dnulls, sizeof(bool) * state->nelems);
     736              : 
     737              :     /*
     738              :      * dvalues.  By agreement with array_agg_deserialize, when the element
     739              :      * type is byval, we just transmit the Datum array as-is, including any
     740              :      * null elements.  For by-ref types, we must invoke the element type's
     741              :      * send function, and we skip null elements (which is why the nulls flags
     742              :      * must be sent first).
     743              :      */
     744           70 :     if (state->typbyval)
     745           70 :         pq_sendbytes(&buf, state->dvalues, sizeof(Datum) * state->nelems);
     746              :     else
     747              :     {
     748              :         SerialIOData *iodata;
     749              :         int         i;
     750              : 
     751              :         /* Avoid repeat catalog lookups for typsend function */
     752            0 :         iodata = (SerialIOData *) fcinfo->flinfo->fn_extra;
     753            0 :         if (iodata == NULL)
     754              :         {
     755              :             Oid         typsend;
     756              :             bool        typisvarlena;
     757              : 
     758              :             iodata = (SerialIOData *)
     759            0 :                 MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
     760              :                                    sizeof(SerialIOData));
     761            0 :             getTypeBinaryOutputInfo(state->element_type, &typsend,
     762              :                                     &typisvarlena);
     763            0 :             fmgr_info_cxt(typsend, &iodata->typsend,
     764            0 :                           fcinfo->flinfo->fn_mcxt);
     765            0 :             fcinfo->flinfo->fn_extra = iodata;
     766              :         }
     767              : 
     768            0 :         for (i = 0; i < state->nelems; i++)
     769              :         {
     770              :             bytea      *outputbytes;
     771              : 
     772            0 :             if (state->dnulls[i])
     773            0 :                 continue;
     774            0 :             outputbytes = SendFunctionCall(&iodata->typsend,
     775            0 :                                            state->dvalues[i]);
     776            0 :             pq_sendint32(&buf, VARSIZE(outputbytes) - VARHDRSZ);
     777            0 :             pq_sendbytes(&buf, VARDATA(outputbytes),
     778            0 :                          VARSIZE(outputbytes) - VARHDRSZ);
     779              :         }
     780              :     }
     781              : 
     782           70 :     result = pq_endtypsend(&buf);
     783              : 
     784           70 :     PG_RETURN_BYTEA_P(result);
     785              : }
     786              : 
     787              : Datum
     788           70 : array_agg_deserialize(PG_FUNCTION_ARGS)
     789              : {
     790              :     bytea      *sstate;
     791              :     ArrayBuildState *result;
     792              :     StringInfoData buf;
     793              :     Oid         element_type;
     794              :     int64       nelems;
     795              :     const char *temp;
     796              : 
     797           70 :     if (!AggCheckCallContext(fcinfo, NULL))
     798            0 :         elog(ERROR, "aggregate function called in non-aggregate context");
     799              : 
     800           70 :     sstate = PG_GETARG_BYTEA_PP(0);
     801              : 
     802              :     /*
     803              :      * Initialize a StringInfo so that we can "receive" it using the standard
     804              :      * recv-function infrastructure.
     805              :      */
     806           70 :     initReadOnlyStringInfo(&buf, VARDATA_ANY(sstate),
     807           70 :                            VARSIZE_ANY_EXHDR(sstate));
     808              : 
     809              :     /* element_type */
     810           70 :     element_type = pq_getmsgint(&buf, 4);
     811              : 
     812              :     /* nelems */
     813           70 :     nelems = pq_getmsgint64(&buf);
     814              : 
     815              :     /* Create output ArrayBuildState with the needed number of elements */
     816           70 :     result = initArrayResultWithSize(element_type, CurrentMemoryContext,
     817              :                                      false, nelems);
     818           70 :     result->nelems = nelems;
     819              : 
     820              :     /* typlen */
     821           70 :     result->typlen = pq_getmsgint(&buf, 2);
     822              : 
     823              :     /* typbyval */
     824           70 :     result->typbyval = pq_getmsgbyte(&buf);
     825              : 
     826              :     /* typalign */
     827           70 :     result->typalign = pq_getmsgbyte(&buf);
     828              : 
     829              :     /* dnulls */
     830           70 :     temp = pq_getmsgbytes(&buf, sizeof(bool) * nelems);
     831           70 :     memcpy(result->dnulls, temp, sizeof(bool) * nelems);
     832              : 
     833              :     /* dvalues --- see comment in array_agg_serialize */
     834           70 :     if (result->typbyval)
     835              :     {
     836           70 :         temp = pq_getmsgbytes(&buf, sizeof(Datum) * nelems);
     837           70 :         memcpy(result->dvalues, temp, sizeof(Datum) * nelems);
     838              :     }
     839              :     else
     840              :     {
     841              :         DeserialIOData *iodata;
     842              : 
     843              :         /* Avoid repeat catalog lookups for typreceive function */
     844            0 :         iodata = (DeserialIOData *) fcinfo->flinfo->fn_extra;
     845            0 :         if (iodata == NULL)
     846              :         {
     847              :             Oid         typreceive;
     848              : 
     849              :             iodata = (DeserialIOData *)
     850            0 :                 MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
     851              :                                    sizeof(DeserialIOData));
     852            0 :             getTypeBinaryInputInfo(element_type, &typreceive,
     853              :                                    &iodata->typioparam);
     854            0 :             fmgr_info_cxt(typreceive, &iodata->typreceive,
     855            0 :                           fcinfo->flinfo->fn_mcxt);
     856            0 :             fcinfo->flinfo->fn_extra = iodata;
     857              :         }
     858              : 
     859            0 :         for (int i = 0; i < nelems; i++)
     860              :         {
     861              :             int         itemlen;
     862              :             StringInfoData elem_buf;
     863              : 
     864            0 :             if (result->dnulls[i])
     865              :             {
     866            0 :                 result->dvalues[i] = (Datum) 0;
     867            0 :                 continue;
     868              :             }
     869              : 
     870            0 :             itemlen = pq_getmsgint(&buf, 4);
     871            0 :             if (itemlen < 0 || itemlen > (buf.len - buf.cursor))
     872            0 :                 ereport(ERROR,
     873              :                         (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
     874              :                          errmsg("insufficient data left in message")));
     875              : 
     876              :             /*
     877              :              * Rather than copying data around, we just initialize a
     878              :              * StringInfo pointing to the correct portion of the message
     879              :              * buffer.
     880              :              */
     881            0 :             initReadOnlyStringInfo(&elem_buf, &buf.data[buf.cursor], itemlen);
     882              : 
     883            0 :             buf.cursor += itemlen;
     884              : 
     885              :             /* Now call the element's receiveproc */
     886            0 :             result->dvalues[i] = ReceiveFunctionCall(&iodata->typreceive,
     887              :                                                      &elem_buf,
     888              :                                                      iodata->typioparam,
     889              :                                                      -1);
     890              :         }
     891              :     }
     892              : 
     893           70 :     pq_getmsgend(&buf);
     894              : 
     895           70 :     PG_RETURN_POINTER(result);
     896              : }
     897              : 
     898              : Datum
     899        81663 : array_agg_finalfn(PG_FUNCTION_ARGS)
     900              : {
     901              :     Datum       result;
     902              :     ArrayBuildState *state;
     903              :     int         dims[1];
     904              :     int         lbs[1];
     905              : 
     906              :     /* cannot be called directly because of internal-type argument */
     907              :     Assert(AggCheckCallContext(fcinfo, NULL));
     908              : 
     909        81663 :     state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
     910              : 
     911        81663 :     if (state == NULL)
     912         5502 :         PG_RETURN_NULL();       /* returns null iff no input values */
     913              : 
     914        76161 :     dims[0] = state->nelems;
     915        76161 :     lbs[0] = 1;
     916              : 
     917              :     /*
     918              :      * Make the result.  We cannot release the ArrayBuildState because
     919              :      * sometimes aggregate final functions are re-executed.  Rather, it is
     920              :      * nodeAgg.c's responsibility to reset the aggcontext when it's safe to do
     921              :      * so.
     922              :      */
     923        76161 :     result = makeMdArrayResult(state, 1, dims, lbs,
     924              :                                CurrentMemoryContext,
     925              :                                false);
     926              : 
     927        76161 :     PG_RETURN_DATUM(result);
     928              : }
     929              : 
     930              : /*
     931              :  * ARRAY_AGG(anyarray) aggregate function
     932              :  */
     933              : Datum
     934        43589 : array_agg_array_transfn(PG_FUNCTION_ARGS)
     935              : {
     936        43589 :     Oid         arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
     937              :     MemoryContext aggcontext;
     938              :     ArrayBuildStateArr *state;
     939              : 
     940        43589 :     if (arg1_typeid == InvalidOid)
     941            0 :         ereport(ERROR,
     942              :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     943              :                  errmsg("could not determine input data type")));
     944              : 
     945              :     /*
     946              :      * Note: we do not need a run-time check about whether arg1_typeid is a
     947              :      * valid array type, because the parser would have verified that while
     948              :      * resolving the input/result types of this polymorphic aggregate.
     949              :      */
     950              : 
     951        43589 :     if (!AggCheckCallContext(fcinfo, &aggcontext))
     952              :     {
     953              :         /* cannot be called directly because of internal-type argument */
     954            0 :         elog(ERROR, "array_agg_array_transfn called in non-aggregate context");
     955              :     }
     956              : 
     957              : 
     958        43589 :     if (PG_ARGISNULL(0))
     959          253 :         state = initArrayResultArr(arg1_typeid, InvalidOid, aggcontext, false);
     960              :     else
     961        43336 :         state = (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
     962              : 
     963        43589 :     state = accumArrayResultArr(state,
     964              :                                 PG_GETARG_DATUM(1),
     965        43589 :                                 PG_ARGISNULL(1),
     966              :                                 arg1_typeid,
     967              :                                 aggcontext);
     968              : 
     969              :     /*
     970              :      * The transition type for array_agg() is declared to be "internal", which
     971              :      * is a pass-by-value type the same size as a pointer.  So we can safely
     972              :      * pass the ArrayBuildStateArr pointer through nodeAgg.c's machinations.
     973              :      */
     974        43577 :     PG_RETURN_POINTER(state);
     975              : }
     976              : 
     977              : Datum
     978           70 : array_agg_array_combine(PG_FUNCTION_ARGS)
     979              : {
     980              :     ArrayBuildStateArr *state1;
     981              :     ArrayBuildStateArr *state2;
     982              :     MemoryContext agg_context;
     983              :     MemoryContext old_context;
     984              : 
     985           70 :     if (!AggCheckCallContext(fcinfo, &agg_context))
     986            0 :         elog(ERROR, "aggregate function called in non-aggregate context");
     987              : 
     988           70 :     state1 = PG_ARGISNULL(0) ? NULL : (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
     989           70 :     state2 = PG_ARGISNULL(1) ? NULL : (ArrayBuildStateArr *) PG_GETARG_POINTER(1);
     990              : 
     991           70 :     if (state2 == NULL)
     992              :     {
     993              :         /*
     994              :          * NULL state2 is easy, just return state1, which we know is already
     995              :          * in the agg_context
     996              :          */
     997            0 :         if (state1 == NULL)
     998            0 :             PG_RETURN_NULL();
     999            0 :         PG_RETURN_POINTER(state1);
    1000              :     }
    1001              : 
    1002           70 :     if (state1 == NULL)
    1003              :     {
    1004              :         /* We must copy state2's data into the agg_context */
    1005           40 :         old_context = MemoryContextSwitchTo(agg_context);
    1006              : 
    1007           40 :         state1 = initArrayResultArr(state2->array_type, InvalidOid,
    1008              :                                     agg_context, false);
    1009              : 
    1010           40 :         state1->abytes = state2->abytes;
    1011           40 :         state1->data = (char *) palloc(state1->abytes);
    1012              : 
    1013           40 :         if (state2->nullbitmap)
    1014              :         {
    1015           20 :             int         size = (state2->aitems + 7) / 8;
    1016              : 
    1017           20 :             state1->nullbitmap = (uint8 *) palloc(size);
    1018           20 :             memcpy(state1->nullbitmap, state2->nullbitmap, size);
    1019              :         }
    1020              : 
    1021           40 :         memcpy(state1->data, state2->data, state2->nbytes);
    1022           40 :         state1->nbytes = state2->nbytes;
    1023           40 :         state1->aitems = state2->aitems;
    1024           40 :         state1->nitems = state2->nitems;
    1025           40 :         state1->ndims = state2->ndims;
    1026           40 :         memcpy(state1->dims, state2->dims, sizeof(state2->dims));
    1027           40 :         memcpy(state1->lbs, state2->lbs, sizeof(state2->lbs));
    1028           40 :         state1->array_type = state2->array_type;
    1029           40 :         state1->element_type = state2->element_type;
    1030              : 
    1031           40 :         MemoryContextSwitchTo(old_context);
    1032              : 
    1033           40 :         PG_RETURN_POINTER(state1);
    1034              :     }
    1035              : 
    1036              :     /* We only need to combine the two states if state2 has any items */
    1037           30 :     if (state2->nitems > 0)
    1038              :     {
    1039              :         MemoryContext oldContext;
    1040              :         int         reqsize;
    1041              :         int         newnitems;
    1042              :         int         i;
    1043              : 
    1044              :         /*
    1045              :          * Check the states are compatible with each other.  Ensure we use the
    1046              :          * same error messages that are listed in accumArrayResultArr so that
    1047              :          * the same error is shown as would have been if we'd not used the
    1048              :          * combine function for the aggregation.
    1049              :          */
    1050           30 :         if (state1->ndims != state2->ndims)
    1051            0 :             ereport(ERROR,
    1052              :                     (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    1053              :                      errmsg("cannot accumulate arrays of different dimensionality")));
    1054              : 
    1055              :         /* Check dimensions match ignoring the first dimension. */
    1056           60 :         for (i = 1; i < state1->ndims; i++)
    1057              :         {
    1058           30 :             if (state1->dims[i] != state2->dims[i] || state1->lbs[i] != state2->lbs[i])
    1059            0 :                 ereport(ERROR,
    1060              :                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    1061              :                          errmsg("cannot accumulate arrays of different dimensionality")));
    1062              :         }
    1063              : 
    1064              :         /* Types should match already. */
    1065              :         Assert(state1->array_type == state2->array_type);
    1066              :         Assert(state1->element_type == state2->element_type);
    1067              : 
    1068              :         /* Calculate new sizes, guarding against overflow. */
    1069           60 :         if (pg_add_s32_overflow(state1->nbytes, state2->nbytes, &reqsize) ||
    1070           30 :             pg_add_s32_overflow(state1->nitems, state2->nitems, &newnitems))
    1071            0 :             ereport(ERROR,
    1072              :                     (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    1073              :                      errmsg("array size exceeds the maximum allowed (%zu)",
    1074              :                             MaxArraySize)));
    1075              : 
    1076           30 :         oldContext = MemoryContextSwitchTo(state1->mcontext);
    1077              : 
    1078              :         /*
    1079              :          * If there's not enough space in state1 then we'll need to reallocate
    1080              :          * more.
    1081              :          */
    1082           30 :         if (state1->abytes < reqsize)
    1083              :         {
    1084              :             /* use a power of 2 size rather than allocating just reqsize */
    1085           10 :             state1->abytes = pg_nextpower2_32(reqsize);
    1086           10 :             state1->data = (char *) repalloc(state1->data, state1->abytes);
    1087              :         }
    1088              : 
    1089              :         /* Combine the null bitmaps, if present. */
    1090           30 :         if (state1->nullbitmap || state2->nullbitmap)
    1091              :         {
    1092           15 :             if (state1->nullbitmap == NULL)
    1093              :             {
    1094              :                 /*
    1095              :                  * First input with nulls; we must retrospectively handle any
    1096              :                  * previous inputs by marking all their items non-null.
    1097              :                  */
    1098            0 :                 state1->aitems = pg_nextpower2_32(Max(256, newnitems));
    1099            0 :                 state1->nullbitmap = (uint8 *) palloc((state1->aitems + 7) / 8);
    1100            0 :                 array_bitmap_copy(state1->nullbitmap, 0,
    1101              :                                   NULL, 0,
    1102              :                                   state1->nitems);
    1103              :             }
    1104           15 :             else if (newnitems > state1->aitems)
    1105              :             {
    1106           10 :                 state1->aitems = pg_nextpower2_32(newnitems);
    1107           10 :                 state1->nullbitmap = (uint8 *)
    1108           10 :                     repalloc(state1->nullbitmap, (state1->aitems + 7) / 8);
    1109              :             }
    1110              :             /* This will do the right thing if state2->nullbitmap is NULL: */
    1111           15 :             array_bitmap_copy(state1->nullbitmap, state1->nitems,
    1112           15 :                               state2->nullbitmap, 0,
    1113              :                               state2->nitems);
    1114              :         }
    1115              : 
    1116              :         /* Finally, combine the data and adjust sizes. */
    1117           30 :         memcpy(state1->data + state1->nbytes, state2->data, state2->nbytes);
    1118           30 :         state1->nbytes += state2->nbytes;
    1119           30 :         state1->nitems += state2->nitems;
    1120              : 
    1121           30 :         state1->dims[0] += state2->dims[0];
    1122              :         /* remaining dims already match, per test above */
    1123              : 
    1124           30 :         MemoryContextSwitchTo(oldContext);
    1125              :     }
    1126              : 
    1127           30 :     PG_RETURN_POINTER(state1);
    1128              : }
    1129              : 
    1130              : /*
    1131              :  * array_agg_array_serialize
    1132              :  *      Serialize ArrayBuildStateArr into bytea.
    1133              :  */
    1134              : Datum
    1135           70 : array_agg_array_serialize(PG_FUNCTION_ARGS)
    1136              : {
    1137              :     ArrayBuildStateArr *state;
    1138              :     StringInfoData buf;
    1139              :     bytea      *result;
    1140              : 
    1141              :     /* cannot be called directly because of internal-type argument */
    1142              :     Assert(AggCheckCallContext(fcinfo, NULL));
    1143              : 
    1144           70 :     state = (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
    1145              : 
    1146           70 :     pq_begintypsend(&buf);
    1147              : 
    1148              :     /*
    1149              :      * element_type. Putting this first is more convenient in deserialization
    1150              :      * so that we can init the new state sooner.
    1151              :      */
    1152           70 :     pq_sendint32(&buf, state->element_type);
    1153              : 
    1154              :     /* array_type */
    1155           70 :     pq_sendint32(&buf, state->array_type);
    1156              : 
    1157              :     /* nbytes */
    1158           70 :     pq_sendint32(&buf, state->nbytes);
    1159              : 
    1160              :     /* data */
    1161           70 :     pq_sendbytes(&buf, state->data, state->nbytes);
    1162              : 
    1163              :     /* abytes */
    1164           70 :     pq_sendint32(&buf, state->abytes);
    1165              : 
    1166              :     /* aitems */
    1167           70 :     pq_sendint32(&buf, state->aitems);
    1168              : 
    1169              :     /* nullbitmap */
    1170           70 :     if (state->nullbitmap)
    1171              :     {
    1172              :         Assert(state->aitems > 0);
    1173           35 :         pq_sendbytes(&buf, state->nullbitmap, (state->aitems + 7) / 8);
    1174              :     }
    1175              : 
    1176              :     /* nitems */
    1177           70 :     pq_sendint32(&buf, state->nitems);
    1178              : 
    1179              :     /* ndims */
    1180           70 :     pq_sendint32(&buf, state->ndims);
    1181              : 
    1182              :     /* dims: XXX should we just send ndims elements? */
    1183           70 :     pq_sendbytes(&buf, state->dims, sizeof(state->dims));
    1184              : 
    1185              :     /* lbs */
    1186           70 :     pq_sendbytes(&buf, state->lbs, sizeof(state->lbs));
    1187              : 
    1188           70 :     result = pq_endtypsend(&buf);
    1189              : 
    1190           70 :     PG_RETURN_BYTEA_P(result);
    1191              : }
    1192              : 
    1193              : Datum
    1194           70 : array_agg_array_deserialize(PG_FUNCTION_ARGS)
    1195              : {
    1196              :     bytea      *sstate;
    1197              :     ArrayBuildStateArr *result;
    1198              :     StringInfoData buf;
    1199              :     Oid         element_type;
    1200              :     Oid         array_type;
    1201              :     int         nbytes;
    1202              :     const char *temp;
    1203              : 
    1204              :     /* cannot be called directly because of internal-type argument */
    1205              :     Assert(AggCheckCallContext(fcinfo, NULL));
    1206              : 
    1207           70 :     sstate = PG_GETARG_BYTEA_PP(0);
    1208              : 
    1209              :     /*
    1210              :      * Initialize a StringInfo so that we can "receive" it using the standard
    1211              :      * recv-function infrastructure.
    1212              :      */
    1213           70 :     initReadOnlyStringInfo(&buf, VARDATA_ANY(sstate),
    1214           70 :                            VARSIZE_ANY_EXHDR(sstate));
    1215              : 
    1216              :     /* element_type */
    1217           70 :     element_type = pq_getmsgint(&buf, 4);
    1218              : 
    1219              :     /* array_type */
    1220           70 :     array_type = pq_getmsgint(&buf, 4);
    1221              : 
    1222              :     /* nbytes */
    1223           70 :     nbytes = pq_getmsgint(&buf, 4);
    1224              : 
    1225           70 :     result = initArrayResultArr(array_type, element_type,
    1226              :                                 CurrentMemoryContext, false);
    1227              : 
    1228           70 :     result->abytes = 1024;
    1229           90 :     while (result->abytes < nbytes)
    1230           20 :         result->abytes *= 2;
    1231              : 
    1232           70 :     result->data = (char *) palloc(result->abytes);
    1233              : 
    1234              :     /* data */
    1235           70 :     temp = pq_getmsgbytes(&buf, nbytes);
    1236           70 :     memcpy(result->data, temp, nbytes);
    1237           70 :     result->nbytes = nbytes;
    1238              : 
    1239              :     /* abytes */
    1240           70 :     result->abytes = pq_getmsgint(&buf, 4);
    1241              : 
    1242              :     /* aitems: might be 0 */
    1243           70 :     result->aitems = pq_getmsgint(&buf, 4);
    1244              : 
    1245              :     /* nullbitmap */
    1246           70 :     if (result->aitems > 0)
    1247              :     {
    1248           35 :         int         size = (result->aitems + 7) / 8;
    1249              : 
    1250           35 :         result->nullbitmap = (uint8 *) palloc(size);
    1251           35 :         temp = pq_getmsgbytes(&buf, size);
    1252           35 :         memcpy(result->nullbitmap, temp, size);
    1253              :     }
    1254              :     else
    1255           35 :         result->nullbitmap = NULL;
    1256              : 
    1257              :     /* nitems */
    1258           70 :     result->nitems = pq_getmsgint(&buf, 4);
    1259              : 
    1260              :     /* ndims */
    1261           70 :     result->ndims = pq_getmsgint(&buf, 4);
    1262              : 
    1263              :     /* dims */
    1264           70 :     temp = pq_getmsgbytes(&buf, sizeof(result->dims));
    1265           70 :     memcpy(result->dims, temp, sizeof(result->dims));
    1266              : 
    1267              :     /* lbs */
    1268           70 :     temp = pq_getmsgbytes(&buf, sizeof(result->lbs));
    1269           70 :     memcpy(result->lbs, temp, sizeof(result->lbs));
    1270              : 
    1271           70 :     pq_getmsgend(&buf);
    1272              : 
    1273           70 :     PG_RETURN_POINTER(result);
    1274              : }
    1275              : 
    1276              : Datum
    1277          212 : array_agg_array_finalfn(PG_FUNCTION_ARGS)
    1278              : {
    1279              :     Datum       result;
    1280              :     ArrayBuildStateArr *state;
    1281              : 
    1282              :     /* cannot be called directly because of internal-type argument */
    1283              :     Assert(AggCheckCallContext(fcinfo, NULL));
    1284              : 
    1285          212 :     state = PG_ARGISNULL(0) ? NULL : (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
    1286              : 
    1287          212 :     if (state == NULL)
    1288            1 :         PG_RETURN_NULL();       /* returns null iff no input values */
    1289              : 
    1290              :     /*
    1291              :      * Make the result.  We cannot release the ArrayBuildStateArr because
    1292              :      * sometimes aggregate final functions are re-executed.  Rather, it is
    1293              :      * nodeAgg.c's responsibility to reset the aggcontext when it's safe to do
    1294              :      * so.
    1295              :      */
    1296          211 :     result = makeArrayResultArr(state, CurrentMemoryContext, false);
    1297              : 
    1298          211 :     PG_RETURN_DATUM(result);
    1299              : }
    1300              : 
    1301              : /*-----------------------------------------------------------------------------
    1302              :  * array_position, array_position_start :
    1303              :  *          return the offset of a value in an array.
    1304              :  *
    1305              :  * IS NOT DISTINCT FROM semantics are used for comparisons.  Return NULL when
    1306              :  * the value is not found.
    1307              :  *-----------------------------------------------------------------------------
    1308              :  */
    1309              : Datum
    1310          128 : array_position(PG_FUNCTION_ARGS)
    1311              : {
    1312          128 :     return array_position_common(fcinfo);
    1313              : }
    1314              : 
    1315              : Datum
    1316           12 : array_position_start(PG_FUNCTION_ARGS)
    1317              : {
    1318           12 :     return array_position_common(fcinfo);
    1319              : }
    1320              : 
    1321              : /*
    1322              :  * array_position_common
    1323              :  *      Common code for array_position and array_position_start
    1324              :  *
    1325              :  * These are separate wrappers for the sake of opr_sanity regression test.
    1326              :  * They are not strict so we have to test for null inputs explicitly.
    1327              :  */
    1328              : static Datum
    1329          140 : array_position_common(FunctionCallInfo fcinfo)
    1330              : {
    1331              :     ArrayType  *array;
    1332          140 :     Oid         collation = PG_GET_COLLATION();
    1333              :     Oid         element_type;
    1334              :     Datum       searched_element,
    1335              :                 value;
    1336              :     bool        isnull;
    1337              :     int         position,
    1338              :                 position_min;
    1339          140 :     bool        found = false;
    1340              :     TypeCacheEntry *typentry;
    1341              :     ArrayMetaState *my_extra;
    1342              :     bool        null_search;
    1343              :     ArrayIterator array_iterator;
    1344              : 
    1345          140 :     if (PG_ARGISNULL(0))
    1346            0 :         PG_RETURN_NULL();
    1347              : 
    1348          140 :     array = PG_GETARG_ARRAYTYPE_P(0);
    1349              : 
    1350              :     /*
    1351              :      * We refuse to search for elements in multi-dimensional arrays, since we
    1352              :      * have no good way to report the element's location in the array.
    1353              :      */
    1354          140 :     if (ARR_NDIM(array) > 1)
    1355            4 :         ereport(ERROR,
    1356              :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1357              :                  errmsg("searching for elements in multidimensional arrays is not supported")));
    1358              : 
    1359              :     /* Searching in an empty array is well-defined, though: it always fails */
    1360          136 :     if (ARR_NDIM(array) < 1)
    1361            0 :         PG_RETURN_NULL();
    1362              : 
    1363          136 :     if (PG_ARGISNULL(1))
    1364              :     {
    1365              :         /* fast return when the array doesn't have nulls */
    1366           10 :         if (!array_contains_nulls(array))
    1367            5 :             PG_RETURN_NULL();
    1368            5 :         searched_element = (Datum) 0;
    1369            5 :         null_search = true;
    1370              :     }
    1371              :     else
    1372              :     {
    1373          126 :         searched_element = PG_GETARG_DATUM(1);
    1374          126 :         null_search = false;
    1375              :     }
    1376              : 
    1377          131 :     element_type = ARR_ELEMTYPE(array);
    1378          131 :     position = (ARR_LBOUND(array))[0] - 1;
    1379              : 
    1380              :     /* figure out where to start */
    1381          131 :     if (PG_NARGS() == 3)
    1382              :     {
    1383           12 :         if (PG_ARGISNULL(2))
    1384            0 :             ereport(ERROR,
    1385              :                     (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
    1386              :                      errmsg("initial position must not be null")));
    1387              : 
    1388           12 :         position_min = PG_GETARG_INT32(2);
    1389              :     }
    1390              :     else
    1391          119 :         position_min = (ARR_LBOUND(array))[0];
    1392              : 
    1393              :     /*
    1394              :      * We arrange to look up type info for array_create_iterator only once per
    1395              :      * series of calls, assuming the element type doesn't change underneath
    1396              :      * us.
    1397              :      */
    1398          131 :     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
    1399          131 :     if (my_extra == NULL)
    1400              :     {
    1401           59 :         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
    1402              :                                                       sizeof(ArrayMetaState));
    1403           59 :         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
    1404           59 :         my_extra->element_type = ~element_type;
    1405              :     }
    1406              : 
    1407          131 :     if (my_extra->element_type != element_type)
    1408              :     {
    1409           59 :         get_typlenbyvalalign(element_type,
    1410              :                              &my_extra->typlen,
    1411              :                              &my_extra->typbyval,
    1412              :                              &my_extra->typalign);
    1413              : 
    1414           59 :         typentry = lookup_type_cache(element_type, TYPECACHE_EQ_OPR_FINFO);
    1415              : 
    1416           59 :         if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
    1417            0 :             ereport(ERROR,
    1418              :                     (errcode(ERRCODE_UNDEFINED_FUNCTION),
    1419              :                      errmsg("could not identify an equality operator for type %s",
    1420              :                             format_type_be(element_type))));
    1421              : 
    1422           59 :         my_extra->element_type = element_type;
    1423           59 :         fmgr_info_cxt(typentry->eq_opr_finfo.fn_oid, &my_extra->proc,
    1424           59 :                       fcinfo->flinfo->fn_mcxt);
    1425              :     }
    1426              : 
    1427              :     /* Examine each array element until we find a match. */
    1428          131 :     array_iterator = array_create_iterator(array, 0, my_extra);
    1429          395 :     while (array_iterate(array_iterator, &value, &isnull))
    1430              :     {
    1431          373 :         position++;
    1432              : 
    1433              :         /* skip initial elements if caller requested so */
    1434          373 :         if (position < position_min)
    1435           52 :             continue;
    1436              : 
    1437              :         /*
    1438              :          * Can't look at the array element's value if it's null; but if we
    1439              :          * search for null, we have a hit and are done.
    1440              :          */
    1441          321 :         if (isnull || null_search)
    1442              :         {
    1443           35 :             if (isnull && null_search)
    1444              :             {
    1445            5 :                 found = true;
    1446            5 :                 break;
    1447              :             }
    1448              :             else
    1449           30 :                 continue;
    1450              :         }
    1451              : 
    1452              :         /* not nulls, so run the operator */
    1453          286 :         if (DatumGetBool(FunctionCall2Coll(&my_extra->proc, collation,
    1454              :                                            searched_element, value)))
    1455              :         {
    1456          104 :             found = true;
    1457          104 :             break;
    1458              :         }
    1459              :     }
    1460              : 
    1461          131 :     array_free_iterator(array_iterator);
    1462              : 
    1463              :     /* Avoid leaking memory when handed toasted input */
    1464          131 :     PG_FREE_IF_COPY(array, 0);
    1465              : 
    1466          131 :     if (!found)
    1467           22 :         PG_RETURN_NULL();
    1468              : 
    1469          109 :     PG_RETURN_INT32(position);
    1470              : }
    1471              : 
    1472              : /*-----------------------------------------------------------------------------
    1473              :  * array_positions :
    1474              :  *          return an array of positions of a value in an array.
    1475              :  *
    1476              :  * IS NOT DISTINCT FROM semantics are used for comparisons.  Returns NULL when
    1477              :  * the input array is NULL.  When the value is not found in the array, returns
    1478              :  * an empty array.
    1479              :  *
    1480              :  * This is not strict so we have to test for null inputs explicitly.
    1481              :  *-----------------------------------------------------------------------------
    1482              :  */
    1483              : Datum
    1484           46 : array_positions(PG_FUNCTION_ARGS)
    1485              : {
    1486              :     ArrayType  *array;
    1487           46 :     Oid         collation = PG_GET_COLLATION();
    1488              :     Oid         element_type;
    1489              :     Datum       searched_element,
    1490              :                 value;
    1491              :     bool        isnull;
    1492              :     int         position;
    1493              :     TypeCacheEntry *typentry;
    1494              :     ArrayMetaState *my_extra;
    1495              :     bool        null_search;
    1496              :     ArrayIterator array_iterator;
    1497           46 :     ArrayBuildState *astate = NULL;
    1498              : 
    1499           46 :     if (PG_ARGISNULL(0))
    1500           10 :         PG_RETURN_NULL();
    1501              : 
    1502           36 :     array = PG_GETARG_ARRAYTYPE_P(0);
    1503              : 
    1504              :     /*
    1505              :      * We refuse to search for elements in multi-dimensional arrays, since we
    1506              :      * have no good way to report the element's location in the array.
    1507              :      */
    1508           36 :     if (ARR_NDIM(array) > 1)
    1509            4 :         ereport(ERROR,
    1510              :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1511              :                  errmsg("searching for elements in multidimensional arrays is not supported")));
    1512              : 
    1513           32 :     astate = initArrayResult(INT4OID, CurrentMemoryContext, false);
    1514              : 
    1515              :     /* Searching in an empty array is well-defined, though: it always fails */
    1516           32 :     if (ARR_NDIM(array) < 1)
    1517            0 :         PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
    1518              : 
    1519           32 :     if (PG_ARGISNULL(1))
    1520              :     {
    1521              :         /* fast return when the array doesn't have nulls */
    1522           10 :         if (!array_contains_nulls(array))
    1523            5 :             PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
    1524            5 :         searched_element = (Datum) 0;
    1525            5 :         null_search = true;
    1526              :     }
    1527              :     else
    1528              :     {
    1529           22 :         searched_element = PG_GETARG_DATUM(1);
    1530           22 :         null_search = false;
    1531              :     }
    1532              : 
    1533           27 :     element_type = ARR_ELEMTYPE(array);
    1534           27 :     position = (ARR_LBOUND(array))[0] - 1;
    1535              : 
    1536              :     /*
    1537              :      * We arrange to look up type info for array_create_iterator only once per
    1538              :      * series of calls, assuming the element type doesn't change underneath
    1539              :      * us.
    1540              :      */
    1541           27 :     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
    1542           27 :     if (my_extra == NULL)
    1543              :     {
    1544           23 :         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
    1545              :                                                       sizeof(ArrayMetaState));
    1546           23 :         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
    1547           23 :         my_extra->element_type = ~element_type;
    1548              :     }
    1549              : 
    1550           27 :     if (my_extra->element_type != element_type)
    1551              :     {
    1552           23 :         get_typlenbyvalalign(element_type,
    1553              :                              &my_extra->typlen,
    1554              :                              &my_extra->typbyval,
    1555              :                              &my_extra->typalign);
    1556              : 
    1557           23 :         typentry = lookup_type_cache(element_type, TYPECACHE_EQ_OPR_FINFO);
    1558              : 
    1559           23 :         if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
    1560            0 :             ereport(ERROR,
    1561              :                     (errcode(ERRCODE_UNDEFINED_FUNCTION),
    1562              :                      errmsg("could not identify an equality operator for type %s",
    1563              :                             format_type_be(element_type))));
    1564              : 
    1565           23 :         my_extra->element_type = element_type;
    1566           23 :         fmgr_info_cxt(typentry->eq_opr_finfo.fn_oid, &my_extra->proc,
    1567           23 :                       fcinfo->flinfo->fn_mcxt);
    1568              :     }
    1569              : 
    1570              :     /*
    1571              :      * Accumulate each array position iff the element matches the given
    1572              :      * element.
    1573              :      */
    1574           27 :     array_iterator = array_create_iterator(array, 0, my_extra);
    1575          574 :     while (array_iterate(array_iterator, &value, &isnull))
    1576              :     {
    1577          547 :         position += 1;
    1578              : 
    1579              :         /*
    1580              :          * Can't look at the array element's value if it's null; but if we
    1581              :          * search for null, we have a hit.
    1582              :          */
    1583          547 :         if (isnull || null_search)
    1584              :         {
    1585           60 :             if (isnull && null_search)
    1586              :                 astate =
    1587           10 :                     accumArrayResult(astate, Int32GetDatum(position), false,
    1588              :                                      INT4OID, CurrentMemoryContext);
    1589              : 
    1590           60 :             continue;
    1591              :         }
    1592              : 
    1593              :         /* not nulls, so run the operator */
    1594          487 :         if (DatumGetBool(FunctionCall2Coll(&my_extra->proc, collation,
    1595              :                                            searched_element, value)))
    1596              :             astate =
    1597           63 :                 accumArrayResult(astate, Int32GetDatum(position), false,
    1598              :                                  INT4OID, CurrentMemoryContext);
    1599              :     }
    1600              : 
    1601           27 :     array_free_iterator(array_iterator);
    1602              : 
    1603              :     /* Avoid leaking memory when handed toasted input */
    1604           27 :     PG_FREE_IF_COPY(array, 0);
    1605              : 
    1606           27 :     PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
    1607              : }
    1608              : 
    1609              : /*
    1610              :  * array_shuffle_n
    1611              :  *      Return a copy of array with n randomly chosen items.
    1612              :  *
    1613              :  * The number of items must not exceed the size of the first dimension of the
    1614              :  * array.  We preserve the first dimension's lower bound if keep_lb,
    1615              :  * else it's set to 1.  Lower-order dimensions are preserved in any case.
    1616              :  *
    1617              :  * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
    1618              :  * from the system catalogs, given only the elmtyp. However, the caller is
    1619              :  * in a better position to cache this info across multiple calls.
    1620              :  */
    1621              : static ArrayType *
    1622           32 : array_shuffle_n(ArrayType *array, int n, bool keep_lb,
    1623              :                 Oid elmtyp, TypeCacheEntry *typentry)
    1624              : {
    1625              :     ArrayType  *result;
    1626              :     int         ndim,
    1627              :                *dims,
    1628              :                *lbs,
    1629              :                 nelm,
    1630              :                 nitem,
    1631              :                 rdims[MAXDIM],
    1632              :                 rlbs[MAXDIM];
    1633              :     int16       elmlen;
    1634              :     bool        elmbyval;
    1635              :     char        elmalign;
    1636              :     Datum      *elms,
    1637              :                *ielms;
    1638              :     bool       *nuls,
    1639              :                *inuls;
    1640              : 
    1641           32 :     ndim = ARR_NDIM(array);
    1642           32 :     dims = ARR_DIMS(array);
    1643           32 :     lbs = ARR_LBOUND(array);
    1644              : 
    1645           32 :     elmlen = typentry->typlen;
    1646           32 :     elmbyval = typentry->typbyval;
    1647           32 :     elmalign = typentry->typalign;
    1648              : 
    1649              :     /* If the target array is empty, exit fast */
    1650           32 :     if (ndim < 1 || dims[0] < 1 || n < 1)
    1651            0 :         return construct_empty_array(elmtyp);
    1652              : 
    1653           32 :     deconstruct_array(array, elmtyp, elmlen, elmbyval, elmalign,
    1654              :                       &elms, &nuls, &nelm);
    1655              : 
    1656           32 :     nitem = dims[0];            /* total number of items */
    1657           32 :     nelm /= nitem;              /* number of elements per item */
    1658              : 
    1659              :     Assert(n <= nitem);          /* else it's caller error */
    1660              : 
    1661              :     /*
    1662              :      * Shuffle array using Fisher-Yates algorithm.  Scan the array and swap
    1663              :      * current item (nelm datums starting at ielms) with a randomly chosen
    1664              :      * later item (nelm datums starting at jelms) in each iteration.  We can
    1665              :      * stop once we've done n iterations; then first n items are the result.
    1666              :      */
    1667           32 :     ielms = elms;
    1668           32 :     inuls = nuls;
    1669          152 :     for (int i = 0; i < n; i++)
    1670              :     {
    1671          120 :         int         j = (int) pg_prng_uint64_range(&pg_global_prng_state, i, nitem - 1) * nelm;
    1672          120 :         Datum      *jelms = elms + j;
    1673          120 :         bool       *jnuls = nuls + j;
    1674              : 
    1675              :         /* Swap i'th and j'th items; advance ielms/inuls to next item */
    1676          328 :         for (int k = 0; k < nelm; k++)
    1677              :         {
    1678          208 :             Datum       elm = *ielms;
    1679          208 :             bool        nul = *inuls;
    1680              : 
    1681          208 :             *ielms++ = *jelms;
    1682          208 :             *inuls++ = *jnuls;
    1683          208 :             *jelms++ = elm;
    1684          208 :             *jnuls++ = nul;
    1685              :         }
    1686              :     }
    1687              : 
    1688              :     /* Set up dimensions of the result */
    1689           32 :     memcpy(rdims, dims, ndim * sizeof(int));
    1690           32 :     memcpy(rlbs, lbs, ndim * sizeof(int));
    1691           32 :     rdims[0] = n;
    1692           32 :     if (!keep_lb)
    1693           16 :         rlbs[0] = 1;
    1694              : 
    1695           32 :     result = construct_md_array(elms, nuls, ndim, rdims, rlbs,
    1696              :                                 elmtyp, elmlen, elmbyval, elmalign);
    1697              : 
    1698           32 :     pfree(elms);
    1699           32 :     pfree(nuls);
    1700              : 
    1701           32 :     return result;
    1702              : }
    1703              : 
    1704              : /*
    1705              :  * array_shuffle
    1706              :  *
    1707              :  * Returns an array with the same dimensions as the input array, with its
    1708              :  * first-dimension elements in random order.
    1709              :  */
    1710              : Datum
    1711           16 : array_shuffle(PG_FUNCTION_ARGS)
    1712              : {
    1713           16 :     ArrayType  *array = PG_GETARG_ARRAYTYPE_P(0);
    1714              :     ArrayType  *result;
    1715              :     Oid         elmtyp;
    1716              :     TypeCacheEntry *typentry;
    1717              : 
    1718              :     /*
    1719              :      * There is no point in shuffling empty arrays or arrays with less than
    1720              :      * two items.
    1721              :      */
    1722           16 :     if (ARR_NDIM(array) < 1 || ARR_DIMS(array)[0] < 2)
    1723            0 :         PG_RETURN_ARRAYTYPE_P(array);
    1724              : 
    1725           16 :     elmtyp = ARR_ELEMTYPE(array);
    1726           16 :     typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
    1727           16 :     if (typentry == NULL || typentry->type_id != elmtyp)
    1728              :     {
    1729           16 :         typentry = lookup_type_cache(elmtyp, 0);
    1730           16 :         fcinfo->flinfo->fn_extra = typentry;
    1731              :     }
    1732              : 
    1733           16 :     result = array_shuffle_n(array, ARR_DIMS(array)[0], true, elmtyp, typentry);
    1734              : 
    1735           16 :     PG_RETURN_ARRAYTYPE_P(result);
    1736              : }
    1737              : 
    1738              : /*
    1739              :  * array_sample
    1740              :  *
    1741              :  * Returns an array of n randomly chosen first-dimension elements
    1742              :  * from the input array.
    1743              :  */
    1744              : Datum
    1745           24 : array_sample(PG_FUNCTION_ARGS)
    1746              : {
    1747           24 :     ArrayType  *array = PG_GETARG_ARRAYTYPE_P(0);
    1748           24 :     int         n = PG_GETARG_INT32(1);
    1749              :     ArrayType  *result;
    1750              :     Oid         elmtyp;
    1751              :     TypeCacheEntry *typentry;
    1752              :     int         nitem;
    1753              : 
    1754           24 :     nitem = (ARR_NDIM(array) < 1) ? 0 : ARR_DIMS(array)[0];
    1755              : 
    1756           24 :     if (n < 0 || n > nitem)
    1757            8 :         ereport(ERROR,
    1758              :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1759              :                  errmsg("sample size must be between 0 and %d", nitem)));
    1760              : 
    1761           16 :     elmtyp = ARR_ELEMTYPE(array);
    1762           16 :     typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
    1763           16 :     if (typentry == NULL || typentry->type_id != elmtyp)
    1764              :     {
    1765           16 :         typentry = lookup_type_cache(elmtyp, 0);
    1766           16 :         fcinfo->flinfo->fn_extra = typentry;
    1767              :     }
    1768              : 
    1769           16 :     result = array_shuffle_n(array, n, false, elmtyp, typentry);
    1770              : 
    1771           16 :     PG_RETURN_ARRAYTYPE_P(result);
    1772              : }
    1773              : 
    1774              : 
    1775              : /*
    1776              :  * array_reverse_n
    1777              :  *      Return a copy of array with reversed items.
    1778              :  *
    1779              :  * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
    1780              :  * from the system catalogs, given only the elmtyp. However, the caller is
    1781              :  * in a better position to cache this info across multiple calls.
    1782              :  */
    1783              : static ArrayType *
    1784           15 : array_reverse_n(ArrayType *array, Oid elmtyp, TypeCacheEntry *typentry)
    1785              : {
    1786              :     ArrayType  *result;
    1787              :     int         ndim,
    1788              :                *dims,
    1789              :                *lbs,
    1790              :                 nelm,
    1791              :                 nitem,
    1792              :                 rdims[MAXDIM],
    1793              :                 rlbs[MAXDIM];
    1794              :     int16       elmlen;
    1795              :     bool        elmbyval;
    1796              :     char        elmalign;
    1797              :     Datum      *elms,
    1798              :                *ielms;
    1799              :     bool       *nuls,
    1800              :                *inuls;
    1801              : 
    1802           15 :     ndim = ARR_NDIM(array);
    1803           15 :     dims = ARR_DIMS(array);
    1804           15 :     lbs = ARR_LBOUND(array);
    1805              : 
    1806           15 :     elmlen = typentry->typlen;
    1807           15 :     elmbyval = typentry->typbyval;
    1808           15 :     elmalign = typentry->typalign;
    1809              : 
    1810           15 :     deconstruct_array(array, elmtyp, elmlen, elmbyval, elmalign,
    1811              :                       &elms, &nuls, &nelm);
    1812              : 
    1813           15 :     nitem = dims[0];            /* total number of items */
    1814           15 :     nelm /= nitem;              /* number of elements per item */
    1815              : 
    1816              :     /* Reverse the array */
    1817           15 :     ielms = elms;
    1818           15 :     inuls = nuls;
    1819           45 :     for (int i = 0; i < nitem / 2; i++)
    1820              :     {
    1821           30 :         int         j = (nitem - i - 1) * nelm;
    1822           30 :         Datum      *jelms = elms + j;
    1823           30 :         bool       *jnuls = nuls + j;
    1824              : 
    1825              :         /* Swap i'th and j'th items; advance ielms/inuls to next item */
    1826           70 :         for (int k = 0; k < nelm; k++)
    1827              :         {
    1828           40 :             Datum       elm = *ielms;
    1829           40 :             bool        nul = *inuls;
    1830              : 
    1831           40 :             *ielms++ = *jelms;
    1832           40 :             *inuls++ = *jnuls;
    1833           40 :             *jelms++ = elm;
    1834           40 :             *jnuls++ = nul;
    1835              :         }
    1836              :     }
    1837              : 
    1838              :     /* Set up dimensions of the result */
    1839           15 :     memcpy(rdims, dims, ndim * sizeof(int));
    1840           15 :     memcpy(rlbs, lbs, ndim * sizeof(int));
    1841           15 :     rdims[0] = nitem;
    1842              : 
    1843           15 :     result = construct_md_array(elms, nuls, ndim, rdims, rlbs,
    1844              :                                 elmtyp, elmlen, elmbyval, elmalign);
    1845              : 
    1846           15 :     pfree(elms);
    1847           15 :     pfree(nuls);
    1848              : 
    1849           15 :     return result;
    1850              : }
    1851              : 
    1852              : /*
    1853              :  * array_reverse
    1854              :  *
    1855              :  * Returns an array with the same dimensions as the input array, with its
    1856              :  * first-dimension elements in reverse order.
    1857              :  */
    1858              : Datum
    1859           25 : array_reverse(PG_FUNCTION_ARGS)
    1860              : {
    1861           25 :     ArrayType  *array = PG_GETARG_ARRAYTYPE_P(0);
    1862              :     ArrayType  *result;
    1863              :     Oid         elmtyp;
    1864              :     TypeCacheEntry *typentry;
    1865              : 
    1866              :     /*
    1867              :      * There is no point in reversing empty arrays or arrays with less than
    1868              :      * two items.
    1869              :      */
    1870           25 :     if (ARR_NDIM(array) < 1 || ARR_DIMS(array)[0] < 2)
    1871           10 :         PG_RETURN_ARRAYTYPE_P(array);
    1872              : 
    1873           15 :     elmtyp = ARR_ELEMTYPE(array);
    1874           15 :     typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
    1875           15 :     if (typentry == NULL || typentry->type_id != elmtyp)
    1876              :     {
    1877           15 :         typentry = lookup_type_cache(elmtyp, 0);
    1878           15 :         fcinfo->flinfo->fn_extra = (void *) typentry;
    1879              :     }
    1880              : 
    1881           15 :     result = array_reverse_n(array, elmtyp, typentry);
    1882              : 
    1883           15 :     PG_RETURN_ARRAYTYPE_P(result);
    1884              : }
    1885              : 
    1886              : /*
    1887              :  * array_sort
    1888              :  *
    1889              :  * Sorts the first dimension of the array.
    1890              :  */
    1891              : static ArrayType *
    1892          127 : array_sort_internal(ArrayType *array, bool descending, bool nulls_first,
    1893              :                     FunctionCallInfo fcinfo)
    1894              : {
    1895              :     ArrayType  *newarray;
    1896          127 :     Oid         collation = PG_GET_COLLATION();
    1897              :     int         ndim,
    1898              :                *dims,
    1899              :                *lbs;
    1900              :     ArraySortCachedInfo *cache_info;
    1901              :     Oid         elmtyp;
    1902              :     Oid         sort_typ;
    1903              :     Oid         sort_opr;
    1904              :     Tuplesortstate *tuplesortstate;
    1905              :     ArrayIterator array_iterator;
    1906              :     Datum       value;
    1907              :     bool        isnull;
    1908          127 :     ArrayBuildStateAny *astate = NULL;
    1909              : 
    1910          127 :     ndim = ARR_NDIM(array);
    1911          127 :     dims = ARR_DIMS(array);
    1912          127 :     lbs = ARR_LBOUND(array);
    1913              : 
    1914              :     /* Quick exit if we don't need to sort */
    1915          127 :     if (ndim < 1 || dims[0] < 2)
    1916           20 :         return array;
    1917              : 
    1918              :     /* Set up cache area if we didn't already */
    1919          107 :     cache_info = (ArraySortCachedInfo *) fcinfo->flinfo->fn_extra;
    1920          107 :     if (cache_info == NULL)
    1921              :     {
    1922              :         cache_info = (ArraySortCachedInfo *)
    1923          107 :             MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt,
    1924              :                                    sizeof(ArraySortCachedInfo));
    1925          107 :         fcinfo->flinfo->fn_extra = cache_info;
    1926              :     }
    1927              : 
    1928              :     /* Fetch and cache required data if we don't have it */
    1929          107 :     elmtyp = ARR_ELEMTYPE(array);
    1930          107 :     if (elmtyp != cache_info->array_meta.element_type)
    1931              :     {
    1932              :         TypeCacheEntry *typentry;
    1933              : 
    1934          107 :         typentry = lookup_type_cache(elmtyp,
    1935              :                                      TYPECACHE_LT_OPR | TYPECACHE_GT_OPR);
    1936          107 :         cache_info->array_meta.element_type = elmtyp;
    1937          107 :         cache_info->array_meta.typlen = typentry->typlen;
    1938          107 :         cache_info->array_meta.typbyval = typentry->typbyval;
    1939          107 :         cache_info->array_meta.typalign = typentry->typalign;
    1940          107 :         cache_info->elem_lt_opr = typentry->lt_opr;
    1941          107 :         cache_info->elem_gt_opr = typentry->gt_opr;
    1942          107 :         cache_info->array_type = typentry->typarray;
    1943              :     }
    1944              : 
    1945              :     /* Identify the sort operator to use */
    1946          107 :     if (ndim == 1)
    1947              :     {
    1948              :         /* Need to sort the element type */
    1949           73 :         sort_typ = elmtyp;
    1950           73 :         sort_opr = (descending ? cache_info->elem_gt_opr : cache_info->elem_lt_opr);
    1951              :     }
    1952              :     else
    1953              :     {
    1954              :         /* Otherwise we're sorting arrays */
    1955           34 :         sort_typ = cache_info->array_type;
    1956           34 :         if (!OidIsValid(sort_typ))
    1957            0 :             ereport(ERROR,
    1958              :                     (errcode(ERRCODE_UNDEFINED_OBJECT),
    1959              :                      errmsg("could not find array type for data type %s",
    1960              :                             format_type_be(elmtyp))));
    1961              :         /* We know what operators to use for arrays */
    1962           34 :         sort_opr = (descending ? ARRAY_GT_OP : ARRAY_LT_OP);
    1963              :     }
    1964              : 
    1965              :     /*
    1966              :      * Fail if we don't know how to sort.  The error message is chosen to
    1967              :      * match what array_lt()/array_gt() will say in the multidimensional case.
    1968              :      */
    1969          107 :     if (!OidIsValid(sort_opr))
    1970            4 :         ereport(ERROR,
    1971              :                 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1972              :                 errmsg("could not identify a comparison function for type %s",
    1973              :                        format_type_be(elmtyp)));
    1974              : 
    1975              :     /* Put the things to be sorted (elements or sub-arrays) into a tuplesort */
    1976          103 :     tuplesortstate = tuplesort_begin_datum(sort_typ,
    1977              :                                            sort_opr,
    1978              :                                            collation,
    1979              :                                            nulls_first,
    1980              :                                            work_mem,
    1981              :                                            NULL,
    1982              :                                            TUPLESORT_NONE);
    1983              : 
    1984          103 :     array_iterator = array_create_iterator(array, ndim - 1,
    1985              :                                            &cache_info->array_meta);
    1986          549 :     while (array_iterate(array_iterator, &value, &isnull))
    1987              :     {
    1988          446 :         tuplesort_putdatum(tuplesortstate, value, isnull);
    1989              :     }
    1990          103 :     array_free_iterator(array_iterator);
    1991              : 
    1992              :     /* Do the sort */
    1993          103 :     tuplesort_performsort(tuplesortstate);
    1994              : 
    1995              :     /* Extract results into a new array */
    1996          537 :     while (tuplesort_getdatum(tuplesortstate, true, false, &value, &isnull, NULL))
    1997              :     {
    1998          438 :         astate = accumArrayResultAny(astate, value, isnull,
    1999              :                                      sort_typ, CurrentMemoryContext);
    2000              :     }
    2001           99 :     tuplesort_end(tuplesortstate);
    2002              : 
    2003           99 :     newarray = DatumGetArrayTypeP(makeArrayResultAny(astate,
    2004              :                                                      CurrentMemoryContext,
    2005              :                                                      true));
    2006              : 
    2007              :     /* Adjust lower bound to match the input */
    2008           99 :     ARR_LBOUND(newarray)[0] = lbs[0];
    2009              : 
    2010           99 :     return newarray;
    2011              : }
    2012              : 
    2013              : Datum
    2014           97 : array_sort(PG_FUNCTION_ARGS)
    2015              : {
    2016           97 :     ArrayType  *array = PG_GETARG_ARRAYTYPE_P(0);
    2017              : 
    2018           97 :     PG_RETURN_ARRAYTYPE_P(array_sort_internal(array,
    2019              :                                               false,
    2020              :                                               false,
    2021              :                                               fcinfo));
    2022              : }
    2023              : 
    2024              : Datum
    2025           10 : array_sort_order(PG_FUNCTION_ARGS)
    2026              : {
    2027           10 :     ArrayType  *array = PG_GETARG_ARRAYTYPE_P(0);
    2028           10 :     bool        descending = PG_GETARG_BOOL(1);
    2029              : 
    2030           10 :     PG_RETURN_ARRAYTYPE_P(array_sort_internal(array,
    2031              :                                               descending,
    2032              :                                               descending,
    2033              :                                               fcinfo));
    2034              : }
    2035              : 
    2036              : Datum
    2037           20 : array_sort_order_nulls_first(PG_FUNCTION_ARGS)
    2038              : {
    2039           20 :     ArrayType  *array = PG_GETARG_ARRAYTYPE_P(0);
    2040           20 :     bool        descending = PG_GETARG_BOOL(1);
    2041           20 :     bool        nulls_first = PG_GETARG_BOOL(2);
    2042              : 
    2043           20 :     PG_RETURN_ARRAYTYPE_P(array_sort_internal(array,
    2044              :                                               descending,
    2045              :                                               nulls_first,
    2046              :                                               fcinfo));
    2047              : }
        

Generated by: LCOV version 2.0-1