LCOV - differential code coverage report
Current view: top level - src/backend/utils/adt - array_userfuncs.c (source / functions) Coverage Total Hit UNC LBC UIC UBC GBC GIC GNC CBC EUB ECB
Current: Differential Code Coverage HEAD vs 15 Lines: 85.0 % 581 494 335 7 24 2 16 174 237 67 53 155
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 21 21 12 9 21
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

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

Generated by: LCOV version v1.16-55-g56c0a2a