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

Generated by: LCOV version 2.0-1