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

Generated by: LCOV version 1.14