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

Generated by: LCOV version 1.14