LCOV - code coverage report
Current view: top level - src/backend/utils/adt - array_userfuncs.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 598 682 87.7 %
Date: 2025-04-24 12:15:10 Functions: 29 29 100.0 %
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-2025, 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        1946 : 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        1946 :     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
      89        1946 :     if (my_extra == NULL)
      90             :     {
      91             :         my_extra = (ArrayMetaState *)
      92        1212 :             MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
      93             :                                sizeof(ArrayMetaState));
      94        1212 :         my_extra->element_type = InvalidOid;
      95        1212 :         fcinfo->flinfo->fn_extra = my_extra;
      96             :     }
      97             : 
      98             :     /* Figure out which context we want the result in */
      99        1946 :     if (!AggCheckCallContext(fcinfo, &resultcxt))
     100        1826 :         resultcxt = CurrentMemoryContext;
     101             : 
     102             :     /* Now collect the array value */
     103        1946 :     if (!PG_ARGISNULL(argno))
     104             :     {
     105        1922 :         MemoryContext oldcxt = MemoryContextSwitchTo(resultcxt);
     106             : 
     107        1922 :         eah = PG_GETARG_EXPANDED_ARRAYX(argno, my_extra);
     108        1922 :         MemoryContextSwitchTo(oldcxt);
     109             :     }
     110             :     else
     111             :     {
     112             :         /* We have to look up the array type and element type */
     113          24 :         Oid         arr_typeid = get_fn_expr_argtype(fcinfo->flinfo, argno);
     114             : 
     115          24 :         if (!OidIsValid(arr_typeid))
     116           0 :             ereport(ERROR,
     117             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     118             :                      errmsg("could not determine input data type")));
     119          24 :         element_type = get_element_type(arr_typeid);
     120          24 :         if (!OidIsValid(element_type))
     121           0 :             ereport(ERROR,
     122             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
     123             :                      errmsg("input data type is not an array")));
     124             : 
     125          24 :         eah = construct_empty_expanded_array(element_type,
     126             :                                              resultcxt,
     127             :                                              my_extra);
     128             :     }
     129             : 
     130        1946 :     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        1866 : 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        1866 :     eah = fetch_array_arg_replace_nulls(fcinfo, 0);
     151        1866 :     isNull = PG_ARGISNULL(1);
     152        1866 :     if (isNull)
     153           0 :         newelem = (Datum) 0;
     154             :     else
     155        1866 :         newelem = PG_GETARG_DATUM(1);
     156             : 
     157        1866 :     if (eah->ndims == 1)
     158             :     {
     159             :         /* append newelem */
     160        1450 :         lb = eah->lbound;
     161        1450 :         dimv = eah->dims;
     162             : 
     163             :         /* index of added elem is at lb[0] + (dimv[0] - 1) + 1 */
     164        1450 :         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         416 :     else if (eah->ndims == 0)
     170         416 :         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        1866 :     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
     178             : 
     179        1866 :     result = array_set_element(EOHPGetRWDatum(&eah->hdr),
     180             :                                1, &indx, newelem, isNull,
     181        1866 :                                -1, my_extra->typlen, my_extra->typbyval, my_extra->typalign);
     182             : 
     183        1866 :     PG_RETURN_DATUM(result);
     184             : }
     185             : 
     186             : /*
     187             :  * array_append_support()
     188             :  *
     189             :  * Planner support function for array_append()
     190             :  */
     191             : Datum
     192         324 : array_append_support(PG_FUNCTION_ARGS)
     193             : {
     194         324 :     Node       *rawreq = (Node *) PG_GETARG_POINTER(0);
     195         324 :     Node       *ret = NULL;
     196             : 
     197         324 :     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           2 :         SupportRequestModifyInPlace *req = (SupportRequestModifyInPlace *) rawreq;
     205           2 :         Param      *arg = (Param *) linitial(req->args);
     206             : 
     207           2 :         if (arg && IsA(arg, Param) &&
     208           2 :             arg->paramkind == PARAM_EXTERN &&
     209           2 :             arg->paramid == req->paramid)
     210           2 :             ret = (Node *) arg;
     211             :     }
     212             : 
     213         324 :     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          80 : 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          80 :     isNull = PG_ARGISNULL(0);
     234          80 :     if (isNull)
     235           0 :         newelem = (Datum) 0;
     236             :     else
     237          80 :         newelem = PG_GETARG_DATUM(0);
     238          80 :     eah = fetch_array_arg_replace_nulls(fcinfo, 1);
     239             : 
     240          80 :     if (eah->ndims == 1)
     241             :     {
     242             :         /* prepend newelem */
     243          80 :         lb = eah->lbound;
     244          80 :         lb0 = lb[0];
     245             : 
     246          80 :         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          80 :     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
     263             : 
     264          80 :     result = array_set_element(EOHPGetRWDatum(&eah->hdr),
     265             :                                1, &indx, newelem, isNull,
     266          80 :                                -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          80 :     if (eah->ndims == 1)
     271             :     {
     272             :         /* This is ok whether we've deconstructed or not */
     273          80 :         eah->lbound[0] = lb0;
     274             :     }
     275             : 
     276          80 :     PG_RETURN_DATUM(result);
     277             : }
     278             : 
     279             : /*
     280             :  * array_prepend_support()
     281             :  *
     282             :  * Planner support function for array_prepend()
     283             :  */
     284             : Datum
     285          54 : array_prepend_support(PG_FUNCTION_ARGS)
     286             : {
     287          54 :     Node       *rawreq = (Node *) PG_GETARG_POINTER(0);
     288          54 :     Node       *ret = NULL;
     289             : 
     290          54 :     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           2 :         SupportRequestModifyInPlace *req = (SupportRequestModifyInPlace *) rawreq;
     298           2 :         Param      *arg = (Param *) lsecond(req->args);
     299             : 
     300           2 :         if (arg && IsA(arg, Param) &&
     301           2 :             arg->paramkind == PARAM_EXTERN &&
     302           2 :             arg->paramid == req->paramid)
     303           2 :             ret = (Node *) arg;
     304             :     }
     305             : 
     306          54 :     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       29994 : 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       29994 :     if (PG_ARGISNULL(0))
     349             :     {
     350        1710 :         if (PG_ARGISNULL(1))
     351           0 :             PG_RETURN_NULL();
     352        1710 :         result = PG_GETARG_ARRAYTYPE_P(1);
     353        1710 :         PG_RETURN_ARRAYTYPE_P(result);
     354             :     }
     355       28284 :     if (PG_ARGISNULL(1))
     356             :     {
     357           0 :         result = PG_GETARG_ARRAYTYPE_P(0);
     358           0 :         PG_RETURN_ARRAYTYPE_P(result);
     359             :     }
     360             : 
     361       28284 :     v1 = PG_GETARG_ARRAYTYPE_P(0);
     362       28284 :     v2 = PG_GETARG_ARRAYTYPE_P(1);
     363             : 
     364       28284 :     element_type1 = ARR_ELEMTYPE(v1);
     365       28284 :     element_type2 = ARR_ELEMTYPE(v2);
     366             : 
     367             :     /* Check we have matching element types */
     368       28284 :     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       28284 :     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       28284 :     ndims1 = ARR_NDIM(v1);
     390       28284 :     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       28284 :     if (ndims1 == 0 && ndims2 > 0)
     399           0 :         PG_RETURN_ARRAYTYPE_P(v2);
     400             : 
     401       28284 :     if (ndims2 == 0)
     402          34 :         PG_RETURN_ARRAYTYPE_P(v1);
     403             : 
     404             :     /* the rest fall under rule 3, 4, or 5 */
     405       28250 :     if (ndims1 != ndims2 &&
     406          18 :         ndims1 != ndims2 - 1 &&
     407          12 :         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       28250 :     lbs1 = ARR_LBOUND(v1);
     417       28250 :     lbs2 = ARR_LBOUND(v2);
     418       28250 :     dims1 = ARR_DIMS(v1);
     419       28250 :     dims2 = ARR_DIMS(v2);
     420       28250 :     dat1 = ARR_DATA_PTR(v1);
     421       28250 :     dat2 = ARR_DATA_PTR(v2);
     422       28250 :     bitmap1 = ARR_NULLBITMAP(v1);
     423       28250 :     bitmap2 = ARR_NULLBITMAP(v2);
     424       28250 :     nitems1 = ArrayGetNItems(ndims1, dims1);
     425       28250 :     nitems2 = ArrayGetNItems(ndims2, dims2);
     426       28250 :     ndatabytes1 = ARR_SIZE(v1) - ARR_DATA_OFFSET(v1);
     427       28250 :     ndatabytes2 = ARR_SIZE(v2) - ARR_DATA_OFFSET(v2);
     428             : 
     429       28250 :     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       28232 :         ndims = ndims1;
     436       28232 :         dims = (int *) palloc(ndims * sizeof(int));
     437       28232 :         lbs = (int *) palloc(ndims * sizeof(int));
     438             : 
     439       28232 :         dims[0] = dims1[0] + dims2[0];
     440       28232 :         lbs[0] = lbs1[0];
     441             : 
     442       28244 :         for (i = 1; i < ndims; i++)
     443             :         {
     444          12 :             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          12 :             dims[i] = dims1[i];
     452          12 :             lbs[i] = lbs1[i];
     453             :         }
     454             :     }
     455          18 :     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           6 :         ndims = ndims2;
     462           6 :         dims = (int *) palloc(ndims * sizeof(int));
     463           6 :         lbs = (int *) palloc(ndims * sizeof(int));
     464           6 :         memcpy(dims, dims2, ndims * sizeof(int));
     465           6 :         memcpy(lbs, lbs2, ndims * sizeof(int));
     466             : 
     467             :         /* increment number of elements in outer array */
     468           6 :         dims[0] += 1;
     469             : 
     470             :         /* make sure the added element matches our existing elements */
     471          12 :         for (i = 0; i < ndims1; i++)
     472             :         {
     473           6 :             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          12 :         ndims = ndims1;
     490          12 :         dims = (int *) palloc(ndims * sizeof(int));
     491          12 :         lbs = (int *) palloc(ndims * sizeof(int));
     492          12 :         memcpy(dims, dims1, ndims * sizeof(int));
     493          12 :         memcpy(lbs, lbs1, ndims * sizeof(int));
     494             : 
     495             :         /* increment number of elements in outer array */
     496          12 :         dims[0] += 1;
     497             : 
     498             :         /* make sure the added element matches our existing elements */
     499          24 :         for (i = 0; i < ndims2; i++)
     500             :         {
     501          12 :             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       28250 :     nitems = ArrayGetNItems(ndims, dims);
     512       28250 :     ArrayCheckBounds(ndims, dims, lbs);
     513             : 
     514             :     /* build the result array */
     515       28250 :     ndatabytes = ndatabytes1 + ndatabytes2;
     516       28250 :     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       28250 :         dataoffset = 0;         /* marker for no null bitmap */
     524       28250 :         nbytes = ndatabytes + ARR_OVERHEAD_NONULLS(ndims);
     525             :     }
     526       28250 :     result = (ArrayType *) palloc0(nbytes);
     527       28250 :     SET_VARSIZE(result, nbytes);
     528       28250 :     result->ndim = ndims;
     529       28250 :     result->dataoffset = dataoffset;
     530       28250 :     result->elemtype = element_type;
     531       28250 :     memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
     532       28250 :     memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
     533             :     /* data area is arg1 then arg2 */
     534       28250 :     memcpy(ARR_DATA_PTR(result), dat1, ndatabytes1);
     535       28250 :     memcpy(ARR_DATA_PTR(result) + ndatabytes1, dat2, ndatabytes2);
     536             :     /* handle the null bitmap if needed */
     537       28250 :     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       28250 :     PG_RETURN_ARRAYTYPE_P(result);
     548             : }
     549             : 
     550             : 
     551             : /*
     552             :  * ARRAY_AGG(anynonarray) aggregate function
     553             :  */
     554             : Datum
     555     2511068 : array_agg_transfn(PG_FUNCTION_ARGS)
     556             : {
     557     2511068 :     Oid         arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
     558             :     MemoryContext aggcontext;
     559             :     ArrayBuildState *state;
     560             :     Datum       elem;
     561             : 
     562     2511068 :     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     2511068 :     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     2511068 :     if (PG_ARGISNULL(0))
     580      140550 :         state = initArrayResult(arg1_typeid, aggcontext, false);
     581             :     else
     582     2370518 :         state = (ArrayBuildState *) PG_GETARG_POINTER(0);
     583             : 
     584     2511068 :     elem = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1);
     585             : 
     586     2511068 :     state = accumArrayResult(state,
     587             :                              elem,
     588     2511068 :                              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     2511068 :     PG_RETURN_POINTER(state);
     598             : }
     599             : 
     600             : Datum
     601          80 : array_agg_combine(PG_FUNCTION_ARGS)
     602             : {
     603             :     ArrayBuildState *state1;
     604             :     ArrayBuildState *state2;
     605             :     MemoryContext agg_context;
     606             :     MemoryContext old_context;
     607             : 
     608          80 :     if (!AggCheckCallContext(fcinfo, &agg_context))
     609           0 :         elog(ERROR, "aggregate function called in non-aggregate context");
     610             : 
     611          80 :     state1 = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
     612          80 :     state2 = PG_ARGISNULL(1) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(1);
     613             : 
     614          80 :     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          80 :     if (state1 == NULL)
     626             :     {
     627             :         /* We must copy state2's data into the agg_context */
     628          60 :         state1 = initArrayResultWithSize(state2->element_type, agg_context,
     629             :                                          false, state2->alen);
     630             : 
     631          60 :         old_context = MemoryContextSwitchTo(agg_context);
     632             : 
     633       23280 :         for (int i = 0; i < state2->nelems; i++)
     634             :         {
     635       23220 :             if (!state2->dnulls[i])
     636       17416 :                 state1->dvalues[i] = datumCopy(state2->dvalues[i],
     637       17416 :                                                state1->typbyval,
     638       17416 :                                                state1->typlen);
     639             :             else
     640        5804 :                 state1->dvalues[i] = (Datum) 0;
     641             :         }
     642             : 
     643          60 :         MemoryContextSwitchTo(old_context);
     644             : 
     645          60 :         memcpy(state1->dnulls, state2->dnulls, sizeof(bool) * state2->nelems);
     646             : 
     647          60 :         state1->nelems = state2->nelems;
     648             : 
     649          60 :         PG_RETURN_POINTER(state1);
     650             :     }
     651          20 :     else if (state2->nelems > 0)
     652             :     {
     653             :         /* We only need to combine the two states if state2 has any elements */
     654          20 :         int         reqsize = state1->nelems + state2->nelems;
     655          20 :         MemoryContext oldContext = MemoryContextSwitchTo(state1->mcontext);
     656             : 
     657             :         Assert(state1->element_type == state2->element_type);
     658             : 
     659             :         /* Enlarge state1 arrays if needed */
     660          20 :         if (state1->alen < reqsize)
     661             :         {
     662             :             /* Use a power of 2 size rather than allocating just reqsize */
     663          20 :             state1->alen = pg_nextpower2_32(reqsize);
     664          40 :             state1->dvalues = (Datum *) repalloc(state1->dvalues,
     665          20 :                                                  state1->alen * sizeof(Datum));
     666          20 :             state1->dnulls = (bool *) repalloc(state1->dnulls,
     667          20 :                                                state1->alen * sizeof(bool));
     668             :         }
     669             : 
     670             :         /* Copy in the state2 elements to the end of the state1 arrays */
     671        6800 :         for (int i = 0; i < state2->nelems; i++)
     672             :         {
     673        6780 :             if (!state2->dnulls[i])
     674        5084 :                 state1->dvalues[i + state1->nelems] =
     675        5084 :                     datumCopy(state2->dvalues[i],
     676        5084 :                               state1->typbyval,
     677        5084 :                               state1->typlen);
     678             :             else
     679        1696 :                 state1->dvalues[i + state1->nelems] = (Datum) 0;
     680             :         }
     681             : 
     682          20 :         memcpy(&state1->dnulls[state1->nelems], state2->dnulls,
     683          20 :                sizeof(bool) * state2->nelems);
     684             : 
     685          20 :         state1->nelems = reqsize;
     686             : 
     687          20 :         MemoryContextSwitchTo(oldContext);
     688             :     }
     689             : 
     690          20 :     PG_RETURN_POINTER(state1);
     691             : }
     692             : 
     693             : /*
     694             :  * array_agg_serialize
     695             :  *      Serialize ArrayBuildState into bytea.
     696             :  */
     697             : Datum
     698          80 : 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          80 :     state = (ArrayBuildState *) PG_GETARG_POINTER(0);
     708             : 
     709          80 :     pq_begintypsend(&buf);
     710             : 
     711             :     /*
     712             :      * element_type. Putting this first is more convenient in deserialization
     713             :      */
     714          80 :     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          80 :     pq_sendint64(&buf, state->nelems);
     721             : 
     722             :     /* alen can be decided during deserialization */
     723             : 
     724             :     /* typlen */
     725          80 :     pq_sendint16(&buf, state->typlen);
     726             : 
     727             :     /* typbyval */
     728          80 :     pq_sendbyte(&buf, state->typbyval);
     729             : 
     730             :     /* typalign */
     731          80 :     pq_sendbyte(&buf, state->typalign);
     732             : 
     733             :     /* dnulls */
     734          80 :     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          80 :     if (state->typbyval)
     744          80 :         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          80 :     result = pq_endtypsend(&buf);
     782             : 
     783          80 :     PG_RETURN_BYTEA_P(result);
     784             : }
     785             : 
     786             : Datum
     787          80 : 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          80 :     if (!AggCheckCallContext(fcinfo, NULL))
     797           0 :         elog(ERROR, "aggregate function called in non-aggregate context");
     798             : 
     799          80 :     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          80 :     initReadOnlyStringInfo(&buf, VARDATA_ANY(sstate),
     806          80 :                            VARSIZE_ANY_EXHDR(sstate));
     807             : 
     808             :     /* element_type */
     809          80 :     element_type = pq_getmsgint(&buf, 4);
     810             : 
     811             :     /* nelems */
     812          80 :     nelems = pq_getmsgint64(&buf);
     813             : 
     814             :     /* Create output ArrayBuildState with the needed number of elements */
     815          80 :     result = initArrayResultWithSize(element_type, CurrentMemoryContext,
     816             :                                      false, nelems);
     817          80 :     result->nelems = nelems;
     818             : 
     819             :     /* typlen */
     820          80 :     result->typlen = pq_getmsgint(&buf, 2);
     821             : 
     822             :     /* typbyval */
     823          80 :     result->typbyval = pq_getmsgbyte(&buf);
     824             : 
     825             :     /* typalign */
     826          80 :     result->typalign = pq_getmsgbyte(&buf);
     827             : 
     828             :     /* dnulls */
     829          80 :     temp = pq_getmsgbytes(&buf, sizeof(bool) * nelems);
     830          80 :     memcpy(result->dnulls, temp, sizeof(bool) * nelems);
     831             : 
     832             :     /* dvalues --- see comment in array_agg_serialize */
     833          80 :     if (result->typbyval)
     834             :     {
     835          80 :         temp = pq_getmsgbytes(&buf, sizeof(Datum) * nelems);
     836          80 :         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          80 :     pq_getmsgend(&buf);
     893             : 
     894          80 :     PG_RETURN_POINTER(result);
     895             : }
     896             : 
     897             : Datum
     898      154330 : 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      154330 :     state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
     909             : 
     910      154330 :     if (state == NULL)
     911       13778 :         PG_RETURN_NULL();       /* returns null iff no input values */
     912             : 
     913      140552 :     dims[0] = state->nelems;
     914      140552 :     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      140552 :     result = makeMdArrayResult(state, 1, dims, lbs,
     923             :                                CurrentMemoryContext,
     924             :                                false);
     925             : 
     926      140552 :     PG_RETURN_DATUM(result);
     927             : }
     928             : 
     929             : /*
     930             :  * ARRAY_AGG(anyarray) aggregate function
     931             :  */
     932             : Datum
     933       60222 : array_agg_array_transfn(PG_FUNCTION_ARGS)
     934             : {
     935       60222 :     Oid         arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
     936             :     MemoryContext aggcontext;
     937             :     ArrayBuildStateArr *state;
     938             : 
     939       60222 :     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       60222 :     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       60222 :     if (PG_ARGISNULL(0))
     958         224 :         state = initArrayResultArr(arg1_typeid, InvalidOid, aggcontext, false);
     959             :     else
     960       59998 :         state = (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
     961             : 
     962       60222 :     state = accumArrayResultArr(state,
     963             :                                 PG_GETARG_DATUM(1),
     964       60222 :                                 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       60204 :     PG_RETURN_POINTER(state);
     974             : }
     975             : 
     976             : Datum
     977          80 : array_agg_array_combine(PG_FUNCTION_ARGS)
     978             : {
     979             :     ArrayBuildStateArr *state1;
     980             :     ArrayBuildStateArr *state2;
     981             :     MemoryContext agg_context;
     982             :     MemoryContext old_context;
     983             : 
     984          80 :     if (!AggCheckCallContext(fcinfo, &agg_context))
     985           0 :         elog(ERROR, "aggregate function called in non-aggregate context");
     986             : 
     987          80 :     state1 = PG_ARGISNULL(0) ? NULL : (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
     988          80 :     state2 = PG_ARGISNULL(1) ? NULL : (ArrayBuildStateArr *) PG_GETARG_POINTER(1);
     989             : 
     990          80 :     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          80 :     if (state1 == NULL)
    1002             :     {
    1003             :         /* We must copy state2's data into the agg_context */
    1004          60 :         old_context = MemoryContextSwitchTo(agg_context);
    1005             : 
    1006          60 :         state1 = initArrayResultArr(state2->array_type, InvalidOid,
    1007             :                                     agg_context, false);
    1008             : 
    1009          60 :         state1->abytes = state2->abytes;
    1010          60 :         state1->data = (char *) palloc(state1->abytes);
    1011             : 
    1012          60 :         if (state2->nullbitmap)
    1013             :         {
    1014          30 :             int         size = (state2->aitems + 7) / 8;
    1015             : 
    1016          30 :             state1->nullbitmap = (bits8 *) palloc(size);
    1017          30 :             memcpy(state1->nullbitmap, state2->nullbitmap, size);
    1018             :         }
    1019             : 
    1020          60 :         memcpy(state1->data, state2->data, state2->nbytes);
    1021          60 :         state1->nbytes = state2->nbytes;
    1022          60 :         state1->aitems = state2->aitems;
    1023          60 :         state1->nitems = state2->nitems;
    1024          60 :         state1->ndims = state2->ndims;
    1025          60 :         memcpy(state1->dims, state2->dims, sizeof(state2->dims));
    1026          60 :         memcpy(state1->lbs, state2->lbs, sizeof(state2->lbs));
    1027          60 :         state1->array_type = state2->array_type;
    1028          60 :         state1->element_type = state2->element_type;
    1029             : 
    1030          60 :         MemoryContextSwitchTo(old_context);
    1031             : 
    1032          60 :         PG_RETURN_POINTER(state1);
    1033             :     }
    1034             : 
    1035             :     /* We only need to combine the two states if state2 has any items */
    1036          20 :     else if (state2->nitems > 0)
    1037             :     {
    1038             :         MemoryContext oldContext;
    1039          20 :         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          20 :         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          40 :         for (i = 1; i < state1->ndims; i++)
    1055             :         {
    1056          20 :             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          20 :         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          20 :         if (state1->abytes < reqsize)
    1070             :         {
    1071             :             /* use a power of 2 size rather than allocating just reqsize */
    1072          10 :             state1->abytes = pg_nextpower2_32(reqsize);
    1073          10 :             state1->data = (char *) repalloc(state1->data, state1->abytes);
    1074             :         }
    1075             : 
    1076          20 :         if (state2->nullbitmap)
    1077             :         {
    1078          10 :             int         newnitems = state1->nitems + state2->nitems;
    1079             : 
    1080          10 :             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          10 :             else if (newnitems > state1->aitems)
    1093             :             {
    1094          10 :                 int         newaitems = state1->aitems + state2->aitems;
    1095             : 
    1096          10 :                 state1->aitems = pg_nextpower2_32(newaitems);
    1097          10 :                 state1->nullbitmap = (bits8 *)
    1098          10 :                     repalloc(state1->nullbitmap, (state1->aitems + 7) / 8);
    1099             :             }
    1100          10 :             array_bitmap_copy(state1->nullbitmap, state1->nitems,
    1101          10 :                               state2->nullbitmap, 0,
    1102             :                               state2->nitems);
    1103             :         }
    1104             : 
    1105          20 :         memcpy(state1->data + state1->nbytes, state2->data, state2->nbytes);
    1106          20 :         state1->nbytes += state2->nbytes;
    1107          20 :         state1->nitems += state2->nitems;
    1108             : 
    1109          20 :         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          20 :         MemoryContextSwitchTo(oldContext);
    1116             :     }
    1117             : 
    1118          20 :     PG_RETURN_POINTER(state1);
    1119             : }
    1120             : 
    1121             : /*
    1122             :  * array_agg_array_serialize
    1123             :  *      Serialize ArrayBuildStateArr into bytea.
    1124             :  */
    1125             : Datum
    1126          80 : 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          80 :     state = (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
    1136             : 
    1137          80 :     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          80 :     pq_sendint32(&buf, state->element_type);
    1144             : 
    1145             :     /* array_type */
    1146          80 :     pq_sendint32(&buf, state->array_type);
    1147             : 
    1148             :     /* nbytes */
    1149          80 :     pq_sendint32(&buf, state->nbytes);
    1150             : 
    1151             :     /* data */
    1152          80 :     pq_sendbytes(&buf, state->data, state->nbytes);
    1153             : 
    1154             :     /* abytes */
    1155          80 :     pq_sendint32(&buf, state->abytes);
    1156             : 
    1157             :     /* aitems */
    1158          80 :     pq_sendint32(&buf, state->aitems);
    1159             : 
    1160             :     /* nullbitmap */
    1161          80 :     if (state->nullbitmap)
    1162             :     {
    1163             :         Assert(state->aitems > 0);
    1164          40 :         pq_sendbytes(&buf, state->nullbitmap, (state->aitems + 7) / 8);
    1165             :     }
    1166             : 
    1167             :     /* nitems */
    1168          80 :     pq_sendint32(&buf, state->nitems);
    1169             : 
    1170             :     /* ndims */
    1171          80 :     pq_sendint32(&buf, state->ndims);
    1172             : 
    1173             :     /* dims: XXX should we just send ndims elements? */
    1174          80 :     pq_sendbytes(&buf, state->dims, sizeof(state->dims));
    1175             : 
    1176             :     /* lbs */
    1177          80 :     pq_sendbytes(&buf, state->lbs, sizeof(state->lbs));
    1178             : 
    1179          80 :     result = pq_endtypsend(&buf);
    1180             : 
    1181          80 :     PG_RETURN_BYTEA_P(result);
    1182             : }
    1183             : 
    1184             : Datum
    1185          80 : 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          80 :     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          80 :     initReadOnlyStringInfo(&buf, VARDATA_ANY(sstate),
    1205          80 :                            VARSIZE_ANY_EXHDR(sstate));
    1206             : 
    1207             :     /* element_type */
    1208          80 :     element_type = pq_getmsgint(&buf, 4);
    1209             : 
    1210             :     /* array_type */
    1211          80 :     array_type = pq_getmsgint(&buf, 4);
    1212             : 
    1213             :     /* nbytes */
    1214          80 :     nbytes = pq_getmsgint(&buf, 4);
    1215             : 
    1216          80 :     result = initArrayResultArr(array_type, element_type,
    1217             :                                 CurrentMemoryContext, false);
    1218             : 
    1219          80 :     result->abytes = 1024;
    1220         110 :     while (result->abytes < nbytes)
    1221          30 :         result->abytes *= 2;
    1222             : 
    1223          80 :     result->data = (char *) palloc(result->abytes);
    1224             : 
    1225             :     /* data */
    1226          80 :     temp = pq_getmsgbytes(&buf, nbytes);
    1227          80 :     memcpy(result->data, temp, nbytes);
    1228          80 :     result->nbytes = nbytes;
    1229             : 
    1230             :     /* abytes */
    1231          80 :     result->abytes = pq_getmsgint(&buf, 4);
    1232             : 
    1233             :     /* aitems: might be 0 */
    1234          80 :     result->aitems = pq_getmsgint(&buf, 4);
    1235             : 
    1236             :     /* nullbitmap */
    1237          80 :     if (result->aitems > 0)
    1238             :     {
    1239          40 :         int         size = (result->aitems + 7) / 8;
    1240             : 
    1241          40 :         result->nullbitmap = (bits8 *) palloc(size);
    1242          40 :         temp = pq_getmsgbytes(&buf, size);
    1243          40 :         memcpy(result->nullbitmap, temp, size);
    1244             :     }
    1245             :     else
    1246          40 :         result->nullbitmap = NULL;
    1247             : 
    1248             :     /* nitems */
    1249          80 :     result->nitems = pq_getmsgint(&buf, 4);
    1250             : 
    1251             :     /* ndims */
    1252          80 :     result->ndims = pq_getmsgint(&buf, 4);
    1253             : 
    1254             :     /* dims */
    1255          80 :     temp = pq_getmsgbytes(&buf, sizeof(result->dims));
    1256          80 :     memcpy(result->dims, temp, sizeof(result->dims));
    1257             : 
    1258             :     /* lbs */
    1259          80 :     temp = pq_getmsgbytes(&buf, sizeof(result->lbs));
    1260          80 :     memcpy(result->lbs, temp, sizeof(result->lbs));
    1261             : 
    1262          80 :     pq_getmsgend(&buf);
    1263             : 
    1264          80 :     PG_RETURN_POINTER(result);
    1265             : }
    1266             : 
    1267             : Datum
    1268         186 : 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         186 :     state = PG_ARGISNULL(0) ? NULL : (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
    1277             : 
    1278         186 :     if (state == NULL)
    1279           0 :         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         186 :     result = makeArrayResultArr(state, CurrentMemoryContext, false);
    1288             : 
    1289         186 :     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         216 : array_position(PG_FUNCTION_ARGS)
    1302             : {
    1303         216 :     return array_position_common(fcinfo);
    1304             : }
    1305             : 
    1306             : Datum
    1307          18 : array_position_start(PG_FUNCTION_ARGS)
    1308             : {
    1309          18 :     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         234 : array_position_common(FunctionCallInfo fcinfo)
    1321             : {
    1322             :     ArrayType  *array;
    1323         234 :     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         234 :     bool        found = false;
    1331             :     TypeCacheEntry *typentry;
    1332             :     ArrayMetaState *my_extra;
    1333             :     bool        null_search;
    1334             :     ArrayIterator array_iterator;
    1335             : 
    1336         234 :     if (PG_ARGISNULL(0))
    1337           0 :         PG_RETURN_NULL();
    1338             : 
    1339         234 :     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         234 :     if (ARR_NDIM(array) > 1)
    1346           6 :         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         228 :     if (ARR_NDIM(array) < 1)
    1352           0 :         PG_RETURN_NULL();
    1353             : 
    1354         228 :     if (PG_ARGISNULL(1))
    1355             :     {
    1356             :         /* fast return when the array doesn't have nulls */
    1357          12 :         if (!array_contains_nulls(array))
    1358           6 :             PG_RETURN_NULL();
    1359           6 :         searched_element = (Datum) 0;
    1360           6 :         null_search = true;
    1361             :     }
    1362             :     else
    1363             :     {
    1364         216 :         searched_element = PG_GETARG_DATUM(1);
    1365         216 :         null_search = false;
    1366             :     }
    1367             : 
    1368         222 :     element_type = ARR_ELEMTYPE(array);
    1369         222 :     position = (ARR_LBOUND(array))[0] - 1;
    1370             : 
    1371             :     /* figure out where to start */
    1372         222 :     if (PG_NARGS() == 3)
    1373             :     {
    1374          18 :         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          18 :         position_min = PG_GETARG_INT32(2);
    1380             :     }
    1381             :     else
    1382         204 :         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         222 :     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
    1390         222 :     if (my_extra == NULL)
    1391             :     {
    1392          84 :         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
    1393             :                                                       sizeof(ArrayMetaState));
    1394          84 :         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
    1395          84 :         my_extra->element_type = ~element_type;
    1396             :     }
    1397             : 
    1398         222 :     if (my_extra->element_type != element_type)
    1399             :     {
    1400          84 :         get_typlenbyvalalign(element_type,
    1401             :                              &my_extra->typlen,
    1402             :                              &my_extra->typbyval,
    1403             :                              &my_extra->typalign);
    1404             : 
    1405          84 :         typentry = lookup_type_cache(element_type, TYPECACHE_EQ_OPR_FINFO);
    1406             : 
    1407          84 :         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          84 :         my_extra->element_type = element_type;
    1414          84 :         fmgr_info_cxt(typentry->eq_opr_finfo.fn_oid, &my_extra->proc,
    1415          84 :                       fcinfo->flinfo->fn_mcxt);
    1416             :     }
    1417             : 
    1418             :     /* Examine each array element until we find a match. */
    1419         222 :     array_iterator = array_create_iterator(array, 0, my_extra);
    1420         618 :     while (array_iterate(array_iterator, &value, &isnull))
    1421             :     {
    1422         576 :         position++;
    1423             : 
    1424             :         /* skip initial elements if caller requested so */
    1425         576 :         if (position < position_min)
    1426          78 :             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         498 :         if (isnull || null_search)
    1433             :         {
    1434          42 :             if (isnull && null_search)
    1435             :             {
    1436           6 :                 found = true;
    1437           6 :                 break;
    1438             :             }
    1439             :             else
    1440          36 :                 continue;
    1441             :         }
    1442             : 
    1443             :         /* not nulls, so run the operator */
    1444         456 :         if (DatumGetBool(FunctionCall2Coll(&my_extra->proc, collation,
    1445             :                                            searched_element, value)))
    1446             :         {
    1447         174 :             found = true;
    1448         174 :             break;
    1449             :         }
    1450             :     }
    1451             : 
    1452         222 :     array_free_iterator(array_iterator);
    1453             : 
    1454             :     /* Avoid leaking memory when handed toasted input */
    1455         222 :     PG_FREE_IF_COPY(array, 0);
    1456             : 
    1457         222 :     if (!found)
    1458          42 :         PG_RETURN_NULL();
    1459             : 
    1460         180 :     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          60 : array_positions(PG_FUNCTION_ARGS)
    1476             : {
    1477             :     ArrayType  *array;
    1478          60 :     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          60 :     ArrayBuildState *astate = NULL;
    1489             : 
    1490          60 :     if (PG_ARGISNULL(0))
    1491          12 :         PG_RETURN_NULL();
    1492             : 
    1493          48 :     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          48 :     if (ARR_NDIM(array) > 1)
    1500           6 :         ereport(ERROR,
    1501             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1502             :                  errmsg("searching for elements in multidimensional arrays is not supported")));
    1503             : 
    1504          42 :     astate = initArrayResult(INT4OID, CurrentMemoryContext, false);
    1505             : 
    1506             :     /* Searching in an empty array is well-defined, though: it always fails */
    1507          42 :     if (ARR_NDIM(array) < 1)
    1508           0 :         PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
    1509             : 
    1510          42 :     if (PG_ARGISNULL(1))
    1511             :     {
    1512             :         /* fast return when the array doesn't have nulls */
    1513          12 :         if (!array_contains_nulls(array))
    1514           6 :             PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
    1515           6 :         searched_element = (Datum) 0;
    1516           6 :         null_search = true;
    1517             :     }
    1518             :     else
    1519             :     {
    1520          30 :         searched_element = PG_GETARG_DATUM(1);
    1521          30 :         null_search = false;
    1522             :     }
    1523             : 
    1524          36 :     element_type = ARR_ELEMTYPE(array);
    1525          36 :     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          36 :     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
    1533          36 :     if (my_extra == NULL)
    1534             :     {
    1535          30 :         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
    1536             :                                                       sizeof(ArrayMetaState));
    1537          30 :         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
    1538          30 :         my_extra->element_type = ~element_type;
    1539             :     }
    1540             : 
    1541          36 :     if (my_extra->element_type != element_type)
    1542             :     {
    1543          30 :         get_typlenbyvalalign(element_type,
    1544             :                              &my_extra->typlen,
    1545             :                              &my_extra->typbyval,
    1546             :                              &my_extra->typalign);
    1547             : 
    1548          30 :         typentry = lookup_type_cache(element_type, TYPECACHE_EQ_OPR_FINFO);
    1549             : 
    1550          30 :         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          30 :         my_extra->element_type = element_type;
    1557          30 :         fmgr_info_cxt(typentry->eq_opr_finfo.fn_oid, &my_extra->proc,
    1558          30 :                       fcinfo->flinfo->fn_mcxt);
    1559             :     }
    1560             : 
    1561             :     /*
    1562             :      * Accumulate each array position iff the element matches the given
    1563             :      * element.
    1564             :      */
    1565          36 :     array_iterator = array_create_iterator(array, 0, my_extra);
    1566         816 :     while (array_iterate(array_iterator, &value, &isnull))
    1567             :     {
    1568         780 :         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         780 :         if (isnull || null_search)
    1575             :         {
    1576          72 :             if (isnull && null_search)
    1577             :                 astate =
    1578          12 :                     accumArrayResult(astate, Int32GetDatum(position), false,
    1579             :                                      INT4OID, CurrentMemoryContext);
    1580             : 
    1581          72 :             continue;
    1582             :         }
    1583             : 
    1584             :         /* not nulls, so run the operator */
    1585         708 :         if (DatumGetBool(FunctionCall2Coll(&my_extra->proc, collation,
    1586             :                                            searched_element, value)))
    1587             :             astate =
    1588          90 :                 accumArrayResult(astate, Int32GetDatum(position), false,
    1589             :                                  INT4OID, CurrentMemoryContext);
    1590             :     }
    1591             : 
    1592          36 :     array_free_iterator(array_iterator);
    1593             : 
    1594             :     /* Avoid leaking memory when handed toasted input */
    1595          36 :     PG_FREE_IF_COPY(array, 0);
    1596             : 
    1597          36 :     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          48 : 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          48 :     ndim = ARR_NDIM(array);
    1633          48 :     dims = ARR_DIMS(array);
    1634          48 :     lbs = ARR_LBOUND(array);
    1635             : 
    1636          48 :     elmlen = typentry->typlen;
    1637          48 :     elmbyval = typentry->typbyval;
    1638          48 :     elmalign = typentry->typalign;
    1639             : 
    1640             :     /* If the target array is empty, exit fast */
    1641          48 :     if (ndim < 1 || dims[0] < 1 || n < 1)
    1642           0 :         return construct_empty_array(elmtyp);
    1643             : 
    1644          48 :     deconstruct_array(array, elmtyp, elmlen, elmbyval, elmalign,
    1645             :                       &elms, &nuls, &nelm);
    1646             : 
    1647          48 :     nitem = dims[0];            /* total number of items */
    1648          48 :     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          48 :     ielms = elms;
    1659          48 :     inuls = nuls;
    1660         228 :     for (int i = 0; i < n; i++)
    1661             :     {
    1662         180 :         int         j = (int) pg_prng_uint64_range(&pg_global_prng_state, i, nitem - 1) * nelm;
    1663         180 :         Datum      *jelms = elms + j;
    1664         180 :         bool       *jnuls = nuls + j;
    1665             : 
    1666             :         /* Swap i'th and j'th items; advance ielms/inuls to next item */
    1667         492 :         for (int k = 0; k < nelm; k++)
    1668             :         {
    1669         312 :             Datum       elm = *ielms;
    1670         312 :             bool        nul = *inuls;
    1671             : 
    1672         312 :             *ielms++ = *jelms;
    1673         312 :             *inuls++ = *jnuls;
    1674         312 :             *jelms++ = elm;
    1675         312 :             *jnuls++ = nul;
    1676             :         }
    1677             :     }
    1678             : 
    1679             :     /* Set up dimensions of the result */
    1680          48 :     memcpy(rdims, dims, ndim * sizeof(int));
    1681          48 :     memcpy(rlbs, lbs, ndim * sizeof(int));
    1682          48 :     rdims[0] = n;
    1683          48 :     if (!keep_lb)
    1684          24 :         rlbs[0] = 1;
    1685             : 
    1686          48 :     result = construct_md_array(elms, nuls, ndim, rdims, rlbs,
    1687             :                                 elmtyp, elmlen, elmbyval, elmalign);
    1688             : 
    1689          48 :     pfree(elms);
    1690          48 :     pfree(nuls);
    1691             : 
    1692          48 :     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          24 : array_shuffle(PG_FUNCTION_ARGS)
    1703             : {
    1704          24 :     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          24 :     if (ARR_NDIM(array) < 1 || ARR_DIMS(array)[0] < 2)
    1714           0 :         PG_RETURN_ARRAYTYPE_P(array);
    1715             : 
    1716          24 :     elmtyp = ARR_ELEMTYPE(array);
    1717          24 :     typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
    1718          24 :     if (typentry == NULL || typentry->type_id != elmtyp)
    1719             :     {
    1720          24 :         typentry = lookup_type_cache(elmtyp, 0);
    1721          24 :         fcinfo->flinfo->fn_extra = typentry;
    1722             :     }
    1723             : 
    1724          24 :     result = array_shuffle_n(array, ARR_DIMS(array)[0], true, elmtyp, typentry);
    1725             : 
    1726          24 :     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          36 : array_sample(PG_FUNCTION_ARGS)
    1737             : {
    1738          36 :     ArrayType  *array = PG_GETARG_ARRAYTYPE_P(0);
    1739          36 :     int         n = PG_GETARG_INT32(1);
    1740             :     ArrayType  *result;
    1741             :     Oid         elmtyp;
    1742             :     TypeCacheEntry *typentry;
    1743             :     int         nitem;
    1744             : 
    1745          36 :     nitem = (ARR_NDIM(array) < 1) ? 0 : ARR_DIMS(array)[0];
    1746             : 
    1747          36 :     if (n < 0 || n > nitem)
    1748          12 :         ereport(ERROR,
    1749             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1750             :                  errmsg("sample size must be between 0 and %d", nitem)));
    1751             : 
    1752          24 :     elmtyp = ARR_ELEMTYPE(array);
    1753          24 :     typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
    1754          24 :     if (typentry == NULL || typentry->type_id != elmtyp)
    1755             :     {
    1756          24 :         typentry = lookup_type_cache(elmtyp, 0);
    1757          24 :         fcinfo->flinfo->fn_extra = typentry;
    1758             :     }
    1759             : 
    1760          24 :     result = array_shuffle_n(array, n, false, elmtyp, typentry);
    1761             : 
    1762          24 :     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          18 : 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          18 :     ndim = ARR_NDIM(array);
    1794          18 :     dims = ARR_DIMS(array);
    1795          18 :     lbs = ARR_LBOUND(array);
    1796             : 
    1797          18 :     elmlen = typentry->typlen;
    1798          18 :     elmbyval = typentry->typbyval;
    1799          18 :     elmalign = typentry->typalign;
    1800             : 
    1801          18 :     deconstruct_array(array, elmtyp, elmlen, elmbyval, elmalign,
    1802             :                       &elms, &nuls, &nelm);
    1803             : 
    1804          18 :     nitem = dims[0];            /* total number of items */
    1805          18 :     nelm /= nitem;              /* number of elements per item */
    1806             : 
    1807             :     /* Reverse the array */
    1808          18 :     ielms = elms;
    1809          18 :     inuls = nuls;
    1810          54 :     for (int i = 0; i < nitem / 2; i++)
    1811             :     {
    1812          36 :         int         j = (nitem - i - 1) * nelm;
    1813          36 :         Datum      *jelms = elms + j;
    1814          36 :         bool       *jnuls = nuls + j;
    1815             : 
    1816             :         /* Swap i'th and j'th items; advance ielms/inuls to next item */
    1817          84 :         for (int k = 0; k < nelm; k++)
    1818             :         {
    1819          48 :             Datum       elm = *ielms;
    1820          48 :             bool        nul = *inuls;
    1821             : 
    1822          48 :             *ielms++ = *jelms;
    1823          48 :             *inuls++ = *jnuls;
    1824          48 :             *jelms++ = elm;
    1825          48 :             *jnuls++ = nul;
    1826             :         }
    1827             :     }
    1828             : 
    1829             :     /* Set up dimensions of the result */
    1830          18 :     memcpy(rdims, dims, ndim * sizeof(int));
    1831          18 :     memcpy(rlbs, lbs, ndim * sizeof(int));
    1832          18 :     rdims[0] = nitem;
    1833             : 
    1834          18 :     result = construct_md_array(elms, nuls, ndim, rdims, rlbs,
    1835             :                                 elmtyp, elmlen, elmbyval, elmalign);
    1836             : 
    1837          18 :     pfree(elms);
    1838          18 :     pfree(nuls);
    1839             : 
    1840          18 :     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          30 : array_reverse(PG_FUNCTION_ARGS)
    1851             : {
    1852          30 :     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          30 :     if (ARR_NDIM(array) < 1 || ARR_DIMS(array)[0] < 2)
    1862          12 :         PG_RETURN_ARRAYTYPE_P(array);
    1863             : 
    1864          18 :     elmtyp = ARR_ELEMTYPE(array);
    1865          18 :     typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
    1866          18 :     if (typentry == NULL || typentry->type_id != elmtyp)
    1867             :     {
    1868          18 :         typentry = lookup_type_cache(elmtyp, 0);
    1869          18 :         fcinfo->flinfo->fn_extra = (void *) typentry;
    1870             :     }
    1871             : 
    1872          18 :     result = array_reverse_n(array, elmtyp, typentry);
    1873             : 
    1874          18 :     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         156 : array_sort_internal(ArrayType *array, bool descending, bool nulls_first,
    1884             :                     FunctionCallInfo fcinfo)
    1885             : {
    1886             :     ArrayType  *newarray;
    1887         156 :     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         156 :     ArrayBuildStateAny *astate = NULL;
    1900             : 
    1901         156 :     ndim = ARR_NDIM(array);
    1902         156 :     dims = ARR_DIMS(array);
    1903         156 :     lbs = ARR_LBOUND(array);
    1904             : 
    1905             :     /* Quick exit if we don't need to sort */
    1906         156 :     if (ndim < 1 || dims[0] < 2)
    1907          24 :         return array;
    1908             : 
    1909             :     /* Set up cache area if we didn't already */
    1910         132 :     cache_info = (ArraySortCachedInfo *) fcinfo->flinfo->fn_extra;
    1911         132 :     if (cache_info == NULL)
    1912             :     {
    1913             :         cache_info = (ArraySortCachedInfo *)
    1914         132 :             MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt,
    1915             :                                    sizeof(ArraySortCachedInfo));
    1916         132 :         fcinfo->flinfo->fn_extra = cache_info;
    1917             :     }
    1918             : 
    1919             :     /* Fetch and cache required data if we don't have it */
    1920         132 :     elmtyp = ARR_ELEMTYPE(array);
    1921         132 :     if (elmtyp != cache_info->array_meta.element_type)
    1922             :     {
    1923             :         TypeCacheEntry *typentry;
    1924             : 
    1925         132 :         typentry = lookup_type_cache(elmtyp,
    1926             :                                      TYPECACHE_LT_OPR | TYPECACHE_GT_OPR);
    1927         132 :         cache_info->array_meta.element_type = elmtyp;
    1928         132 :         cache_info->array_meta.typlen = typentry->typlen;
    1929         132 :         cache_info->array_meta.typbyval = typentry->typbyval;
    1930         132 :         cache_info->array_meta.typalign = typentry->typalign;
    1931         132 :         cache_info->elem_lt_opr = typentry->lt_opr;
    1932         132 :         cache_info->elem_gt_opr = typentry->gt_opr;
    1933         132 :         cache_info->array_type = typentry->typarray;
    1934             :     }
    1935             : 
    1936             :     /* Identify the sort operator to use */
    1937         132 :     if (ndim == 1)
    1938             :     {
    1939             :         /* Need to sort the element type */
    1940          90 :         sort_typ = elmtyp;
    1941          90 :         sort_opr = (descending ? cache_info->elem_gt_opr : cache_info->elem_lt_opr);
    1942             :     }
    1943             :     else
    1944             :     {
    1945             :         /* Otherwise we're sorting arrays */
    1946          42 :         sort_typ = cache_info->array_type;
    1947          42 :         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          42 :         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         132 :     if (!OidIsValid(sort_opr))
    1961           6 :         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         126 :     tuplesortstate = tuplesort_begin_datum(sort_typ,
    1968             :                                            sort_opr,
    1969             :                                            collation,
    1970             :                                            nulls_first,
    1971             :                                            work_mem,
    1972             :                                            NULL,
    1973             :                                            TUPLESORT_NONE);
    1974             : 
    1975         126 :     array_iterator = array_create_iterator(array, ndim - 1,
    1976             :                                            &cache_info->array_meta);
    1977         666 :     while (array_iterate(array_iterator, &value, &isnull))
    1978             :     {
    1979         540 :         tuplesort_putdatum(tuplesortstate, value, isnull);
    1980             :     }
    1981         126 :     array_free_iterator(array_iterator);
    1982             : 
    1983             :     /* Do the sort */
    1984         126 :     tuplesort_performsort(tuplesortstate);
    1985             : 
    1986             :     /* Extract results into a new array */
    1987         648 :     while (tuplesort_getdatum(tuplesortstate, true, false, &value, &isnull, NULL))
    1988             :     {
    1989         528 :         astate = accumArrayResultAny(astate, value, isnull,
    1990             :                                      sort_typ, CurrentMemoryContext);
    1991             :     }
    1992         120 :     tuplesort_end(tuplesortstate);
    1993             : 
    1994         120 :     newarray = DatumGetArrayTypeP(makeArrayResultAny(astate,
    1995             :                                                      CurrentMemoryContext,
    1996             :                                                      true));
    1997             : 
    1998             :     /* Adjust lower bound to match the input */
    1999         120 :     ARR_LBOUND(newarray)[0] = lbs[0];
    2000             : 
    2001         120 :     return newarray;
    2002             : }
    2003             : 
    2004             : Datum
    2005         120 : array_sort(PG_FUNCTION_ARGS)
    2006             : {
    2007         120 :     ArrayType  *array = PG_GETARG_ARRAYTYPE_P(0);
    2008             : 
    2009         120 :     PG_RETURN_ARRAYTYPE_P(array_sort_internal(array,
    2010             :                                               false,
    2011             :                                               false,
    2012             :                                               fcinfo));
    2013             : }
    2014             : 
    2015             : Datum
    2016          12 : array_sort_order(PG_FUNCTION_ARGS)
    2017             : {
    2018          12 :     ArrayType  *array = PG_GETARG_ARRAYTYPE_P(0);
    2019          12 :     bool        descending = PG_GETARG_BOOL(1);
    2020             : 
    2021          12 :     PG_RETURN_ARRAYTYPE_P(array_sort_internal(array,
    2022             :                                               descending,
    2023             :                                               descending,
    2024             :                                               fcinfo));
    2025             : }
    2026             : 
    2027             : Datum
    2028          24 : array_sort_order_nulls_first(PG_FUNCTION_ARGS)
    2029             : {
    2030          24 :     ArrayType  *array = PG_GETARG_ARRAYTYPE_P(0);
    2031          24 :     bool        descending = PG_GETARG_BOOL(1);
    2032          24 :     bool        nulls_first = PG_GETARG_BOOL(2);
    2033             : 
    2034          24 :     PG_RETURN_ARRAYTYPE_P(array_sort_internal(array,
    2035             :                                               descending,
    2036             :                                               nulls_first,
    2037             :                                               fcinfo));
    2038             : }

Generated by: LCOV version 1.14