LCOV - code coverage report
Current view: top level - src/backend/utils/adt - array_userfuncs.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 522 605 86.3 %
Date: 2024-11-21 08:14:44 Functions: 23 23 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-2024, PostgreSQL Global Development Group
       7             :  *
       8             :  * IDENTIFICATION
       9             :  *    src/backend/utils/adt/array_userfuncs.c
      10             :  *
      11             :  *-------------------------------------------------------------------------
      12             :  */
      13             : #include "postgres.h"
      14             : 
      15             : #include "catalog/pg_type.h"
      16             : #include "common/int.h"
      17             : #include "common/pg_prng.h"
      18             : #include "libpq/pqformat.h"
      19             : #include "port/pg_bitutils.h"
      20             : #include "utils/array.h"
      21             : #include "utils/builtins.h"
      22             : #include "utils/datum.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       29908 : 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       29908 :     if (PG_ARGISNULL(0))
     273             :     {
     274        1626 :         if (PG_ARGISNULL(1))
     275           0 :             PG_RETURN_NULL();
     276        1626 :         result = PG_GETARG_ARRAYTYPE_P(1);
     277        1626 :         PG_RETURN_ARRAYTYPE_P(result);
     278             :     }
     279       28282 :     if (PG_ARGISNULL(1))
     280             :     {
     281           0 :         result = PG_GETARG_ARRAYTYPE_P(0);
     282           0 :         PG_RETURN_ARRAYTYPE_P(result);
     283             :     }
     284             : 
     285       28282 :     v1 = PG_GETARG_ARRAYTYPE_P(0);
     286       28282 :     v2 = PG_GETARG_ARRAYTYPE_P(1);
     287             : 
     288       28282 :     element_type1 = ARR_ELEMTYPE(v1);
     289       28282 :     element_type2 = ARR_ELEMTYPE(v2);
     290             : 
     291             :     /* Check we have matching element types */
     292       28282 :     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       28282 :     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       28282 :     ndims1 = ARR_NDIM(v1);
     314       28282 :     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       28282 :     if (ndims1 == 0 && ndims2 > 0)
     323           0 :         PG_RETURN_ARRAYTYPE_P(v2);
     324             : 
     325       28282 :     if (ndims2 == 0)
     326          34 :         PG_RETURN_ARRAYTYPE_P(v1);
     327             : 
     328             :     /* the rest fall under rule 3, 4, or 5 */
     329       28248 :     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       28248 :     lbs1 = ARR_LBOUND(v1);
     341       28248 :     lbs2 = ARR_LBOUND(v2);
     342       28248 :     dims1 = ARR_DIMS(v1);
     343       28248 :     dims2 = ARR_DIMS(v2);
     344       28248 :     dat1 = ARR_DATA_PTR(v1);
     345       28248 :     dat2 = ARR_DATA_PTR(v2);
     346       28248 :     bitmap1 = ARR_NULLBITMAP(v1);
     347       28248 :     bitmap2 = ARR_NULLBITMAP(v2);
     348       28248 :     nitems1 = ArrayGetNItems(ndims1, dims1);
     349       28248 :     nitems2 = ArrayGetNItems(ndims2, dims2);
     350       28248 :     ndatabytes1 = ARR_SIZE(v1) - ARR_DATA_OFFSET(v1);
     351       28248 :     ndatabytes2 = ARR_SIZE(v2) - ARR_DATA_OFFSET(v2);
     352             : 
     353       28248 :     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       28230 :         ndims = ndims1;
     360       28230 :         dims = (int *) palloc(ndims * sizeof(int));
     361       28230 :         lbs = (int *) palloc(ndims * sizeof(int));
     362             : 
     363       28230 :         dims[0] = dims1[0] + dims2[0];
     364       28230 :         lbs[0] = lbs1[0];
     365             : 
     366       28242 :         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       28248 :     nitems = ArrayGetNItems(ndims, dims);
     436       28248 :     ArrayCheckBounds(ndims, dims, lbs);
     437             : 
     438             :     /* build the result array */
     439       28248 :     ndatabytes = ndatabytes1 + ndatabytes2;
     440       28248 :     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       28248 :         dataoffset = 0;         /* marker for no null bitmap */
     448       28248 :         nbytes = ndatabytes + ARR_OVERHEAD_NONULLS(ndims);
     449             :     }
     450       28248 :     result = (ArrayType *) palloc0(nbytes);
     451       28248 :     SET_VARSIZE(result, nbytes);
     452       28248 :     result->ndim = ndims;
     453       28248 :     result->dataoffset = dataoffset;
     454       28248 :     result->elemtype = element_type;
     455       28248 :     memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
     456       28248 :     memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
     457             :     /* data area is arg1 then arg2 */
     458       28248 :     memcpy(ARR_DATA_PTR(result), dat1, ndatabytes1);
     459       28248 :     memcpy(ARR_DATA_PTR(result) + ndatabytes1, dat2, ndatabytes2);
     460             :     /* handle the null bitmap if needed */
     461       28248 :     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       28248 :     PG_RETURN_ARRAYTYPE_P(result);
     472             : }
     473             : 
     474             : 
     475             : /*
     476             :  * ARRAY_AGG(anynonarray) aggregate function
     477             :  */
     478             : Datum
     479     1857764 : array_agg_transfn(PG_FUNCTION_ARGS)
     480             : {
     481     1857764 :     Oid         arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
     482             :     MemoryContext aggcontext;
     483             :     ArrayBuildState *state;
     484             :     Datum       elem;
     485             : 
     486     1857764 :     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     1857764 :     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     1857764 :     if (PG_ARGISNULL(0))
     504       78446 :         state = initArrayResult(arg1_typeid, aggcontext, false);
     505             :     else
     506     1779318 :         state = (ArrayBuildState *) PG_GETARG_POINTER(0);
     507             : 
     508     1857764 :     elem = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1);
     509             : 
     510     1857764 :     state = accumArrayResult(state,
     511             :                              elem,
     512     1857764 :                              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     1857764 :     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       23424 :         for (int i = 0; i < state2->nelems; i++)
     558             :         {
     559       23364 :             if (!state2->dnulls[i])
     560       16910 :                 state1->dvalues[i] = datumCopy(state2->dvalues[i],
     561       16910 :                                                state1->typbyval,
     562       16910 :                                                state1->typlen);
     563             :             else
     564        6454 :                 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        6656 :         for (int i = 0; i < state2->nelems; i++)
     596             :         {
     597        6636 :             if (!state2->dnulls[i])
     598        5590 :                 state1->dvalues[i + state1->nelems] =
     599        5590 :                     datumCopy(state2->dvalues[i],
     600        5590 :                               state1->typbyval,
     601        5590 :                               state1->typlen);
     602             :             else
     603        1046 :                 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             :      * Initialize a StringInfo so that we can "receive" it using the standard
     727             :      * recv-function infrastructure.
     728             :      */
     729          80 :     initReadOnlyStringInfo(&buf, VARDATA_ANY(sstate),
     730          80 :                            VARSIZE_ANY_EXHDR(sstate));
     731             : 
     732             :     /* element_type */
     733          80 :     element_type = pq_getmsgint(&buf, 4);
     734             : 
     735             :     /* nelems */
     736          80 :     nelems = pq_getmsgint64(&buf);
     737             : 
     738             :     /* Create output ArrayBuildState with the needed number of elements */
     739          80 :     result = initArrayResultWithSize(element_type, CurrentMemoryContext,
     740             :                                      false, nelems);
     741          80 :     result->nelems = nelems;
     742             : 
     743             :     /* typlen */
     744          80 :     result->typlen = pq_getmsgint(&buf, 2);
     745             : 
     746             :     /* typbyval */
     747          80 :     result->typbyval = pq_getmsgbyte(&buf);
     748             : 
     749             :     /* typalign */
     750          80 :     result->typalign = pq_getmsgbyte(&buf);
     751             : 
     752             :     /* dnulls */
     753          80 :     temp = pq_getmsgbytes(&buf, sizeof(bool) * nelems);
     754          80 :     memcpy(result->dnulls, temp, sizeof(bool) * nelems);
     755             : 
     756             :     /* dvalues --- see comment in array_agg_serialize */
     757          80 :     if (result->typbyval)
     758             :     {
     759          80 :         temp = pq_getmsgbytes(&buf, sizeof(Datum) * nelems);
     760          80 :         memcpy(result->dvalues, temp, sizeof(Datum) * nelems);
     761             :     }
     762             :     else
     763             :     {
     764             :         DeserialIOData *iodata;
     765             : 
     766             :         /* Avoid repeat catalog lookups for typreceive function */
     767           0 :         iodata = (DeserialIOData *) fcinfo->flinfo->fn_extra;
     768           0 :         if (iodata == NULL)
     769             :         {
     770             :             Oid         typreceive;
     771             : 
     772             :             iodata = (DeserialIOData *)
     773           0 :                 MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
     774             :                                    sizeof(DeserialIOData));
     775           0 :             getTypeBinaryInputInfo(element_type, &typreceive,
     776             :                                    &iodata->typioparam);
     777           0 :             fmgr_info_cxt(typreceive, &iodata->typreceive,
     778           0 :                           fcinfo->flinfo->fn_mcxt);
     779           0 :             fcinfo->flinfo->fn_extra = (void *) iodata;
     780             :         }
     781             : 
     782           0 :         for (int i = 0; i < nelems; i++)
     783             :         {
     784             :             int         itemlen;
     785             :             StringInfoData elem_buf;
     786             : 
     787           0 :             if (result->dnulls[i])
     788             :             {
     789           0 :                 result->dvalues[i] = (Datum) 0;
     790           0 :                 continue;
     791             :             }
     792             : 
     793           0 :             itemlen = pq_getmsgint(&buf, 4);
     794           0 :             if (itemlen < 0 || itemlen > (buf.len - buf.cursor))
     795           0 :                 ereport(ERROR,
     796             :                         (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
     797             :                          errmsg("insufficient data left in message")));
     798             : 
     799             :             /*
     800             :              * Rather than copying data around, we just initialize a
     801             :              * StringInfo pointing to the correct portion of the message
     802             :              * buffer.
     803             :              */
     804           0 :             initReadOnlyStringInfo(&elem_buf, &buf.data[buf.cursor], itemlen);
     805             : 
     806           0 :             buf.cursor += itemlen;
     807             : 
     808             :             /* Now call the element's receiveproc */
     809           0 :             result->dvalues[i] = ReceiveFunctionCall(&iodata->typreceive,
     810             :                                                      &elem_buf,
     811             :                                                      iodata->typioparam,
     812             :                                                      -1);
     813             :         }
     814             :     }
     815             : 
     816          80 :     pq_getmsgend(&buf);
     817             : 
     818          80 :     PG_RETURN_POINTER(result);
     819             : }
     820             : 
     821             : Datum
     822       87840 : array_agg_finalfn(PG_FUNCTION_ARGS)
     823             : {
     824             :     Datum       result;
     825             :     ArrayBuildState *state;
     826             :     int         dims[1];
     827             :     int         lbs[1];
     828             : 
     829             :     /* cannot be called directly because of internal-type argument */
     830             :     Assert(AggCheckCallContext(fcinfo, NULL));
     831             : 
     832       87840 :     state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
     833             : 
     834       87840 :     if (state == NULL)
     835        9392 :         PG_RETURN_NULL();       /* returns null iff no input values */
     836             : 
     837       78448 :     dims[0] = state->nelems;
     838       78448 :     lbs[0] = 1;
     839             : 
     840             :     /*
     841             :      * Make the result.  We cannot release the ArrayBuildState because
     842             :      * sometimes aggregate final functions are re-executed.  Rather, it is
     843             :      * nodeAgg.c's responsibility to reset the aggcontext when it's safe to do
     844             :      * so.
     845             :      */
     846       78448 :     result = makeMdArrayResult(state, 1, dims, lbs,
     847             :                                CurrentMemoryContext,
     848             :                                false);
     849             : 
     850       78448 :     PG_RETURN_DATUM(result);
     851             : }
     852             : 
     853             : /*
     854             :  * ARRAY_AGG(anyarray) aggregate function
     855             :  */
     856             : Datum
     857       60222 : array_agg_array_transfn(PG_FUNCTION_ARGS)
     858             : {
     859       60222 :     Oid         arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
     860             :     MemoryContext aggcontext;
     861             :     ArrayBuildStateArr *state;
     862             : 
     863       60222 :     if (arg1_typeid == InvalidOid)
     864           0 :         ereport(ERROR,
     865             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     866             :                  errmsg("could not determine input data type")));
     867             : 
     868             :     /*
     869             :      * Note: we do not need a run-time check about whether arg1_typeid is a
     870             :      * valid array type, because the parser would have verified that while
     871             :      * resolving the input/result types of this polymorphic aggregate.
     872             :      */
     873             : 
     874       60222 :     if (!AggCheckCallContext(fcinfo, &aggcontext))
     875             :     {
     876             :         /* cannot be called directly because of internal-type argument */
     877           0 :         elog(ERROR, "array_agg_array_transfn called in non-aggregate context");
     878             :     }
     879             : 
     880             : 
     881       60222 :     if (PG_ARGISNULL(0))
     882         224 :         state = initArrayResultArr(arg1_typeid, InvalidOid, aggcontext, false);
     883             :     else
     884       59998 :         state = (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
     885             : 
     886       60222 :     state = accumArrayResultArr(state,
     887             :                                 PG_GETARG_DATUM(1),
     888       60222 :                                 PG_ARGISNULL(1),
     889             :                                 arg1_typeid,
     890             :                                 aggcontext);
     891             : 
     892             :     /*
     893             :      * The transition type for array_agg() is declared to be "internal", which
     894             :      * is a pass-by-value type the same size as a pointer.  So we can safely
     895             :      * pass the ArrayBuildStateArr pointer through nodeAgg.c's machinations.
     896             :      */
     897       60204 :     PG_RETURN_POINTER(state);
     898             : }
     899             : 
     900             : Datum
     901          80 : array_agg_array_combine(PG_FUNCTION_ARGS)
     902             : {
     903             :     ArrayBuildStateArr *state1;
     904             :     ArrayBuildStateArr *state2;
     905             :     MemoryContext agg_context;
     906             :     MemoryContext old_context;
     907             : 
     908          80 :     if (!AggCheckCallContext(fcinfo, &agg_context))
     909           0 :         elog(ERROR, "aggregate function called in non-aggregate context");
     910             : 
     911          80 :     state1 = PG_ARGISNULL(0) ? NULL : (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
     912          80 :     state2 = PG_ARGISNULL(1) ? NULL : (ArrayBuildStateArr *) PG_GETARG_POINTER(1);
     913             : 
     914          80 :     if (state2 == NULL)
     915             :     {
     916             :         /*
     917             :          * NULL state2 is easy, just return state1, which we know is already
     918             :          * in the agg_context
     919             :          */
     920           0 :         if (state1 == NULL)
     921           0 :             PG_RETURN_NULL();
     922           0 :         PG_RETURN_POINTER(state1);
     923             :     }
     924             : 
     925          80 :     if (state1 == NULL)
     926             :     {
     927             :         /* We must copy state2's data into the agg_context */
     928          60 :         old_context = MemoryContextSwitchTo(agg_context);
     929             : 
     930          60 :         state1 = initArrayResultArr(state2->array_type, InvalidOid,
     931             :                                     agg_context, false);
     932             : 
     933          60 :         state1->abytes = state2->abytes;
     934          60 :         state1->data = (char *) palloc(state1->abytes);
     935             : 
     936          60 :         if (state2->nullbitmap)
     937             :         {
     938          30 :             int         size = (state2->aitems + 7) / 8;
     939             : 
     940          30 :             state1->nullbitmap = (bits8 *) palloc(size);
     941          30 :             memcpy(state1->nullbitmap, state2->nullbitmap, size);
     942             :         }
     943             : 
     944          60 :         memcpy(state1->data, state2->data, state2->nbytes);
     945          60 :         state1->nbytes = state2->nbytes;
     946          60 :         state1->aitems = state2->aitems;
     947          60 :         state1->nitems = state2->nitems;
     948          60 :         state1->ndims = state2->ndims;
     949          60 :         memcpy(state1->dims, state2->dims, sizeof(state2->dims));
     950          60 :         memcpy(state1->lbs, state2->lbs, sizeof(state2->lbs));
     951          60 :         state1->array_type = state2->array_type;
     952          60 :         state1->element_type = state2->element_type;
     953             : 
     954          60 :         MemoryContextSwitchTo(old_context);
     955             : 
     956          60 :         PG_RETURN_POINTER(state1);
     957             :     }
     958             : 
     959             :     /* We only need to combine the two states if state2 has any items */
     960          20 :     else if (state2->nitems > 0)
     961             :     {
     962             :         MemoryContext oldContext;
     963          20 :         int         reqsize = state1->nbytes + state2->nbytes;
     964             :         int         i;
     965             : 
     966             :         /*
     967             :          * Check the states are compatible with each other.  Ensure we use the
     968             :          * same error messages that are listed in accumArrayResultArr so that
     969             :          * the same error is shown as would have been if we'd not used the
     970             :          * combine function for the aggregation.
     971             :          */
     972          20 :         if (state1->ndims != state2->ndims)
     973           0 :             ereport(ERROR,
     974             :                     (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
     975             :                      errmsg("cannot accumulate arrays of different dimensionality")));
     976             : 
     977             :         /* Check dimensions match ignoring the first dimension. */
     978          40 :         for (i = 1; i < state1->ndims; i++)
     979             :         {
     980          20 :             if (state1->dims[i] != state2->dims[i] || state1->lbs[i] != state2->lbs[i])
     981           0 :                 ereport(ERROR,
     982             :                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
     983             :                          errmsg("cannot accumulate arrays of different dimensionality")));
     984             :         }
     985             : 
     986             : 
     987          20 :         oldContext = MemoryContextSwitchTo(state1->mcontext);
     988             : 
     989             :         /*
     990             :          * If there's not enough space in state1 then we'll need to reallocate
     991             :          * more.
     992             :          */
     993          20 :         if (state1->abytes < reqsize)
     994             :         {
     995             :             /* use a power of 2 size rather than allocating just reqsize */
     996          10 :             state1->abytes = pg_nextpower2_32(reqsize);
     997          10 :             state1->data = (char *) repalloc(state1->data, state1->abytes);
     998             :         }
     999             : 
    1000          20 :         if (state2->nullbitmap)
    1001             :         {
    1002          10 :             int         newnitems = state1->nitems + state2->nitems;
    1003             : 
    1004          10 :             if (state1->nullbitmap == NULL)
    1005             :             {
    1006             :                 /*
    1007             :                  * First input with nulls; we must retrospectively handle any
    1008             :                  * previous inputs by marking all their items non-null.
    1009             :                  */
    1010           0 :                 state1->aitems = pg_nextpower2_32(Max(256, newnitems + 1));
    1011           0 :                 state1->nullbitmap = (bits8 *) palloc((state1->aitems + 7) / 8);
    1012           0 :                 array_bitmap_copy(state1->nullbitmap, 0,
    1013             :                                   NULL, 0,
    1014             :                                   state1->nitems);
    1015             :             }
    1016          10 :             else if (newnitems > state1->aitems)
    1017             :             {
    1018           4 :                 int         newaitems = state1->aitems + state2->aitems;
    1019             : 
    1020           4 :                 state1->aitems = pg_nextpower2_32(newaitems);
    1021           4 :                 state1->nullbitmap = (bits8 *)
    1022           4 :                     repalloc(state1->nullbitmap, (state1->aitems + 7) / 8);
    1023             :             }
    1024          10 :             array_bitmap_copy(state1->nullbitmap, state1->nitems,
    1025          10 :                               state2->nullbitmap, 0,
    1026             :                               state2->nitems);
    1027             :         }
    1028             : 
    1029          20 :         memcpy(state1->data + state1->nbytes, state2->data, state2->nbytes);
    1030          20 :         state1->nbytes += state2->nbytes;
    1031          20 :         state1->nitems += state2->nitems;
    1032             : 
    1033          20 :         state1->dims[0] += state2->dims[0];
    1034             :         /* remaining dims already match, per test above */
    1035             : 
    1036             :         Assert(state1->array_type == state2->array_type);
    1037             :         Assert(state1->element_type == state2->element_type);
    1038             : 
    1039          20 :         MemoryContextSwitchTo(oldContext);
    1040             :     }
    1041             : 
    1042          20 :     PG_RETURN_POINTER(state1);
    1043             : }
    1044             : 
    1045             : /*
    1046             :  * array_agg_array_serialize
    1047             :  *      Serialize ArrayBuildStateArr into bytea.
    1048             :  */
    1049             : Datum
    1050          80 : array_agg_array_serialize(PG_FUNCTION_ARGS)
    1051             : {
    1052             :     ArrayBuildStateArr *state;
    1053             :     StringInfoData buf;
    1054             :     bytea      *result;
    1055             : 
    1056             :     /* cannot be called directly because of internal-type argument */
    1057             :     Assert(AggCheckCallContext(fcinfo, NULL));
    1058             : 
    1059          80 :     state = (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
    1060             : 
    1061          80 :     pq_begintypsend(&buf);
    1062             : 
    1063             :     /*
    1064             :      * element_type. Putting this first is more convenient in deserialization
    1065             :      * so that we can init the new state sooner.
    1066             :      */
    1067          80 :     pq_sendint32(&buf, state->element_type);
    1068             : 
    1069             :     /* array_type */
    1070          80 :     pq_sendint32(&buf, state->array_type);
    1071             : 
    1072             :     /* nbytes */
    1073          80 :     pq_sendint32(&buf, state->nbytes);
    1074             : 
    1075             :     /* data */
    1076          80 :     pq_sendbytes(&buf, state->data, state->nbytes);
    1077             : 
    1078             :     /* abytes */
    1079          80 :     pq_sendint32(&buf, state->abytes);
    1080             : 
    1081             :     /* aitems */
    1082          80 :     pq_sendint32(&buf, state->aitems);
    1083             : 
    1084             :     /* nullbitmap */
    1085          80 :     if (state->nullbitmap)
    1086             :     {
    1087             :         Assert(state->aitems > 0);
    1088          40 :         pq_sendbytes(&buf, state->nullbitmap, (state->aitems + 7) / 8);
    1089             :     }
    1090             : 
    1091             :     /* nitems */
    1092          80 :     pq_sendint32(&buf, state->nitems);
    1093             : 
    1094             :     /* ndims */
    1095          80 :     pq_sendint32(&buf, state->ndims);
    1096             : 
    1097             :     /* dims: XXX should we just send ndims elements? */
    1098          80 :     pq_sendbytes(&buf, state->dims, sizeof(state->dims));
    1099             : 
    1100             :     /* lbs */
    1101          80 :     pq_sendbytes(&buf, state->lbs, sizeof(state->lbs));
    1102             : 
    1103          80 :     result = pq_endtypsend(&buf);
    1104             : 
    1105          80 :     PG_RETURN_BYTEA_P(result);
    1106             : }
    1107             : 
    1108             : Datum
    1109          80 : array_agg_array_deserialize(PG_FUNCTION_ARGS)
    1110             : {
    1111             :     bytea      *sstate;
    1112             :     ArrayBuildStateArr *result;
    1113             :     StringInfoData buf;
    1114             :     Oid         element_type;
    1115             :     Oid         array_type;
    1116             :     int         nbytes;
    1117             :     const char *temp;
    1118             : 
    1119             :     /* cannot be called directly because of internal-type argument */
    1120             :     Assert(AggCheckCallContext(fcinfo, NULL));
    1121             : 
    1122          80 :     sstate = PG_GETARG_BYTEA_PP(0);
    1123             : 
    1124             :     /*
    1125             :      * Initialize a StringInfo so that we can "receive" it using the standard
    1126             :      * recv-function infrastructure.
    1127             :      */
    1128          80 :     initReadOnlyStringInfo(&buf, VARDATA_ANY(sstate),
    1129          80 :                            VARSIZE_ANY_EXHDR(sstate));
    1130             : 
    1131             :     /* element_type */
    1132          80 :     element_type = pq_getmsgint(&buf, 4);
    1133             : 
    1134             :     /* array_type */
    1135          80 :     array_type = pq_getmsgint(&buf, 4);
    1136             : 
    1137             :     /* nbytes */
    1138          80 :     nbytes = pq_getmsgint(&buf, 4);
    1139             : 
    1140          80 :     result = initArrayResultArr(array_type, element_type,
    1141             :                                 CurrentMemoryContext, false);
    1142             : 
    1143          80 :     result->abytes = 1024;
    1144         110 :     while (result->abytes < nbytes)
    1145          30 :         result->abytes *= 2;
    1146             : 
    1147          80 :     result->data = (char *) palloc(result->abytes);
    1148             : 
    1149             :     /* data */
    1150          80 :     temp = pq_getmsgbytes(&buf, nbytes);
    1151          80 :     memcpy(result->data, temp, nbytes);
    1152          80 :     result->nbytes = nbytes;
    1153             : 
    1154             :     /* abytes */
    1155          80 :     result->abytes = pq_getmsgint(&buf, 4);
    1156             : 
    1157             :     /* aitems: might be 0 */
    1158          80 :     result->aitems = pq_getmsgint(&buf, 4);
    1159             : 
    1160             :     /* nullbitmap */
    1161          80 :     if (result->aitems > 0)
    1162             :     {
    1163          40 :         int         size = (result->aitems + 7) / 8;
    1164             : 
    1165          40 :         result->nullbitmap = (bits8 *) palloc(size);
    1166          40 :         temp = pq_getmsgbytes(&buf, size);
    1167          40 :         memcpy(result->nullbitmap, temp, size);
    1168             :     }
    1169             :     else
    1170          40 :         result->nullbitmap = NULL;
    1171             : 
    1172             :     /* nitems */
    1173          80 :     result->nitems = pq_getmsgint(&buf, 4);
    1174             : 
    1175             :     /* ndims */
    1176          80 :     result->ndims = pq_getmsgint(&buf, 4);
    1177             : 
    1178             :     /* dims */
    1179          80 :     temp = pq_getmsgbytes(&buf, sizeof(result->dims));
    1180          80 :     memcpy(result->dims, temp, sizeof(result->dims));
    1181             : 
    1182             :     /* lbs */
    1183          80 :     temp = pq_getmsgbytes(&buf, sizeof(result->lbs));
    1184          80 :     memcpy(result->lbs, temp, sizeof(result->lbs));
    1185             : 
    1186          80 :     pq_getmsgend(&buf);
    1187             : 
    1188          80 :     PG_RETURN_POINTER(result);
    1189             : }
    1190             : 
    1191             : Datum
    1192         186 : array_agg_array_finalfn(PG_FUNCTION_ARGS)
    1193             : {
    1194             :     Datum       result;
    1195             :     ArrayBuildStateArr *state;
    1196             : 
    1197             :     /* cannot be called directly because of internal-type argument */
    1198             :     Assert(AggCheckCallContext(fcinfo, NULL));
    1199             : 
    1200         186 :     state = PG_ARGISNULL(0) ? NULL : (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
    1201             : 
    1202         186 :     if (state == NULL)
    1203           0 :         PG_RETURN_NULL();       /* returns null iff no input values */
    1204             : 
    1205             :     /*
    1206             :      * Make the result.  We cannot release the ArrayBuildStateArr because
    1207             :      * sometimes aggregate final functions are re-executed.  Rather, it is
    1208             :      * nodeAgg.c's responsibility to reset the aggcontext when it's safe to do
    1209             :      * so.
    1210             :      */
    1211         186 :     result = makeArrayResultArr(state, CurrentMemoryContext, false);
    1212             : 
    1213         186 :     PG_RETURN_DATUM(result);
    1214             : }
    1215             : 
    1216             : /*-----------------------------------------------------------------------------
    1217             :  * array_position, array_position_start :
    1218             :  *          return the offset of a value in an array.
    1219             :  *
    1220             :  * IS NOT DISTINCT FROM semantics are used for comparisons.  Return NULL when
    1221             :  * the value is not found.
    1222             :  *-----------------------------------------------------------------------------
    1223             :  */
    1224             : Datum
    1225         216 : array_position(PG_FUNCTION_ARGS)
    1226             : {
    1227         216 :     return array_position_common(fcinfo);
    1228             : }
    1229             : 
    1230             : Datum
    1231          18 : array_position_start(PG_FUNCTION_ARGS)
    1232             : {
    1233          18 :     return array_position_common(fcinfo);
    1234             : }
    1235             : 
    1236             : /*
    1237             :  * array_position_common
    1238             :  *      Common code for array_position and array_position_start
    1239             :  *
    1240             :  * These are separate wrappers for the sake of opr_sanity regression test.
    1241             :  * They are not strict so we have to test for null inputs explicitly.
    1242             :  */
    1243             : static Datum
    1244         234 : array_position_common(FunctionCallInfo fcinfo)
    1245             : {
    1246             :     ArrayType  *array;
    1247         234 :     Oid         collation = PG_GET_COLLATION();
    1248             :     Oid         element_type;
    1249             :     Datum       searched_element,
    1250             :                 value;
    1251             :     bool        isnull;
    1252             :     int         position,
    1253             :                 position_min;
    1254         234 :     bool        found = false;
    1255             :     TypeCacheEntry *typentry;
    1256             :     ArrayMetaState *my_extra;
    1257             :     bool        null_search;
    1258             :     ArrayIterator array_iterator;
    1259             : 
    1260         234 :     if (PG_ARGISNULL(0))
    1261           0 :         PG_RETURN_NULL();
    1262             : 
    1263         234 :     array = PG_GETARG_ARRAYTYPE_P(0);
    1264             : 
    1265             :     /*
    1266             :      * We refuse to search for elements in multi-dimensional arrays, since we
    1267             :      * have no good way to report the element's location in the array.
    1268             :      */
    1269         234 :     if (ARR_NDIM(array) > 1)
    1270           6 :         ereport(ERROR,
    1271             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1272             :                  errmsg("searching for elements in multidimensional arrays is not supported")));
    1273             : 
    1274             :     /* Searching in an empty array is well-defined, though: it always fails */
    1275         228 :     if (ARR_NDIM(array) < 1)
    1276           0 :         PG_RETURN_NULL();
    1277             : 
    1278         228 :     if (PG_ARGISNULL(1))
    1279             :     {
    1280             :         /* fast return when the array doesn't have nulls */
    1281          12 :         if (!array_contains_nulls(array))
    1282           6 :             PG_RETURN_NULL();
    1283           6 :         searched_element = (Datum) 0;
    1284           6 :         null_search = true;
    1285             :     }
    1286             :     else
    1287             :     {
    1288         216 :         searched_element = PG_GETARG_DATUM(1);
    1289         216 :         null_search = false;
    1290             :     }
    1291             : 
    1292         222 :     element_type = ARR_ELEMTYPE(array);
    1293         222 :     position = (ARR_LBOUND(array))[0] - 1;
    1294             : 
    1295             :     /* figure out where to start */
    1296         222 :     if (PG_NARGS() == 3)
    1297             :     {
    1298          18 :         if (PG_ARGISNULL(2))
    1299           0 :             ereport(ERROR,
    1300             :                     (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
    1301             :                      errmsg("initial position must not be null")));
    1302             : 
    1303          18 :         position_min = PG_GETARG_INT32(2);
    1304             :     }
    1305             :     else
    1306         204 :         position_min = (ARR_LBOUND(array))[0];
    1307             : 
    1308             :     /*
    1309             :      * We arrange to look up type info for array_create_iterator only once per
    1310             :      * series of calls, assuming the element type doesn't change underneath
    1311             :      * us.
    1312             :      */
    1313         222 :     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
    1314         222 :     if (my_extra == NULL)
    1315             :     {
    1316          84 :         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
    1317             :                                                       sizeof(ArrayMetaState));
    1318          84 :         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
    1319          84 :         my_extra->element_type = ~element_type;
    1320             :     }
    1321             : 
    1322         222 :     if (my_extra->element_type != element_type)
    1323             :     {
    1324          84 :         get_typlenbyvalalign(element_type,
    1325             :                              &my_extra->typlen,
    1326             :                              &my_extra->typbyval,
    1327             :                              &my_extra->typalign);
    1328             : 
    1329          84 :         typentry = lookup_type_cache(element_type, TYPECACHE_EQ_OPR_FINFO);
    1330             : 
    1331          84 :         if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
    1332           0 :             ereport(ERROR,
    1333             :                     (errcode(ERRCODE_UNDEFINED_FUNCTION),
    1334             :                      errmsg("could not identify an equality operator for type %s",
    1335             :                             format_type_be(element_type))));
    1336             : 
    1337          84 :         my_extra->element_type = element_type;
    1338          84 :         fmgr_info_cxt(typentry->eq_opr_finfo.fn_oid, &my_extra->proc,
    1339          84 :                       fcinfo->flinfo->fn_mcxt);
    1340             :     }
    1341             : 
    1342             :     /* Examine each array element until we find a match. */
    1343         222 :     array_iterator = array_create_iterator(array, 0, my_extra);
    1344         618 :     while (array_iterate(array_iterator, &value, &isnull))
    1345             :     {
    1346         576 :         position++;
    1347             : 
    1348             :         /* skip initial elements if caller requested so */
    1349         576 :         if (position < position_min)
    1350          78 :             continue;
    1351             : 
    1352             :         /*
    1353             :          * Can't look at the array element's value if it's null; but if we
    1354             :          * search for null, we have a hit and are done.
    1355             :          */
    1356         498 :         if (isnull || null_search)
    1357             :         {
    1358          42 :             if (isnull && null_search)
    1359             :             {
    1360           6 :                 found = true;
    1361           6 :                 break;
    1362             :             }
    1363             :             else
    1364          36 :                 continue;
    1365             :         }
    1366             : 
    1367             :         /* not nulls, so run the operator */
    1368         456 :         if (DatumGetBool(FunctionCall2Coll(&my_extra->proc, collation,
    1369             :                                            searched_element, value)))
    1370             :         {
    1371         174 :             found = true;
    1372         174 :             break;
    1373             :         }
    1374             :     }
    1375             : 
    1376         222 :     array_free_iterator(array_iterator);
    1377             : 
    1378             :     /* Avoid leaking memory when handed toasted input */
    1379         222 :     PG_FREE_IF_COPY(array, 0);
    1380             : 
    1381         222 :     if (!found)
    1382          42 :         PG_RETURN_NULL();
    1383             : 
    1384         180 :     PG_RETURN_INT32(position);
    1385             : }
    1386             : 
    1387             : /*-----------------------------------------------------------------------------
    1388             :  * array_positions :
    1389             :  *          return an array of positions of a value in an array.
    1390             :  *
    1391             :  * IS NOT DISTINCT FROM semantics are used for comparisons.  Returns NULL when
    1392             :  * the input array is NULL.  When the value is not found in the array, returns
    1393             :  * an empty array.
    1394             :  *
    1395             :  * This is not strict so we have to test for null inputs explicitly.
    1396             :  *-----------------------------------------------------------------------------
    1397             :  */
    1398             : Datum
    1399          60 : array_positions(PG_FUNCTION_ARGS)
    1400             : {
    1401             :     ArrayType  *array;
    1402          60 :     Oid         collation = PG_GET_COLLATION();
    1403             :     Oid         element_type;
    1404             :     Datum       searched_element,
    1405             :                 value;
    1406             :     bool        isnull;
    1407             :     int         position;
    1408             :     TypeCacheEntry *typentry;
    1409             :     ArrayMetaState *my_extra;
    1410             :     bool        null_search;
    1411             :     ArrayIterator array_iterator;
    1412          60 :     ArrayBuildState *astate = NULL;
    1413             : 
    1414          60 :     if (PG_ARGISNULL(0))
    1415          12 :         PG_RETURN_NULL();
    1416             : 
    1417          48 :     array = PG_GETARG_ARRAYTYPE_P(0);
    1418             : 
    1419             :     /*
    1420             :      * We refuse to search for elements in multi-dimensional arrays, since we
    1421             :      * have no good way to report the element's location in the array.
    1422             :      */
    1423          48 :     if (ARR_NDIM(array) > 1)
    1424           6 :         ereport(ERROR,
    1425             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1426             :                  errmsg("searching for elements in multidimensional arrays is not supported")));
    1427             : 
    1428          42 :     astate = initArrayResult(INT4OID, CurrentMemoryContext, false);
    1429             : 
    1430             :     /* Searching in an empty array is well-defined, though: it always fails */
    1431          42 :     if (ARR_NDIM(array) < 1)
    1432           0 :         PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
    1433             : 
    1434          42 :     if (PG_ARGISNULL(1))
    1435             :     {
    1436             :         /* fast return when the array doesn't have nulls */
    1437          12 :         if (!array_contains_nulls(array))
    1438           6 :             PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
    1439           6 :         searched_element = (Datum) 0;
    1440           6 :         null_search = true;
    1441             :     }
    1442             :     else
    1443             :     {
    1444          30 :         searched_element = PG_GETARG_DATUM(1);
    1445          30 :         null_search = false;
    1446             :     }
    1447             : 
    1448          36 :     element_type = ARR_ELEMTYPE(array);
    1449          36 :     position = (ARR_LBOUND(array))[0] - 1;
    1450             : 
    1451             :     /*
    1452             :      * We arrange to look up type info for array_create_iterator only once per
    1453             :      * series of calls, assuming the element type doesn't change underneath
    1454             :      * us.
    1455             :      */
    1456          36 :     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
    1457          36 :     if (my_extra == NULL)
    1458             :     {
    1459          30 :         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
    1460             :                                                       sizeof(ArrayMetaState));
    1461          30 :         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
    1462          30 :         my_extra->element_type = ~element_type;
    1463             :     }
    1464             : 
    1465          36 :     if (my_extra->element_type != element_type)
    1466             :     {
    1467          30 :         get_typlenbyvalalign(element_type,
    1468             :                              &my_extra->typlen,
    1469             :                              &my_extra->typbyval,
    1470             :                              &my_extra->typalign);
    1471             : 
    1472          30 :         typentry = lookup_type_cache(element_type, TYPECACHE_EQ_OPR_FINFO);
    1473             : 
    1474          30 :         if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
    1475           0 :             ereport(ERROR,
    1476             :                     (errcode(ERRCODE_UNDEFINED_FUNCTION),
    1477             :                      errmsg("could not identify an equality operator for type %s",
    1478             :                             format_type_be(element_type))));
    1479             : 
    1480          30 :         my_extra->element_type = element_type;
    1481          30 :         fmgr_info_cxt(typentry->eq_opr_finfo.fn_oid, &my_extra->proc,
    1482          30 :                       fcinfo->flinfo->fn_mcxt);
    1483             :     }
    1484             : 
    1485             :     /*
    1486             :      * Accumulate each array position iff the element matches the given
    1487             :      * element.
    1488             :      */
    1489          36 :     array_iterator = array_create_iterator(array, 0, my_extra);
    1490         816 :     while (array_iterate(array_iterator, &value, &isnull))
    1491             :     {
    1492         780 :         position += 1;
    1493             : 
    1494             :         /*
    1495             :          * Can't look at the array element's value if it's null; but if we
    1496             :          * search for null, we have a hit.
    1497             :          */
    1498         780 :         if (isnull || null_search)
    1499             :         {
    1500          72 :             if (isnull && null_search)
    1501             :                 astate =
    1502          12 :                     accumArrayResult(astate, Int32GetDatum(position), false,
    1503             :                                      INT4OID, CurrentMemoryContext);
    1504             : 
    1505          72 :             continue;
    1506             :         }
    1507             : 
    1508             :         /* not nulls, so run the operator */
    1509         708 :         if (DatumGetBool(FunctionCall2Coll(&my_extra->proc, collation,
    1510             :                                            searched_element, value)))
    1511             :             astate =
    1512          90 :                 accumArrayResult(astate, Int32GetDatum(position), false,
    1513             :                                  INT4OID, CurrentMemoryContext);
    1514             :     }
    1515             : 
    1516          36 :     array_free_iterator(array_iterator);
    1517             : 
    1518             :     /* Avoid leaking memory when handed toasted input */
    1519          36 :     PG_FREE_IF_COPY(array, 0);
    1520             : 
    1521          36 :     PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
    1522             : }
    1523             : 
    1524             : /*
    1525             :  * array_shuffle_n
    1526             :  *      Return a copy of array with n randomly chosen items.
    1527             :  *
    1528             :  * The number of items must not exceed the size of the first dimension of the
    1529             :  * array.  We preserve the first dimension's lower bound if keep_lb,
    1530             :  * else it's set to 1.  Lower-order dimensions are preserved in any case.
    1531             :  *
    1532             :  * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
    1533             :  * from the system catalogs, given only the elmtyp. However, the caller is
    1534             :  * in a better position to cache this info across multiple calls.
    1535             :  */
    1536             : static ArrayType *
    1537          48 : array_shuffle_n(ArrayType *array, int n, bool keep_lb,
    1538             :                 Oid elmtyp, TypeCacheEntry *typentry)
    1539             : {
    1540             :     ArrayType  *result;
    1541             :     int         ndim,
    1542             :                *dims,
    1543             :                *lbs,
    1544             :                 nelm,
    1545             :                 nitem,
    1546             :                 rdims[MAXDIM],
    1547             :                 rlbs[MAXDIM];
    1548             :     int16       elmlen;
    1549             :     bool        elmbyval;
    1550             :     char        elmalign;
    1551             :     Datum      *elms,
    1552             :                *ielms;
    1553             :     bool       *nuls,
    1554             :                *inuls;
    1555             : 
    1556          48 :     ndim = ARR_NDIM(array);
    1557          48 :     dims = ARR_DIMS(array);
    1558          48 :     lbs = ARR_LBOUND(array);
    1559             : 
    1560          48 :     elmlen = typentry->typlen;
    1561          48 :     elmbyval = typentry->typbyval;
    1562          48 :     elmalign = typentry->typalign;
    1563             : 
    1564             :     /* If the target array is empty, exit fast */
    1565          48 :     if (ndim < 1 || dims[0] < 1 || n < 1)
    1566           0 :         return construct_empty_array(elmtyp);
    1567             : 
    1568          48 :     deconstruct_array(array, elmtyp, elmlen, elmbyval, elmalign,
    1569             :                       &elms, &nuls, &nelm);
    1570             : 
    1571          48 :     nitem = dims[0];            /* total number of items */
    1572          48 :     nelm /= nitem;              /* number of elements per item */
    1573             : 
    1574             :     Assert(n <= nitem);          /* else it's caller error */
    1575             : 
    1576             :     /*
    1577             :      * Shuffle array using Fisher-Yates algorithm.  Scan the array and swap
    1578             :      * current item (nelm datums starting at ielms) with a randomly chosen
    1579             :      * later item (nelm datums starting at jelms) in each iteration.  We can
    1580             :      * stop once we've done n iterations; then first n items are the result.
    1581             :      */
    1582          48 :     ielms = elms;
    1583          48 :     inuls = nuls;
    1584         228 :     for (int i = 0; i < n; i++)
    1585             :     {
    1586         180 :         int         j = (int) pg_prng_uint64_range(&pg_global_prng_state, i, nitem - 1) * nelm;
    1587         180 :         Datum      *jelms = elms + j;
    1588         180 :         bool       *jnuls = nuls + j;
    1589             : 
    1590             :         /* Swap i'th and j'th items; advance ielms/inuls to next item */
    1591         492 :         for (int k = 0; k < nelm; k++)
    1592             :         {
    1593         312 :             Datum       elm = *ielms;
    1594         312 :             bool        nul = *inuls;
    1595             : 
    1596         312 :             *ielms++ = *jelms;
    1597         312 :             *inuls++ = *jnuls;
    1598         312 :             *jelms++ = elm;
    1599         312 :             *jnuls++ = nul;
    1600             :         }
    1601             :     }
    1602             : 
    1603             :     /* Set up dimensions of the result */
    1604          48 :     memcpy(rdims, dims, ndim * sizeof(int));
    1605          48 :     memcpy(rlbs, lbs, ndim * sizeof(int));
    1606          48 :     rdims[0] = n;
    1607          48 :     if (!keep_lb)
    1608          24 :         rlbs[0] = 1;
    1609             : 
    1610          48 :     result = construct_md_array(elms, nuls, ndim, rdims, rlbs,
    1611             :                                 elmtyp, elmlen, elmbyval, elmalign);
    1612             : 
    1613          48 :     pfree(elms);
    1614          48 :     pfree(nuls);
    1615             : 
    1616          48 :     return result;
    1617             : }
    1618             : 
    1619             : /*
    1620             :  * array_shuffle
    1621             :  *
    1622             :  * Returns an array with the same dimensions as the input array, with its
    1623             :  * first-dimension elements in random order.
    1624             :  */
    1625             : Datum
    1626          24 : array_shuffle(PG_FUNCTION_ARGS)
    1627             : {
    1628          24 :     ArrayType  *array = PG_GETARG_ARRAYTYPE_P(0);
    1629             :     ArrayType  *result;
    1630             :     Oid         elmtyp;
    1631             :     TypeCacheEntry *typentry;
    1632             : 
    1633             :     /*
    1634             :      * There is no point in shuffling empty arrays or arrays with less than
    1635             :      * two items.
    1636             :      */
    1637          24 :     if (ARR_NDIM(array) < 1 || ARR_DIMS(array)[0] < 2)
    1638           0 :         PG_RETURN_ARRAYTYPE_P(array);
    1639             : 
    1640          24 :     elmtyp = ARR_ELEMTYPE(array);
    1641          24 :     typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
    1642          24 :     if (typentry == NULL || typentry->type_id != elmtyp)
    1643             :     {
    1644          24 :         typentry = lookup_type_cache(elmtyp, 0);
    1645          24 :         fcinfo->flinfo->fn_extra = (void *) typentry;
    1646             :     }
    1647             : 
    1648          24 :     result = array_shuffle_n(array, ARR_DIMS(array)[0], true, elmtyp, typentry);
    1649             : 
    1650          24 :     PG_RETURN_ARRAYTYPE_P(result);
    1651             : }
    1652             : 
    1653             : /*
    1654             :  * array_sample
    1655             :  *
    1656             :  * Returns an array of n randomly chosen first-dimension elements
    1657             :  * from the input array.
    1658             :  */
    1659             : Datum
    1660          36 : array_sample(PG_FUNCTION_ARGS)
    1661             : {
    1662          36 :     ArrayType  *array = PG_GETARG_ARRAYTYPE_P(0);
    1663          36 :     int         n = PG_GETARG_INT32(1);
    1664             :     ArrayType  *result;
    1665             :     Oid         elmtyp;
    1666             :     TypeCacheEntry *typentry;
    1667             :     int         nitem;
    1668             : 
    1669          36 :     nitem = (ARR_NDIM(array) < 1) ? 0 : ARR_DIMS(array)[0];
    1670             : 
    1671          36 :     if (n < 0 || n > nitem)
    1672          12 :         ereport(ERROR,
    1673             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1674             :                  errmsg("sample size must be between 0 and %d", nitem)));
    1675             : 
    1676          24 :     elmtyp = ARR_ELEMTYPE(array);
    1677          24 :     typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
    1678          24 :     if (typentry == NULL || typentry->type_id != elmtyp)
    1679             :     {
    1680          24 :         typentry = lookup_type_cache(elmtyp, 0);
    1681          24 :         fcinfo->flinfo->fn_extra = (void *) typentry;
    1682             :     }
    1683             : 
    1684          24 :     result = array_shuffle_n(array, n, false, elmtyp, typentry);
    1685             : 
    1686          24 :     PG_RETURN_ARRAYTYPE_P(result);
    1687             : }
    1688             : 
    1689             : 
    1690             : /*
    1691             :  * array_reverse_n
    1692             :  *      Return a copy of array with reversed items.
    1693             :  *
    1694             :  * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
    1695             :  * from the system catalogs, given only the elmtyp. However, the caller is
    1696             :  * in a better position to cache this info across multiple calls.
    1697             :  */
    1698             : static ArrayType *
    1699          18 : array_reverse_n(ArrayType *array, Oid elmtyp, TypeCacheEntry *typentry)
    1700             : {
    1701             :     ArrayType  *result;
    1702             :     int         ndim,
    1703             :                *dims,
    1704             :                *lbs,
    1705             :                 nelm,
    1706             :                 nitem,
    1707             :                 rdims[MAXDIM],
    1708             :                 rlbs[MAXDIM];
    1709             :     int16       elmlen;
    1710             :     bool        elmbyval;
    1711             :     char        elmalign;
    1712             :     Datum      *elms,
    1713             :                *ielms;
    1714             :     bool       *nuls,
    1715             :                *inuls;
    1716             : 
    1717          18 :     ndim = ARR_NDIM(array);
    1718          18 :     dims = ARR_DIMS(array);
    1719          18 :     lbs = ARR_LBOUND(array);
    1720             : 
    1721          18 :     elmlen = typentry->typlen;
    1722          18 :     elmbyval = typentry->typbyval;
    1723          18 :     elmalign = typentry->typalign;
    1724             : 
    1725          18 :     deconstruct_array(array, elmtyp, elmlen, elmbyval, elmalign,
    1726             :                       &elms, &nuls, &nelm);
    1727             : 
    1728          18 :     nitem = dims[0];            /* total number of items */
    1729          18 :     nelm /= nitem;              /* number of elements per item */
    1730             : 
    1731             :     /* Reverse the array */
    1732          18 :     ielms = elms;
    1733          18 :     inuls = nuls;
    1734          54 :     for (int i = 0; i < nitem / 2; i++)
    1735             :     {
    1736          36 :         int         j = (nitem - i - 1) * nelm;
    1737          36 :         Datum      *jelms = elms + j;
    1738          36 :         bool       *jnuls = nuls + j;
    1739             : 
    1740             :         /* Swap i'th and j'th items; advance ielms/inuls to next item */
    1741          84 :         for (int k = 0; k < nelm; k++)
    1742             :         {
    1743          48 :             Datum       elm = *ielms;
    1744          48 :             bool        nul = *inuls;
    1745             : 
    1746          48 :             *ielms++ = *jelms;
    1747          48 :             *inuls++ = *jnuls;
    1748          48 :             *jelms++ = elm;
    1749          48 :             *jnuls++ = nul;
    1750             :         }
    1751             :     }
    1752             : 
    1753             :     /* Set up dimensions of the result */
    1754          18 :     memcpy(rdims, dims, ndim * sizeof(int));
    1755          18 :     memcpy(rlbs, lbs, ndim * sizeof(int));
    1756          18 :     rdims[0] = nitem;
    1757             : 
    1758          18 :     result = construct_md_array(elms, nuls, ndim, rdims, rlbs,
    1759             :                                 elmtyp, elmlen, elmbyval, elmalign);
    1760             : 
    1761          18 :     pfree(elms);
    1762          18 :     pfree(nuls);
    1763             : 
    1764          18 :     return result;
    1765             : }
    1766             : 
    1767             : /*
    1768             :  * array_reverse
    1769             :  *
    1770             :  * Returns an array with the same dimensions as the input array, with its
    1771             :  * first-dimension elements in reverse order.
    1772             :  */
    1773             : Datum
    1774          30 : array_reverse(PG_FUNCTION_ARGS)
    1775             : {
    1776          30 :     ArrayType  *array = PG_GETARG_ARRAYTYPE_P(0);
    1777             :     ArrayType  *result;
    1778             :     Oid         elmtyp;
    1779             :     TypeCacheEntry *typentry;
    1780             : 
    1781             :     /*
    1782             :      * There is no point in reversing empty arrays or arrays with less than
    1783             :      * two items.
    1784             :      */
    1785          30 :     if (ARR_NDIM(array) < 1 || ARR_DIMS(array)[0] < 2)
    1786          12 :         PG_RETURN_ARRAYTYPE_P(array);
    1787             : 
    1788          18 :     elmtyp = ARR_ELEMTYPE(array);
    1789          18 :     typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
    1790          18 :     if (typentry == NULL || typentry->type_id != elmtyp)
    1791             :     {
    1792          18 :         typentry = lookup_type_cache(elmtyp, 0);
    1793          18 :         fcinfo->flinfo->fn_extra = (void *) typentry;
    1794             :     }
    1795             : 
    1796          18 :     result = array_reverse_n(array, elmtyp, typentry);
    1797             : 
    1798          18 :     PG_RETURN_ARRAYTYPE_P(result);
    1799             : }

Generated by: LCOV version 1.14