LCOV - code coverage report
Current view: top level - src/backend/utils/adt - arrayfuncs.c (source / functions) Hit Total Coverage
Test: PostgreSQL 16beta1 Lines: 2220 2372 93.6 %
Date: 2023-05-31 04:12:22 Functions: 89 89 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * arrayfuncs.c
       4             :  *    Support functions for arrays.
       5             :  *
       6             :  * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/utils/adt/arrayfuncs.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : #include "postgres.h"
      16             : 
      17             : #include <ctype.h>
      18             : #include <math.h>
      19             : 
      20             : #include "access/htup_details.h"
      21             : #include "catalog/pg_type.h"
      22             : #include "funcapi.h"
      23             : #include "libpq/pqformat.h"
      24             : #include "nodes/nodeFuncs.h"
      25             : #include "nodes/supportnodes.h"
      26             : #include "optimizer/optimizer.h"
      27             : #include "port/pg_bitutils.h"
      28             : #include "utils/array.h"
      29             : #include "utils/arrayaccess.h"
      30             : #include "utils/builtins.h"
      31             : #include "utils/datum.h"
      32             : #include "utils/fmgroids.h"
      33             : #include "utils/lsyscache.h"
      34             : #include "utils/memutils.h"
      35             : #include "utils/selfuncs.h"
      36             : #include "utils/typcache.h"
      37             : 
      38             : 
      39             : /*
      40             :  * GUC parameter
      41             :  */
      42             : bool        Array_nulls = true;
      43             : 
      44             : /*
      45             :  * Local definitions
      46             :  */
      47             : #define ASSGN    "="
      48             : 
      49             : #define AARR_FREE_IF_COPY(array,n) \
      50             :     do { \
      51             :         if (!VARATT_IS_EXPANDED_HEADER(array)) \
      52             :             PG_FREE_IF_COPY(array, n); \
      53             :     } while (0)
      54             : 
      55             : typedef enum
      56             : {
      57             :     ARRAY_NO_LEVEL,
      58             :     ARRAY_LEVEL_STARTED,
      59             :     ARRAY_ELEM_STARTED,
      60             :     ARRAY_ELEM_COMPLETED,
      61             :     ARRAY_QUOTED_ELEM_STARTED,
      62             :     ARRAY_QUOTED_ELEM_COMPLETED,
      63             :     ARRAY_ELEM_DELIMITED,
      64             :     ARRAY_LEVEL_COMPLETED,
      65             :     ARRAY_LEVEL_DELIMITED
      66             : } ArrayParseState;
      67             : 
      68             : /* Working state for array_iterate() */
      69             : typedef struct ArrayIteratorData
      70             : {
      71             :     /* basic info about the array, set up during array_create_iterator() */
      72             :     ArrayType  *arr;            /* array we're iterating through */
      73             :     bits8      *nullbitmap;     /* its null bitmap, if any */
      74             :     int         nitems;         /* total number of elements in array */
      75             :     int16       typlen;         /* element type's length */
      76             :     bool        typbyval;       /* element type's byval property */
      77             :     char        typalign;       /* element type's align property */
      78             : 
      79             :     /* information about the requested slice size */
      80             :     int         slice_ndim;     /* slice dimension, or 0 if not slicing */
      81             :     int         slice_len;      /* number of elements per slice */
      82             :     int        *slice_dims;     /* slice dims array */
      83             :     int        *slice_lbound;   /* slice lbound array */
      84             :     Datum      *slice_values;   /* workspace of length slice_len */
      85             :     bool       *slice_nulls;    /* workspace of length slice_len */
      86             : 
      87             :     /* current position information, updated on each iteration */
      88             :     char       *data_ptr;       /* our current position in the array */
      89             :     int         current_item;   /* the item # we're at in the array */
      90             : }           ArrayIteratorData;
      91             : 
      92             : static bool array_isspace(char ch);
      93             : static int  ArrayCount(const char *str, int *dim, char typdelim,
      94             :                        Node *escontext);
      95             : static bool ReadArrayStr(char *arrayStr, const char *origStr,
      96             :                          int nitems, int ndim, int *dim,
      97             :                          FmgrInfo *inputproc, Oid typioparam, int32 typmod,
      98             :                          char typdelim,
      99             :                          int typlen, bool typbyval, char typalign,
     100             :                          Datum *values, bool *nulls,
     101             :                          bool *hasnulls, int32 *nbytes, Node *escontext);
     102             : static void ReadArrayBinary(StringInfo buf, int nitems,
     103             :                             FmgrInfo *receiveproc, Oid typioparam, int32 typmod,
     104             :                             int typlen, bool typbyval, char typalign,
     105             :                             Datum *values, bool *nulls,
     106             :                             bool *hasnulls, int32 *nbytes);
     107             : static Datum array_get_element_expanded(Datum arraydatum,
     108             :                                         int nSubscripts, int *indx,
     109             :                                         int arraytyplen,
     110             :                                         int elmlen, bool elmbyval, char elmalign,
     111             :                                         bool *isNull);
     112             : static Datum array_set_element_expanded(Datum arraydatum,
     113             :                                         int nSubscripts, int *indx,
     114             :                                         Datum dataValue, bool isNull,
     115             :                                         int arraytyplen,
     116             :                                         int elmlen, bool elmbyval, char elmalign);
     117             : static bool array_get_isnull(const bits8 *nullbitmap, int offset);
     118             : static void array_set_isnull(bits8 *nullbitmap, int offset, bool isNull);
     119             : static Datum ArrayCast(char *value, bool byval, int len);
     120             : static int  ArrayCastAndSet(Datum src,
     121             :                             int typlen, bool typbyval, char typalign,
     122             :                             char *dest);
     123             : static char *array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
     124             :                         int typlen, bool typbyval, char typalign);
     125             : static int  array_nelems_size(char *ptr, int offset, bits8 *nullbitmap,
     126             :                               int nitems, int typlen, bool typbyval, char typalign);
     127             : static int  array_copy(char *destptr, int nitems,
     128             :                        char *srcptr, int offset, bits8 *nullbitmap,
     129             :                        int typlen, bool typbyval, char typalign);
     130             : static int  array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
     131             :                              int ndim, int *dim, int *lb,
     132             :                              int *st, int *endp,
     133             :                              int typlen, bool typbyval, char typalign);
     134             : static void array_extract_slice(ArrayType *newarray,
     135             :                                 int ndim, int *dim, int *lb,
     136             :                                 char *arraydataptr, bits8 *arraynullsptr,
     137             :                                 int *st, int *endp,
     138             :                                 int typlen, bool typbyval, char typalign);
     139             : static void array_insert_slice(ArrayType *destArray, ArrayType *origArray,
     140             :                                ArrayType *srcArray,
     141             :                                int ndim, int *dim, int *lb,
     142             :                                int *st, int *endp,
     143             :                                int typlen, bool typbyval, char typalign);
     144             : static int  array_cmp(FunctionCallInfo fcinfo);
     145             : static ArrayType *create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
     146             :                                         Oid elmtype, int dataoffset);
     147             : static ArrayType *array_fill_internal(ArrayType *dims, ArrayType *lbs,
     148             :                                       Datum value, bool isnull, Oid elmtype,
     149             :                                       FunctionCallInfo fcinfo);
     150             : static ArrayType *array_replace_internal(ArrayType *array,
     151             :                                          Datum search, bool search_isnull,
     152             :                                          Datum replace, bool replace_isnull,
     153             :                                          bool remove, Oid collation,
     154             :                                          FunctionCallInfo fcinfo);
     155             : static int  width_bucket_array_float8(Datum operand, ArrayType *thresholds);
     156             : static int  width_bucket_array_fixed(Datum operand,
     157             :                                      ArrayType *thresholds,
     158             :                                      Oid collation,
     159             :                                      TypeCacheEntry *typentry);
     160             : static int  width_bucket_array_variable(Datum operand,
     161             :                                         ArrayType *thresholds,
     162             :                                         Oid collation,
     163             :                                         TypeCacheEntry *typentry);
     164             : 
     165             : 
     166             : /*
     167             :  * array_in :
     168             :  *        converts an array from the external format in "string" to
     169             :  *        its internal format.
     170             :  *
     171             :  * return value :
     172             :  *        the internal representation of the input array
     173             :  */
     174             : Datum
     175      365076 : array_in(PG_FUNCTION_ARGS)
     176             : {
     177      365076 :     char       *string = PG_GETARG_CSTRING(0);  /* external form */
     178      365076 :     Oid         element_type = PG_GETARG_OID(1);    /* type of an array
     179             :                                                      * element */
     180      365076 :     int32       typmod = PG_GETARG_INT32(2);    /* typmod for array elements */
     181      365076 :     Node       *escontext = fcinfo->context;
     182             :     int         typlen;
     183             :     bool        typbyval;
     184             :     char        typalign;
     185             :     char        typdelim;
     186             :     Oid         typioparam;
     187             :     char       *string_save,
     188             :                *p;
     189             :     int         i,
     190             :                 nitems;
     191             :     Datum      *dataPtr;
     192             :     bool       *nullsPtr;
     193             :     bool        hasnulls;
     194             :     int32       nbytes;
     195             :     int32       dataoffset;
     196             :     ArrayType  *retval;
     197             :     int         ndim,
     198             :                 dim[MAXDIM],
     199             :                 lBound[MAXDIM];
     200             :     ArrayMetaState *my_extra;
     201             : 
     202             :     /*
     203             :      * We arrange to look up info about element type, including its input
     204             :      * conversion proc, only once per series of calls, assuming the element
     205             :      * type doesn't change underneath us.
     206             :      */
     207      365076 :     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
     208      365076 :     if (my_extra == NULL)
     209             :     {
     210      257436 :         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
     211             :                                                       sizeof(ArrayMetaState));
     212      257436 :         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
     213      257436 :         my_extra->element_type = ~element_type;
     214             :     }
     215             : 
     216      365076 :     if (my_extra->element_type != element_type)
     217             :     {
     218             :         /*
     219             :          * Get info about element type, including its input conversion proc
     220             :          */
     221      257436 :         get_type_io_data(element_type, IOFunc_input,
     222             :                          &my_extra->typlen, &my_extra->typbyval,
     223             :                          &my_extra->typalign, &my_extra->typdelim,
     224             :                          &my_extra->typioparam, &my_extra->typiofunc);
     225      257436 :         fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
     226      257436 :                       fcinfo->flinfo->fn_mcxt);
     227      257436 :         my_extra->element_type = element_type;
     228             :     }
     229      365076 :     typlen = my_extra->typlen;
     230      365076 :     typbyval = my_extra->typbyval;
     231      365076 :     typalign = my_extra->typalign;
     232      365076 :     typdelim = my_extra->typdelim;
     233      365076 :     typioparam = my_extra->typioparam;
     234             : 
     235             :     /* Make a modifiable copy of the input */
     236      365076 :     string_save = pstrdup(string);
     237             : 
     238             :     /*
     239             :      * If the input string starts with dimension info, read and use that.
     240             :      * Otherwise, we require the input to be in curly-brace style, and we
     241             :      * prescan the input to determine dimensions.
     242             :      *
     243             :      * Dimension info takes the form of one or more [n] or [m:n] items. The
     244             :      * outer loop iterates once per dimension item.
     245             :      */
     246      365076 :     p = string_save;
     247      365076 :     ndim = 0;
     248             :     for (;;)
     249         114 :     {
     250             :         char       *q;
     251             :         int         ub;
     252             : 
     253             :         /*
     254             :          * Note: we currently allow whitespace between, but not within,
     255             :          * dimension items.
     256             :          */
     257      365202 :         while (array_isspace(*p))
     258          12 :             p++;
     259      365190 :         if (*p != '[')
     260      365076 :             break;              /* no more dimension items */
     261         114 :         p++;
     262         114 :         if (ndim >= MAXDIM)
     263           0 :             ereturn(escontext, (Datum) 0,
     264             :                     (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     265             :                      errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
     266             :                             ndim + 1, MAXDIM)));
     267             : 
     268         258 :         for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++)
     269             :              /* skip */ ;
     270         114 :         if (q == p)             /* no digits? */
     271           0 :             ereturn(escontext, (Datum) 0,
     272             :                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     273             :                      errmsg("malformed array literal: \"%s\"", string),
     274             :                      errdetail("\"[\" must introduce explicitly-specified array dimensions.")));
     275             : 
     276         114 :         if (*q == ':')
     277             :         {
     278             :             /* [m:n] format */
     279         114 :             *q = '\0';
     280         114 :             lBound[ndim] = atoi(p);
     281         114 :             p = q + 1;
     282         246 :             for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++)
     283             :                  /* skip */ ;
     284         114 :             if (q == p)         /* no digits? */
     285           0 :                 ereturn(escontext, (Datum) 0,
     286             :                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     287             :                          errmsg("malformed array literal: \"%s\"", string),
     288             :                          errdetail("Missing array dimension value.")));
     289             :         }
     290             :         else
     291             :         {
     292             :             /* [n] format */
     293           0 :             lBound[ndim] = 1;
     294             :         }
     295         114 :         if (*q != ']')
     296           0 :             ereturn(escontext, (Datum) 0,
     297             :                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     298             :                      errmsg("malformed array literal: \"%s\"", string),
     299             :                      errdetail("Missing \"%s\" after array dimensions.",
     300             :                                "]")));
     301             : 
     302         114 :         *q = '\0';
     303         114 :         ub = atoi(p);
     304         114 :         p = q + 1;
     305         114 :         if (ub < lBound[ndim])
     306           0 :             ereturn(escontext, (Datum) 0,
     307             :                     (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
     308             :                      errmsg("upper bound cannot be less than lower bound")));
     309             : 
     310         114 :         dim[ndim] = ub - lBound[ndim] + 1;
     311         114 :         ndim++;
     312             :     }
     313             : 
     314      365076 :     if (ndim == 0)
     315             :     {
     316             :         /* No array dimensions, so intuit dimensions from brace structure */
     317      364994 :         if (*p != '{')
     318           2 :             ereturn(escontext, (Datum) 0,
     319             :                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     320             :                      errmsg("malformed array literal: \"%s\"", string),
     321             :                      errdetail("Array value must start with \"{\" or dimension information.")));
     322      364992 :         ndim = ArrayCount(p, dim, typdelim, escontext);
     323      364952 :         if (ndim < 0)
     324           6 :             PG_RETURN_NULL();
     325      723300 :         for (i = 0; i < ndim; i++)
     326      358354 :             lBound[i] = 1;
     327             :     }
     328             :     else
     329             :     {
     330             :         int         ndim_braces,
     331             :                     dim_braces[MAXDIM];
     332             : 
     333             :         /* If array dimensions are given, expect '=' operator */
     334          82 :         if (strncmp(p, ASSGN, strlen(ASSGN)) != 0)
     335           0 :             ereturn(escontext, (Datum) 0,
     336             :                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     337             :                      errmsg("malformed array literal: \"%s\"", string),
     338             :                      errdetail("Missing \"%s\" after array dimensions.",
     339             :                                ASSGN)));
     340          82 :         p += strlen(ASSGN);
     341          82 :         while (array_isspace(*p))
     342           0 :             p++;
     343             : 
     344             :         /*
     345             :          * intuit dimensions from brace structure -- it better match what we
     346             :          * were given
     347             :          */
     348          82 :         if (*p != '{')
     349           0 :             ereturn(escontext, (Datum) 0,
     350             :                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     351             :                      errmsg("malformed array literal: \"%s\"", string),
     352             :                      errdetail("Array contents must start with \"{\".")));
     353          82 :         ndim_braces = ArrayCount(p, dim_braces, typdelim, escontext);
     354          82 :         if (ndim_braces < 0)
     355           0 :             PG_RETURN_NULL();
     356          82 :         if (ndim_braces != ndim)
     357           0 :             ereturn(escontext, (Datum) 0,
     358             :                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     359             :                      errmsg("malformed array literal: \"%s\"", string),
     360             :                      errdetail("Specified array dimensions do not match array contents.")));
     361         196 :         for (i = 0; i < ndim; ++i)
     362             :         {
     363         114 :             if (dim[i] != dim_braces[i])
     364           0 :                 ereturn(escontext, (Datum) 0,
     365             :                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     366             :                          errmsg("malformed array literal: \"%s\"", string),
     367             :                          errdetail("Specified array dimensions do not match array contents.")));
     368             :         }
     369             :     }
     370             : 
     371             : #ifdef ARRAYDEBUG
     372             :     printf("array_in- ndim %d (", ndim);
     373             :     for (i = 0; i < ndim; i++)
     374             :     {
     375             :         printf(" %d", dim[i]);
     376             :     };
     377             :     printf(") for %s\n", string);
     378             : #endif
     379             : 
     380             :     /* This checks for overflow of the array dimensions */
     381      365028 :     nitems = ArrayGetNItemsSafe(ndim, dim, escontext);
     382      365028 :     if (nitems < 0)
     383           0 :         PG_RETURN_NULL();
     384      365028 :     if (!ArrayCheckBoundsSafe(ndim, dim, lBound, escontext))
     385           0 :         PG_RETURN_NULL();
     386             : 
     387             :     /* Empty array? */
     388      365028 :     if (nitems == 0)
     389        6976 :         PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
     390             : 
     391      358052 :     dataPtr = (Datum *) palloc(nitems * sizeof(Datum));
     392      358052 :     nullsPtr = (bool *) palloc(nitems * sizeof(bool));
     393      358052 :     if (!ReadArrayStr(p, string,
     394             :                       nitems, ndim, dim,
     395             :                       &my_extra->proc, typioparam, typmod,
     396             :                       typdelim,
     397             :                       typlen, typbyval, typalign,
     398             :                       dataPtr, nullsPtr,
     399             :                       &hasnulls, &nbytes, escontext))
     400          12 :         PG_RETURN_NULL();
     401      358020 :     if (hasnulls)
     402             :     {
     403         374 :         dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
     404         374 :         nbytes += dataoffset;
     405             :     }
     406             :     else
     407             :     {
     408      357646 :         dataoffset = 0;         /* marker for no null bitmap */
     409      357646 :         nbytes += ARR_OVERHEAD_NONULLS(ndim);
     410             :     }
     411      358020 :     retval = (ArrayType *) palloc0(nbytes);
     412      358020 :     SET_VARSIZE(retval, nbytes);
     413      358020 :     retval->ndim = ndim;
     414      358020 :     retval->dataoffset = dataoffset;
     415             : 
     416             :     /*
     417             :      * This comes from the array's pg_type.typelem (which points to the base
     418             :      * data type's pg_type.oid) and stores system oids in user tables. This
     419             :      * oid must be preserved by binary upgrades.
     420             :      */
     421      358020 :     retval->elemtype = element_type;
     422      358020 :     memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
     423      358020 :     memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
     424             : 
     425      358020 :     CopyArrayEls(retval,
     426             :                  dataPtr, nullsPtr, nitems,
     427             :                  typlen, typbyval, typalign,
     428             :                  true);
     429             : 
     430      358020 :     pfree(dataPtr);
     431      358020 :     pfree(nullsPtr);
     432      358020 :     pfree(string_save);
     433             : 
     434      358020 :     PG_RETURN_ARRAYTYPE_P(retval);
     435             : }
     436             : 
     437             : /*
     438             :  * array_isspace() --- a non-locale-dependent isspace()
     439             :  *
     440             :  * We used to use isspace() for parsing array values, but that has
     441             :  * undesirable results: an array value might be silently interpreted
     442             :  * differently depending on the locale setting.  Now we just hard-wire
     443             :  * the traditional ASCII definition of isspace().
     444             :  */
     445             : static bool
     446    36049368 : array_isspace(char ch)
     447             : {
     448    36049368 :     if (ch == ' ' ||
     449    35969202 :         ch == '\t' ||
     450    35969116 :         ch == '\n' ||
     451    35969108 :         ch == '\r' ||
     452    35969106 :         ch == '\v' ||
     453             :         ch == '\f')
     454       80264 :         return true;
     455    35969104 :     return false;
     456             : }
     457             : 
     458             : /*
     459             :  * ArrayCount
     460             :  *   Determines the dimensions for an array string.
     461             :  *
     462             :  * Returns number of dimensions as function result.  The axis lengths are
     463             :  * returned in dim[], which must be of size MAXDIM.
     464             :  *
     465             :  * If we detect an error, fill *escontext with error details and return -1
     466             :  * (unless escontext isn't provided, in which case errors will be thrown).
     467             :  */
     468             : static int
     469      365074 : ArrayCount(const char *str, int *dim, char typdelim, Node *escontext)
     470             : {
     471      365074 :     int         nest_level = 0,
     472             :                 i;
     473      365074 :     int         ndim = 1,
     474             :                 temp[MAXDIM],
     475             :                 nelems[MAXDIM],
     476             :                 nelems_last[MAXDIM];
     477      365074 :     bool        in_quotes = false;
     478      365074 :     bool        eoArray = false;
     479      365074 :     bool        empty_array = true;
     480             :     const char *ptr;
     481      365074 :     ArrayParseState parse_state = ARRAY_NO_LEVEL;
     482             : 
     483     2555518 :     for (i = 0; i < MAXDIM; ++i)
     484             :     {
     485     2190444 :         temp[i] = dim[i] = nelems_last[i] = 0;
     486     2190444 :         nelems[i] = 1;
     487             :     }
     488             : 
     489      365074 :     ptr = str;
     490     2148042 :     while (!eoArray)
     491             :     {
     492     1783002 :         bool        itemdone = false;
     493             : 
     494    12966150 :         while (!itemdone)
     495             :         {
     496    11183182 :             if (parse_state == ARRAY_ELEM_STARTED ||
     497             :                 parse_state == ARRAY_QUOTED_ELEM_STARTED)
     498     8624920 :                 empty_array = false;
     499             : 
     500    11183182 :             switch (*ptr)
     501             :             {
     502           6 :                 case '\0':
     503             :                     /* Signal a premature end of the string */
     504           6 :                     ereturn(escontext, -1,
     505             :                             (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     506             :                              errmsg("malformed array literal: \"%s\"", str),
     507             :                              errdetail("Unexpected end of input.")));
     508         142 :                 case '\\':
     509             : 
     510             :                     /*
     511             :                      * An escape must be after a level start, after an element
     512             :                      * start, or after an element delimiter. In any case we
     513             :                      * now must be past an element start.
     514             :                      */
     515         142 :                     if (parse_state != ARRAY_LEVEL_STARTED &&
     516         142 :                         parse_state != ARRAY_ELEM_STARTED &&
     517           6 :                         parse_state != ARRAY_QUOTED_ELEM_STARTED &&
     518             :                         parse_state != ARRAY_ELEM_DELIMITED)
     519           6 :                         ereturn(escontext, -1,
     520             :                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     521             :                                  errmsg("malformed array literal: \"%s\"", str),
     522             :                                  errdetail("Unexpected \"%c\" character.",
     523             :                                            '\\')));
     524         136 :                     if (parse_state != ARRAY_QUOTED_ELEM_STARTED)
     525           0 :                         parse_state = ARRAY_ELEM_STARTED;
     526             :                     /* skip the escaped character */
     527         136 :                     if (*(ptr + 1))
     528         136 :                         ptr++;
     529             :                     else
     530           0 :                         ereturn(escontext, -1,
     531             :                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     532             :                                  errmsg("malformed array literal: \"%s\"", str),
     533             :                                  errdetail("Unexpected end of input.")));
     534         136 :                     break;
     535      741152 :                 case '"':
     536             : 
     537             :                     /*
     538             :                      * A quote must be after a level start, after a quoted
     539             :                      * element start, or after an element delimiter. In any
     540             :                      * case we now must be past an element start.
     541             :                      */
     542      741152 :                     if (parse_state != ARRAY_LEVEL_STARTED &&
     543      282904 :                         parse_state != ARRAY_QUOTED_ELEM_STARTED &&
     544             :                         parse_state != ARRAY_ELEM_DELIMITED)
     545           0 :                         ereturn(escontext, -1,
     546             :                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     547             :                                  errmsg("malformed array literal: \"%s\"", str),
     548             :                                  errdetail("Unexpected array element.")));
     549      741152 :                     in_quotes = !in_quotes;
     550      741152 :                     if (in_quotes)
     551      370576 :                         parse_state = ARRAY_QUOTED_ELEM_STARTED;
     552             :                     else
     553      370576 :                         parse_state = ARRAY_QUOTED_ELEM_COMPLETED;
     554      741152 :                     break;
     555      366364 :                 case '{':
     556      366364 :                     if (!in_quotes)
     557             :                     {
     558             :                         /*
     559             :                          * A left brace can occur if no nesting has occurred
     560             :                          * yet, after a level start, or after a level
     561             :                          * delimiter.
     562             :                          */
     563      366306 :                         if (parse_state != ARRAY_NO_LEVEL &&
     564         708 :                             parse_state != ARRAY_LEVEL_STARTED &&
     565             :                             parse_state != ARRAY_LEVEL_DELIMITED)
     566           6 :                             ereturn(escontext, -1,
     567             :                                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     568             :                                      errmsg("malformed array literal: \"%s\"", str),
     569             :                                      errdetail("Unexpected \"%c\" character.",
     570             :                                                '{')));
     571      366300 :                         parse_state = ARRAY_LEVEL_STARTED;
     572      366300 :                         if (nest_level >= MAXDIM)
     573           2 :                             ereturn(escontext, -1,
     574             :                                     (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     575             :                                      errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
     576             :                                             nest_level + 1, MAXDIM)));
     577      366298 :                         temp[nest_level] = 0;
     578      366298 :                         nest_level++;
     579      366298 :                         if (ndim < nest_level)
     580         454 :                             ndim = nest_level;
     581             :                     }
     582      366356 :                     break;
     583      366298 :                 case '}':
     584      366298 :                     if (!in_quotes)
     585             :                     {
     586             :                         /*
     587             :                          * A right brace can occur after an element start, an
     588             :                          * element completion, a quoted element completion, or
     589             :                          * a level completion.
     590             :                          */
     591      366240 :                         if (parse_state != ARRAY_ELEM_STARTED &&
     592       95260 :                             parse_state != ARRAY_ELEM_COMPLETED &&
     593        7478 :                             parse_state != ARRAY_QUOTED_ELEM_COMPLETED &&
     594        6994 :                             parse_state != ARRAY_LEVEL_COMPLETED &&
     595        6988 :                             !(nest_level == 1 && parse_state == ARRAY_LEVEL_STARTED))
     596           6 :                             ereturn(escontext, -1,
     597             :                                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     598             :                                      errmsg("malformed array literal: \"%s\"", str),
     599             :                                      errdetail("Unexpected \"%c\" character.",
     600             :                                                '}')));
     601      366234 :                         parse_state = ARRAY_LEVEL_COMPLETED;
     602      366234 :                         if (nest_level == 0)
     603           0 :                             ereturn(escontext, -1,
     604             :                                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     605             :                                      errmsg("malformed array literal: \"%s\"", str),
     606             :                                      errdetail("Unmatched \"%c\" character.", '}')));
     607      366234 :                         nest_level--;
     608             : 
     609      366234 :                         if (nelems_last[nest_level] != 0 &&
     610         770 :                             nelems[nest_level] != nelems_last[nest_level])
     611           2 :                             ereturn(escontext, -1,
     612             :                                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     613             :                                      errmsg("malformed array literal: \"%s\"", str),
     614             :                                      errdetail("Multidimensional arrays must have "
     615             :                                                "sub-arrays with matching "
     616             :                                                "dimensions.")));
     617      366232 :                         nelems_last[nest_level] = nelems[nest_level];
     618      366232 :                         nelems[nest_level] = 1;
     619      366232 :                         if (nest_level == 0)
     620      365040 :                             eoArray = itemdone = true;
     621             :                         else
     622             :                         {
     623             :                             /*
     624             :                              * We don't set itemdone here; see comments in
     625             :                              * ReadArrayStr
     626             :                              */
     627        1192 :                             temp[nest_level - 1]++;
     628             :                         }
     629             :                     }
     630      366290 :                     break;
     631     9709220 :                 default:
     632     9709220 :                     if (!in_quotes)
     633             :                     {
     634     7370186 :                         if (*ptr == typdelim)
     635             :                         {
     636             :                             /*
     637             :                              * Delimiters can occur after an element start, an
     638             :                              * element completion, a quoted element
     639             :                              * completion, or a level completion.
     640             :                              */
     641     1417928 :                             if (parse_state != ARRAY_ELEM_STARTED &&
     642      283496 :                                 parse_state != ARRAY_ELEM_COMPLETED &&
     643         708 :                                 parse_state != ARRAY_QUOTED_ELEM_COMPLETED &&
     644             :                                 parse_state != ARRAY_LEVEL_COMPLETED)
     645           0 :                                 ereturn(escontext, -1,
     646             :                                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     647             :                                          errmsg("malformed array literal: \"%s\"", str),
     648             :                                          errdetail("Unexpected \"%c\" character.",
     649             :                                                    typdelim)));
     650     1417928 :                             if (parse_state == ARRAY_LEVEL_COMPLETED)
     651         708 :                                 parse_state = ARRAY_LEVEL_DELIMITED;
     652             :                             else
     653     1417220 :                                 parse_state = ARRAY_ELEM_DELIMITED;
     654     1417928 :                             itemdone = true;
     655     1417928 :                             nelems[nest_level - 1]++;
     656             :                         }
     657     5952258 :                         else if (!array_isspace(*ptr))
     658             :                         {
     659             :                             /*
     660             :                              * Other non-space characters must be after a
     661             :                              * level start, after an element start, or after
     662             :                              * an element delimiter. In any case we now must
     663             :                              * be past an element start.
     664             :                              */
     665     5914062 :                             if (parse_state != ARRAY_LEVEL_STARTED &&
     666     1134316 :                                 parse_state != ARRAY_ELEM_STARTED &&
     667             :                                 parse_state != ARRAY_ELEM_DELIMITED)
     668           6 :                                 ereturn(escontext, -1,
     669             :                                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     670             :                                          errmsg("malformed array literal: \"%s\"", str),
     671             :                                          errdetail("Unexpected array element.")));
     672     5914056 :                             parse_state = ARRAY_ELEM_STARTED;
     673             :                         }
     674             :                     }
     675     9709214 :                     break;
     676             :             }
     677    11183148 :             if (!itemdone)
     678     9400180 :                 ptr++;
     679             :         }
     680     1782968 :         temp[ndim - 1]++;
     681     1782968 :         ptr++;
     682             :     }
     683             : 
     684             :     /* only whitespace is allowed after the closing brace */
     685      365040 :     while (*ptr)
     686             :     {
     687          12 :         if (!array_isspace(*ptr++))
     688          12 :             ereturn(escontext, -1,
     689             :                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     690             :                      errmsg("malformed array literal: \"%s\"", str),
     691             :                      errdetail("Junk after closing right brace.")));
     692             :     }
     693             : 
     694             :     /* special case for an empty array */
     695      365028 :     if (empty_array)
     696        6976 :         return 0;
     697             : 
     698      716520 :     for (i = 0; i < ndim; ++i)
     699      358468 :         dim[i] = temp[i];
     700             : 
     701      358052 :     return ndim;
     702             : }
     703             : 
     704             : /*
     705             :  * ReadArrayStr :
     706             :  *   parses the array string pointed to by "arrayStr" and converts the values
     707             :  *   to internal format.  Unspecified elements are initialized to nulls.
     708             :  *   The array dimensions must already have been determined.
     709             :  *
     710             :  * Inputs:
     711             :  *  arrayStr: the string to parse.
     712             :  *            CAUTION: the contents of "arrayStr" will be modified!
     713             :  *  origStr: the unmodified input string, used only in error messages.
     714             :  *  nitems: total number of array elements, as already determined.
     715             :  *  ndim: number of array dimensions
     716             :  *  dim[]: array axis lengths
     717             :  *  inputproc: type-specific input procedure for element datatype.
     718             :  *  typioparam, typmod: auxiliary values to pass to inputproc.
     719             :  *  typdelim: the value delimiter (type-specific).
     720             :  *  typlen, typbyval, typalign: storage parameters of element datatype.
     721             :  *
     722             :  * Outputs:
     723             :  *  values[]: filled with converted data values.
     724             :  *  nulls[]: filled with is-null markers.
     725             :  *  *hasnulls: set true iff there are any null elements.
     726             :  *  *nbytes: set to total size of data area needed (including alignment
     727             :  *      padding but not including array header overhead).
     728             :  *  *escontext: if this points to an ErrorSaveContext, details of
     729             :  *      any error are reported there.
     730             :  *
     731             :  * Result:
     732             :  *  true for success, false for failure (if escontext is provided).
     733             :  *
     734             :  * Note that values[] and nulls[] are allocated by the caller, and must have
     735             :  * nitems elements.
     736             :  */
     737             : static bool
     738      358052 : ReadArrayStr(char *arrayStr,
     739             :              const char *origStr,
     740             :              int nitems,
     741             :              int ndim,
     742             :              int *dim,
     743             :              FmgrInfo *inputproc,
     744             :              Oid typioparam,
     745             :              int32 typmod,
     746             :              char typdelim,
     747             :              int typlen,
     748             :              bool typbyval,
     749             :              char typalign,
     750             :              Datum *values,
     751             :              bool *nulls,
     752             :              bool *hasnulls,
     753             :              int32 *nbytes,
     754             :              Node *escontext)
     755             : {
     756             :     int         i,
     757      358052 :                 nest_level = 0;
     758             :     char       *srcptr;
     759      358052 :     bool        in_quotes = false;
     760      358052 :     bool        eoArray = false;
     761             :     bool        hasnull;
     762             :     int32       totbytes;
     763      358052 :     int         indx[MAXDIM] = {0},
     764             :                 prod[MAXDIM];
     765             : 
     766      358052 :     mda_get_prod(ndim, dim, prod);
     767             : 
     768             :     /* Initialize is-null markers to true */
     769      358052 :     memset(nulls, true, nitems * sizeof(bool));
     770             : 
     771             :     /*
     772             :      * We have to remove " and \ characters to create a clean item value to
     773             :      * pass to the datatype input routine.  We overwrite each item value
     774             :      * in-place within arrayStr to do this.  srcptr is the current scan point,
     775             :      * and dstptr is where we are copying to.
     776             :      *
     777             :      * We also want to suppress leading and trailing unquoted whitespace. We
     778             :      * use the leadingspace flag to suppress leading space.  Trailing space is
     779             :      * tracked by using dstendptr to point to the last significant output
     780             :      * character.
     781             :      *
     782             :      * The error checking in this routine is mostly pro-forma, since we expect
     783             :      * that ArrayCount() already validated the string.  So we don't bother
     784             :      * with errdetail messages.
     785             :      */
     786      358052 :     srcptr = arrayStr;
     787     2131954 :     while (!eoArray)
     788             :     {
     789     1773934 :         bool        itemdone = false;
     790     1773934 :         bool        leadingspace = true;
     791     1773934 :         bool        hasquoting = false;
     792             :         char       *itemstart;
     793             :         char       *dstptr;
     794             :         char       *dstendptr;
     795             : 
     796     1773934 :         i = -1;
     797     1773934 :         itemstart = dstptr = dstendptr = srcptr;
     798             : 
     799    12934884 :         while (!itemdone)
     800             :         {
     801    11160950 :             switch (*srcptr)
     802             :             {
     803           0 :                 case '\0':
     804             :                     /* Signal a premature end of the string */
     805           0 :                     ereturn(escontext, false,
     806             :                             (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     807             :                              errmsg("malformed array literal: \"%s\"",
     808             :                                     origStr)));
     809             :                     break;
     810         136 :                 case '\\':
     811             :                     /* Skip backslash, copy next character as-is. */
     812         136 :                     srcptr++;
     813         136 :                     if (*srcptr == '\0')
     814           0 :                         ereturn(escontext, false,
     815             :                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     816             :                                  errmsg("malformed array literal: \"%s\"",
     817             :                                         origStr)));
     818         136 :                     *dstptr++ = *srcptr++;
     819             :                     /* Treat the escaped character as non-whitespace */
     820         136 :                     leadingspace = false;
     821         136 :                     dstendptr = dstptr;
     822         136 :                     hasquoting = true;  /* can't be a NULL marker */
     823         136 :                     break;
     824      741140 :                 case '"':
     825      741140 :                     in_quotes = !in_quotes;
     826      741140 :                     if (in_quotes)
     827      370570 :                         leadingspace = false;
     828             :                     else
     829             :                     {
     830             :                         /*
     831             :                          * Advance dstendptr when we exit in_quotes; this
     832             :                          * saves having to do it in all the other in_quotes
     833             :                          * cases.
     834             :                          */
     835      370570 :                         dstendptr = dstptr;
     836             :                     }
     837      741140 :                     hasquoting = true;  /* can't be a NULL marker */
     838      741140 :                     srcptr++;
     839      741140 :                     break;
     840      359294 :                 case '{':
     841      359294 :                     if (!in_quotes)
     842             :                     {
     843      359236 :                         if (nest_level >= ndim)
     844           0 :                             ereturn(escontext, false,
     845             :                                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     846             :                                      errmsg("malformed array literal: \"%s\"",
     847             :                                             origStr)));
     848      359236 :                         nest_level++;
     849      359236 :                         indx[nest_level - 1] = 0;
     850      359236 :                         srcptr++;
     851             :                     }
     852             :                     else
     853          58 :                         *dstptr++ = *srcptr++;
     854      359294 :                     break;
     855      359286 :                 case '}':
     856      359286 :                     if (!in_quotes)
     857             :                     {
     858      359228 :                         if (nest_level == 0)
     859           0 :                             ereturn(escontext, false,
     860             :                                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     861             :                                      errmsg("malformed array literal: \"%s\"",
     862             :                                             origStr)));
     863      359228 :                         if (i == -1)
     864      358744 :                             i = ArrayGetOffset0(ndim, indx, prod);
     865      359228 :                         indx[nest_level - 1] = 0;
     866      359228 :                         nest_level--;
     867      359228 :                         if (nest_level == 0)
     868      358044 :                             eoArray = itemdone = true;
     869             :                         else
     870        1184 :                             indx[nest_level - 1]++;
     871      359228 :                         srcptr++;
     872             :                     }
     873             :                     else
     874          58 :                         *dstptr++ = *srcptr++;
     875      359286 :                     break;
     876     9701094 :                 default:
     877     9701094 :                     if (in_quotes)
     878     2339016 :                         *dstptr++ = *srcptr++;
     879     7362078 :                     else if (*srcptr == typdelim)
     880             :                     {
     881     1415890 :                         if (i == -1)
     882     1415190 :                             i = ArrayGetOffset0(ndim, indx, prod);
     883     1415890 :                         itemdone = true;
     884     1415890 :                         indx[ndim - 1]++;
     885     1415890 :                         srcptr++;
     886             :                     }
     887     5946188 :                     else if (array_isspace(*srcptr))
     888             :                     {
     889             :                         /*
     890             :                          * If leading space, drop it immediately.  Else, copy
     891             :                          * but don't advance dstendptr.
     892             :                          */
     893       38182 :                         if (leadingspace)
     894       37126 :                             srcptr++;
     895             :                         else
     896        1056 :                             *dstptr++ = *srcptr++;
     897             :                     }
     898             :                     else
     899             :                     {
     900     5908006 :                         *dstptr++ = *srcptr++;
     901     5908006 :                         leadingspace = false;
     902     5908006 :                         dstendptr = dstptr;
     903             :                     }
     904     9701094 :                     break;
     905             :             }
     906             :         }
     907             : 
     908             :         Assert(dstptr < srcptr);
     909     1773934 :         *dstendptr = '\0';
     910             : 
     911     1773934 :         if (i < 0 || i >= nitems)
     912           0 :             ereturn(escontext, false,
     913             :                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     914             :                      errmsg("malformed array literal: \"%s\"",
     915             :                             origStr)));
     916             : 
     917     3177298 :         if (Array_nulls && !hasquoting &&
     918     1403364 :             pg_strcasecmp(itemstart, "NULL") == 0)
     919             :         {
     920             :             /* it's a NULL item */
     921         412 :             if (!InputFunctionCallSafe(inputproc, NULL,
     922             :                                        typioparam, typmod,
     923             :                                        escontext,
     924         412 :                                        &values[i]))
     925           0 :                 return false;
     926         412 :             nulls[i] = true;
     927             :         }
     928             :         else
     929             :         {
     930     1773502 :             if (!InputFunctionCallSafe(inputproc, itemstart,
     931             :                                        typioparam, typmod,
     932             :                                        escontext,
     933     1773522 :                                        &values[i]))
     934          12 :                 return false;
     935     1773490 :             nulls[i] = false;
     936             :         }
     937             :     }
     938             : 
     939             :     /*
     940             :      * Check for nulls, compute total data space needed
     941             :      */
     942      358020 :     hasnull = false;
     943      358020 :     totbytes = 0;
     944     2131910 :     for (i = 0; i < nitems; i++)
     945             :     {
     946     1773890 :         if (nulls[i])
     947         412 :             hasnull = true;
     948             :         else
     949             :         {
     950             :             /* let's just make sure data is not toasted */
     951     1773478 :             if (typlen == -1)
     952      847914 :                 values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
     953     1773478 :             totbytes = att_addlength_datum(totbytes, typlen, values[i]);
     954     1773478 :             totbytes = att_align_nominal(totbytes, typalign);
     955             :             /* check for overflow of total request */
     956     1773478 :             if (!AllocSizeIsValid(totbytes))
     957           0 :                 ereturn(escontext, false,
     958             :                         (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     959             :                          errmsg("array size exceeds the maximum allowed (%d)",
     960             :                                 (int) MaxAllocSize)));
     961             :         }
     962             :     }
     963      358020 :     *hasnulls = hasnull;
     964      358020 :     *nbytes = totbytes;
     965      358020 :     return true;
     966             : }
     967             : 
     968             : 
     969             : /*
     970             :  * Copy data into an array object from a temporary array of Datums.
     971             :  *
     972             :  * array: array object (with header fields already filled in)
     973             :  * values: array of Datums to be copied
     974             :  * nulls: array of is-null flags (can be NULL if no nulls)
     975             :  * nitems: number of Datums to be copied
     976             :  * typbyval, typlen, typalign: info about element datatype
     977             :  * freedata: if true and element type is pass-by-ref, pfree data values
     978             :  * referenced by Datums after copying them.
     979             :  *
     980             :  * If the input data is of varlena type, the caller must have ensured that
     981             :  * the values are not toasted.  (Doing it here doesn't work since the
     982             :  * caller has already allocated space for the array...)
     983             :  */
     984             : void
     985     2286352 : CopyArrayEls(ArrayType *array,
     986             :              Datum *values,
     987             :              bool *nulls,
     988             :              int nitems,
     989             :              int typlen,
     990             :              bool typbyval,
     991             :              char typalign,
     992             :              bool freedata)
     993             : {
     994     2286352 :     char       *p = ARR_DATA_PTR(array);
     995     2286352 :     bits8      *bitmap = ARR_NULLBITMAP(array);
     996     2286352 :     int         bitval = 0;
     997     2286352 :     int         bitmask = 1;
     998             :     int         i;
     999             : 
    1000     2286352 :     if (typbyval)
    1001     1602078 :         freedata = false;
    1002             : 
    1003    19750566 :     for (i = 0; i < nitems; i++)
    1004             :     {
    1005    17464214 :         if (nulls && nulls[i])
    1006             :         {
    1007       33092 :             if (!bitmap)        /* shouldn't happen */
    1008           0 :                 elog(ERROR, "null array element where not supported");
    1009             :             /* bitmap bit stays 0 */
    1010             :         }
    1011             :         else
    1012             :         {
    1013    17431122 :             bitval |= bitmask;
    1014    17431122 :             p += ArrayCastAndSet(values[i], typlen, typbyval, typalign, p);
    1015    17431122 :             if (freedata)
    1016      848350 :                 pfree(DatumGetPointer(values[i]));
    1017             :         }
    1018    17464214 :         if (bitmap)
    1019             :         {
    1020      785342 :             bitmask <<= 1;
    1021      785342 :             if (bitmask == 0x100)
    1022             :             {
    1023       95134 :                 *bitmap++ = bitval;
    1024       95134 :                 bitval = 0;
    1025       95134 :                 bitmask = 1;
    1026             :             }
    1027             :         }
    1028             :     }
    1029             : 
    1030     2286352 :     if (bitmap && bitmask != 1)
    1031       17728 :         *bitmap = bitval;
    1032     2286352 : }
    1033             : 
    1034             : /*
    1035             :  * array_out :
    1036             :  *         takes the internal representation of an array and returns a string
    1037             :  *        containing the array in its external format.
    1038             :  */
    1039             : Datum
    1040      524222 : array_out(PG_FUNCTION_ARGS)
    1041             : {
    1042      524222 :     AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
    1043      524222 :     Oid         element_type = AARR_ELEMTYPE(v);
    1044             :     int         typlen;
    1045             :     bool        typbyval;
    1046             :     char        typalign;
    1047             :     char        typdelim;
    1048             :     char       *p,
    1049             :                *tmp,
    1050             :                *retval,
    1051             :               **values,
    1052             :                 dims_str[(MAXDIM * 33) + 2];
    1053             : 
    1054             :     /*
    1055             :      * 33 per dim since we assume 15 digits per number + ':' +'[]'
    1056             :      *
    1057             :      * +2 allows for assignment operator + trailing null
    1058             :      */
    1059             :     bool       *needquotes,
    1060      524222 :                 needdims = false;
    1061             :     size_t      overall_length;
    1062             :     int         nitems,
    1063             :                 i,
    1064             :                 j,
    1065             :                 k,
    1066             :                 indx[MAXDIM];
    1067             :     int         ndim,
    1068             :                *dims,
    1069             :                *lb;
    1070             :     array_iter  iter;
    1071             :     ArrayMetaState *my_extra;
    1072             : 
    1073             :     /*
    1074             :      * We arrange to look up info about element type, including its output
    1075             :      * conversion proc, only once per series of calls, assuming the element
    1076             :      * type doesn't change underneath us.
    1077             :      */
    1078      524222 :     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
    1079      524222 :     if (my_extra == NULL)
    1080             :     {
    1081       20720 :         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
    1082             :                                                       sizeof(ArrayMetaState));
    1083       20720 :         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
    1084       20720 :         my_extra->element_type = ~element_type;
    1085             :     }
    1086             : 
    1087      524222 :     if (my_extra->element_type != element_type)
    1088             :     {
    1089             :         /*
    1090             :          * Get info about element type, including its output conversion proc
    1091             :          */
    1092       20720 :         get_type_io_data(element_type, IOFunc_output,
    1093             :                          &my_extra->typlen, &my_extra->typbyval,
    1094             :                          &my_extra->typalign, &my_extra->typdelim,
    1095             :                          &my_extra->typioparam, &my_extra->typiofunc);
    1096       20720 :         fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
    1097       20720 :                       fcinfo->flinfo->fn_mcxt);
    1098       20720 :         my_extra->element_type = element_type;
    1099             :     }
    1100      524222 :     typlen = my_extra->typlen;
    1101      524222 :     typbyval = my_extra->typbyval;
    1102      524222 :     typalign = my_extra->typalign;
    1103      524222 :     typdelim = my_extra->typdelim;
    1104             : 
    1105      524222 :     ndim = AARR_NDIM(v);
    1106      524222 :     dims = AARR_DIMS(v);
    1107      524222 :     lb = AARR_LBOUND(v);
    1108      524222 :     nitems = ArrayGetNItems(ndim, dims);
    1109             : 
    1110      524222 :     if (nitems == 0)
    1111             :     {
    1112        2502 :         retval = pstrdup("{}");
    1113        2502 :         PG_RETURN_CSTRING(retval);
    1114             :     }
    1115             : 
    1116             :     /*
    1117             :      * we will need to add explicit dimensions if any dimension has a lower
    1118             :      * bound other than one
    1119             :      */
    1120     1044050 :     for (i = 0; i < ndim; i++)
    1121             :     {
    1122      522608 :         if (lb[i] != 1)
    1123             :         {
    1124         278 :             needdims = true;
    1125         278 :             break;
    1126             :         }
    1127             :     }
    1128             : 
    1129             :     /*
    1130             :      * Convert all values to string form, count total space needed (including
    1131             :      * any overhead such as escaping backslashes), and detect whether each
    1132             :      * item needs double quotes.
    1133             :      */
    1134      521720 :     values = (char **) palloc(nitems * sizeof(char *));
    1135      521720 :     needquotes = (bool *) palloc(nitems * sizeof(bool));
    1136      521720 :     overall_length = 0;
    1137             : 
    1138      521720 :     array_iter_setup(&iter, v);
    1139             : 
    1140     1861704 :     for (i = 0; i < nitems; i++)
    1141             :     {
    1142             :         Datum       itemvalue;
    1143             :         bool        isnull;
    1144             :         bool        needquote;
    1145             : 
    1146             :         /* Get source element, checking for NULL */
    1147     1339984 :         itemvalue = array_iter_next(&iter, &isnull, i,
    1148             :                                     typlen, typbyval, typalign);
    1149             : 
    1150     1339984 :         if (isnull)
    1151             :         {
    1152        2220 :             values[i] = pstrdup("NULL");
    1153        2220 :             overall_length += 4;
    1154        2220 :             needquote = false;
    1155             :         }
    1156             :         else
    1157             :         {
    1158     1337764 :             values[i] = OutputFunctionCall(&my_extra->proc, itemvalue);
    1159             : 
    1160             :             /* count data plus backslashes; detect chars needing quotes */
    1161     1337764 :             if (values[i][0] == '\0')
    1162         414 :                 needquote = true;   /* force quotes for empty string */
    1163     1337350 :             else if (pg_strcasecmp(values[i], "NULL") == 0)
    1164           8 :                 needquote = true;   /* force quotes for literal NULL */
    1165             :             else
    1166     1337342 :                 needquote = false;
    1167             : 
    1168    25133392 :             for (tmp = values[i]; *tmp != '\0'; tmp++)
    1169             :             {
    1170    23795628 :                 char        ch = *tmp;
    1171             : 
    1172    23795628 :                 overall_length += 1;
    1173    23795628 :                 if (ch == '"' || ch == '\\')
    1174             :                 {
    1175        2500 :                     needquote = true;
    1176        2500 :                     overall_length += 1;
    1177             :                 }
    1178    47578754 :                 else if (ch == '{' || ch == '}' || ch == typdelim ||
    1179    23785626 :                          array_isspace(ch))
    1180       11376 :                     needquote = true;
    1181             :             }
    1182             :         }
    1183             : 
    1184     1339984 :         needquotes[i] = needquote;
    1185             : 
    1186             :         /* Count the pair of double quotes, if needed */
    1187     1339984 :         if (needquote)
    1188        7412 :             overall_length += 2;
    1189             :         /* and the comma (or other typdelim delimiter) */
    1190     1339984 :         overall_length += 1;
    1191             :     }
    1192             : 
    1193             :     /*
    1194             :      * The very last array element doesn't have a typdelim delimiter after it,
    1195             :      * but that's OK; that space is needed for the trailing '\0'.
    1196             :      *
    1197             :      * Now count total number of curly brace pairs in output string.
    1198             :      */
    1199     1044382 :     for (i = j = 0, k = 1; i < ndim; i++)
    1200             :     {
    1201      522662 :         j += k, k *= dims[i];
    1202             :     }
    1203      521720 :     overall_length += 2 * j;
    1204             : 
    1205             :     /* Format explicit dimensions if required */
    1206      521720 :     dims_str[0] = '\0';
    1207      521720 :     if (needdims)
    1208             :     {
    1209         278 :         char       *ptr = dims_str;
    1210             : 
    1211         612 :         for (i = 0; i < ndim; i++)
    1212             :         {
    1213         334 :             sprintf(ptr, "[%d:%d]", lb[i], lb[i] + dims[i] - 1);
    1214         334 :             ptr += strlen(ptr);
    1215             :         }
    1216         278 :         *ptr++ = *ASSGN;
    1217         278 :         *ptr = '\0';
    1218         278 :         overall_length += ptr - dims_str;
    1219             :     }
    1220             : 
    1221             :     /* Now construct the output string */
    1222      521720 :     retval = (char *) palloc(overall_length);
    1223      521720 :     p = retval;
    1224             : 
    1225             : #define APPENDSTR(str)  (strcpy(p, (str)), p += strlen(p))
    1226             : #define APPENDCHAR(ch)  (*p++ = (ch), *p = '\0')
    1227             : 
    1228      521720 :     if (needdims)
    1229         278 :         APPENDSTR(dims_str);
    1230      521720 :     APPENDCHAR('{');
    1231     1044382 :     for (i = 0; i < ndim; i++)
    1232      522662 :         indx[i] = 0;
    1233      521720 :     j = 0;
    1234      521720 :     k = 0;
    1235             :     do
    1236             :     {
    1237     1342328 :         for (i = j; i < ndim - 1; i++)
    1238        2344 :             APPENDCHAR('{');
    1239             : 
    1240     1339984 :         if (needquotes[k])
    1241             :         {
    1242        7412 :             APPENDCHAR('"');
    1243       71966 :             for (tmp = values[k]; *tmp; tmp++)
    1244             :             {
    1245       64554 :                 char        ch = *tmp;
    1246             : 
    1247       64554 :                 if (ch == '"' || ch == '\\')
    1248        2500 :                     *p++ = '\\';
    1249       64554 :                 *p++ = ch;
    1250             :             }
    1251        7412 :             *p = '\0';
    1252        7412 :             APPENDCHAR('"');
    1253             :         }
    1254             :         else
    1255     1332572 :             APPENDSTR(values[k]);
    1256     1339984 :         pfree(values[k++]);
    1257             : 
    1258     1864048 :         for (i = ndim - 1; i >= 0; i--)
    1259             :         {
    1260     1342328 :             if (++(indx[i]) < dims[i])
    1261             :             {
    1262      818264 :                 APPENDCHAR(typdelim);
    1263      818264 :                 break;
    1264             :             }
    1265             :             else
    1266             :             {
    1267      524064 :                 indx[i] = 0;
    1268      524064 :                 APPENDCHAR('}');
    1269             :             }
    1270             :         }
    1271     1339984 :         j = i;
    1272     1339984 :     } while (j != -1);
    1273             : 
    1274             : #undef APPENDSTR
    1275             : #undef APPENDCHAR
    1276             : 
    1277             :     /* Assert that we calculated the string length accurately */
    1278             :     Assert(overall_length == (p - retval + 1));
    1279             : 
    1280      521720 :     pfree(values);
    1281      521720 :     pfree(needquotes);
    1282             : 
    1283      521720 :     PG_RETURN_CSTRING(retval);
    1284             : }
    1285             : 
    1286             : /*
    1287             :  * array_recv :
    1288             :  *        converts an array from the external binary format to
    1289             :  *        its internal format.
    1290             :  *
    1291             :  * return value :
    1292             :  *        the internal representation of the input array
    1293             :  */
    1294             : Datum
    1295          62 : array_recv(PG_FUNCTION_ARGS)
    1296             : {
    1297          62 :     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
    1298          62 :     Oid         spec_element_type = PG_GETARG_OID(1);   /* type of an array
    1299             :                                                          * element */
    1300          62 :     int32       typmod = PG_GETARG_INT32(2);    /* typmod for array elements */
    1301             :     Oid         element_type;
    1302             :     int         typlen;
    1303             :     bool        typbyval;
    1304             :     char        typalign;
    1305             :     Oid         typioparam;
    1306             :     int         i,
    1307             :                 nitems;
    1308             :     Datum      *dataPtr;
    1309             :     bool       *nullsPtr;
    1310             :     bool        hasnulls;
    1311             :     int32       nbytes;
    1312             :     int32       dataoffset;
    1313             :     ArrayType  *retval;
    1314             :     int         ndim,
    1315             :                 flags,
    1316             :                 dim[MAXDIM],
    1317             :                 lBound[MAXDIM];
    1318             :     ArrayMetaState *my_extra;
    1319             : 
    1320             :     /* Get the array header information */
    1321          62 :     ndim = pq_getmsgint(buf, 4);
    1322          62 :     if (ndim < 0)                /* we do allow zero-dimension arrays */
    1323           0 :         ereport(ERROR,
    1324             :                 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
    1325             :                  errmsg("invalid number of dimensions: %d", ndim)));
    1326          62 :     if (ndim > MAXDIM)
    1327           0 :         ereport(ERROR,
    1328             :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    1329             :                  errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
    1330             :                         ndim, MAXDIM)));
    1331             : 
    1332          62 :     flags = pq_getmsgint(buf, 4);
    1333          62 :     if (flags != 0 && flags != 1)
    1334           0 :         ereport(ERROR,
    1335             :                 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
    1336             :                  errmsg("invalid array flags")));
    1337             : 
    1338             :     /* Check element type recorded in the data */
    1339          62 :     element_type = pq_getmsgint(buf, sizeof(Oid));
    1340             : 
    1341             :     /*
    1342             :      * From a security standpoint, it doesn't matter whether the input's
    1343             :      * element type matches what we expect: the element type's receive
    1344             :      * function has to be robust enough to cope with invalid data.  However,
    1345             :      * from a user-friendliness standpoint, it's nicer to complain about type
    1346             :      * mismatches than to throw "improper binary format" errors.  But there's
    1347             :      * a problem: only built-in types have OIDs that are stable enough to
    1348             :      * believe that a mismatch is a real issue.  So complain only if both OIDs
    1349             :      * are in the built-in range.  Otherwise, carry on with the element type
    1350             :      * we "should" be getting.
    1351             :      */
    1352          62 :     if (element_type != spec_element_type)
    1353             :     {
    1354           0 :         if (element_type < FirstGenbkiObjectId &&
    1355             :             spec_element_type < FirstGenbkiObjectId)
    1356           0 :             ereport(ERROR,
    1357             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
    1358             :                      errmsg("binary data has array element type %u (%s) instead of expected %u (%s)",
    1359             :                             element_type,
    1360             :                             format_type_extended(element_type, -1,
    1361             :                                                  FORMAT_TYPE_ALLOW_INVALID),
    1362             :                             spec_element_type,
    1363             :                             format_type_extended(spec_element_type, -1,
    1364             :                                                  FORMAT_TYPE_ALLOW_INVALID))));
    1365           0 :         element_type = spec_element_type;
    1366             :     }
    1367             : 
    1368         124 :     for (i = 0; i < ndim; i++)
    1369             :     {
    1370          62 :         dim[i] = pq_getmsgint(buf, 4);
    1371          62 :         lBound[i] = pq_getmsgint(buf, 4);
    1372             :     }
    1373             : 
    1374             :     /* This checks for overflow of array dimensions */
    1375          62 :     nitems = ArrayGetNItems(ndim, dim);
    1376          62 :     ArrayCheckBounds(ndim, dim, lBound);
    1377             : 
    1378             :     /*
    1379             :      * We arrange to look up info about element type, including its receive
    1380             :      * conversion proc, only once per series of calls, assuming the element
    1381             :      * type doesn't change underneath us.
    1382             :      */
    1383          62 :     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
    1384          62 :     if (my_extra == NULL)
    1385             :     {
    1386          56 :         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
    1387             :                                                       sizeof(ArrayMetaState));
    1388          56 :         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
    1389          56 :         my_extra->element_type = ~element_type;
    1390             :     }
    1391             : 
    1392          62 :     if (my_extra->element_type != element_type)
    1393             :     {
    1394             :         /* Get info about element type, including its receive proc */
    1395          56 :         get_type_io_data(element_type, IOFunc_receive,
    1396             :                          &my_extra->typlen, &my_extra->typbyval,
    1397             :                          &my_extra->typalign, &my_extra->typdelim,
    1398             :                          &my_extra->typioparam, &my_extra->typiofunc);
    1399          56 :         if (!OidIsValid(my_extra->typiofunc))
    1400           0 :             ereport(ERROR,
    1401             :                     (errcode(ERRCODE_UNDEFINED_FUNCTION),
    1402             :                      errmsg("no binary input function available for type %s",
    1403             :                             format_type_be(element_type))));
    1404          56 :         fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
    1405          56 :                       fcinfo->flinfo->fn_mcxt);
    1406          56 :         my_extra->element_type = element_type;
    1407             :     }
    1408             : 
    1409          62 :     if (nitems == 0)
    1410             :     {
    1411             :         /* Return empty array ... but not till we've validated element_type */
    1412           0 :         PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
    1413             :     }
    1414             : 
    1415          62 :     typlen = my_extra->typlen;
    1416          62 :     typbyval = my_extra->typbyval;
    1417          62 :     typalign = my_extra->typalign;
    1418          62 :     typioparam = my_extra->typioparam;
    1419             : 
    1420          62 :     dataPtr = (Datum *) palloc(nitems * sizeof(Datum));
    1421          62 :     nullsPtr = (bool *) palloc(nitems * sizeof(bool));
    1422          62 :     ReadArrayBinary(buf, nitems,
    1423             :                     &my_extra->proc, typioparam, typmod,
    1424             :                     typlen, typbyval, typalign,
    1425             :                     dataPtr, nullsPtr,
    1426             :                     &hasnulls, &nbytes);
    1427          62 :     if (hasnulls)
    1428             :     {
    1429           0 :         dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
    1430           0 :         nbytes += dataoffset;
    1431             :     }
    1432             :     else
    1433             :     {
    1434          62 :         dataoffset = 0;         /* marker for no null bitmap */
    1435          62 :         nbytes += ARR_OVERHEAD_NONULLS(ndim);
    1436             :     }
    1437          62 :     retval = (ArrayType *) palloc0(nbytes);
    1438          62 :     SET_VARSIZE(retval, nbytes);
    1439          62 :     retval->ndim = ndim;
    1440          62 :     retval->dataoffset = dataoffset;
    1441          62 :     retval->elemtype = element_type;
    1442          62 :     memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
    1443          62 :     memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
    1444             : 
    1445          62 :     CopyArrayEls(retval,
    1446             :                  dataPtr, nullsPtr, nitems,
    1447             :                  typlen, typbyval, typalign,
    1448             :                  true);
    1449             : 
    1450          62 :     pfree(dataPtr);
    1451          62 :     pfree(nullsPtr);
    1452             : 
    1453          62 :     PG_RETURN_ARRAYTYPE_P(retval);
    1454             : }
    1455             : 
    1456             : /*
    1457             :  * ReadArrayBinary:
    1458             :  *   collect the data elements of an array being read in binary style.
    1459             :  *
    1460             :  * Inputs:
    1461             :  *  buf: the data buffer to read from.
    1462             :  *  nitems: total number of array elements (already read).
    1463             :  *  receiveproc: type-specific receive procedure for element datatype.
    1464             :  *  typioparam, typmod: auxiliary values to pass to receiveproc.
    1465             :  *  typlen, typbyval, typalign: storage parameters of element datatype.
    1466             :  *
    1467             :  * Outputs:
    1468             :  *  values[]: filled with converted data values.
    1469             :  *  nulls[]: filled with is-null markers.
    1470             :  *  *hasnulls: set true iff there are any null elements.
    1471             :  *  *nbytes: set to total size of data area needed (including alignment
    1472             :  *      padding but not including array header overhead).
    1473             :  *
    1474             :  * Note that values[] and nulls[] are allocated by the caller, and must have
    1475             :  * nitems elements.
    1476             :  */
    1477             : static void
    1478          62 : ReadArrayBinary(StringInfo buf,
    1479             :                 int nitems,
    1480             :                 FmgrInfo *receiveproc,
    1481             :                 Oid typioparam,
    1482             :                 int32 typmod,
    1483             :                 int typlen,
    1484             :                 bool typbyval,
    1485             :                 char typalign,
    1486             :                 Datum *values,
    1487             :                 bool *nulls,
    1488             :                 bool *hasnulls,
    1489             :                 int32 *nbytes)
    1490             : {
    1491             :     int         i;
    1492             :     bool        hasnull;
    1493             :     int32       totbytes;
    1494             : 
    1495         248 :     for (i = 0; i < nitems; i++)
    1496             :     {
    1497             :         int         itemlen;
    1498             :         StringInfoData elem_buf;
    1499             :         char        csave;
    1500             : 
    1501             :         /* Get and check the item length */
    1502         186 :         itemlen = pq_getmsgint(buf, 4);
    1503         186 :         if (itemlen < -1 || itemlen > (buf->len - buf->cursor))
    1504           0 :             ereport(ERROR,
    1505             :                     (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
    1506             :                      errmsg("insufficient data left in message")));
    1507             : 
    1508         186 :         if (itemlen == -1)
    1509             :         {
    1510             :             /* -1 length means NULL */
    1511           0 :             values[i] = ReceiveFunctionCall(receiveproc, NULL,
    1512             :                                             typioparam, typmod);
    1513           0 :             nulls[i] = true;
    1514           0 :             continue;
    1515             :         }
    1516             : 
    1517             :         /*
    1518             :          * Rather than copying data around, we just set up a phony StringInfo
    1519             :          * pointing to the correct portion of the input buffer. We assume we
    1520             :          * can scribble on the input buffer so as to maintain the convention
    1521             :          * that StringInfos have a trailing null.
    1522             :          */
    1523         186 :         elem_buf.data = &buf->data[buf->cursor];
    1524         186 :         elem_buf.maxlen = itemlen + 1;
    1525         186 :         elem_buf.len = itemlen;
    1526         186 :         elem_buf.cursor = 0;
    1527             : 
    1528         186 :         buf->cursor += itemlen;
    1529             : 
    1530         186 :         csave = buf->data[buf->cursor];
    1531         186 :         buf->data[buf->cursor] = '\0';
    1532             : 
    1533             :         /* Now call the element's receiveproc */
    1534         186 :         values[i] = ReceiveFunctionCall(receiveproc, &elem_buf,
    1535             :                                         typioparam, typmod);
    1536         186 :         nulls[i] = false;
    1537             : 
    1538             :         /* Trouble if it didn't eat the whole buffer */
    1539         186 :         if (elem_buf.cursor != itemlen)
    1540           0 :             ereport(ERROR,
    1541             :                     (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
    1542             :                      errmsg("improper binary format in array element %d",
    1543             :                             i + 1)));
    1544             : 
    1545         186 :         buf->data[buf->cursor] = csave;
    1546             :     }
    1547             : 
    1548             :     /*
    1549             :      * Check for nulls, compute total data space needed
    1550             :      */
    1551          62 :     hasnull = false;
    1552          62 :     totbytes = 0;
    1553         248 :     for (i = 0; i < nitems; i++)
    1554             :     {
    1555         186 :         if (nulls[i])
    1556           0 :             hasnull = true;
    1557             :         else
    1558             :         {
    1559             :             /* let's just make sure data is not toasted */
    1560         186 :             if (typlen == -1)
    1561         108 :                 values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
    1562         186 :             totbytes = att_addlength_datum(totbytes, typlen, values[i]);
    1563         186 :             totbytes = att_align_nominal(totbytes, typalign);
    1564             :             /* check for overflow of total request */
    1565         186 :             if (!AllocSizeIsValid(totbytes))
    1566           0 :                 ereport(ERROR,
    1567             :                         (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    1568             :                          errmsg("array size exceeds the maximum allowed (%d)",
    1569             :                                 (int) MaxAllocSize)));
    1570             :         }
    1571             :     }
    1572          62 :     *hasnulls = hasnull;
    1573          62 :     *nbytes = totbytes;
    1574          62 : }
    1575             : 
    1576             : 
    1577             : /*
    1578             :  * array_send :
    1579             :  *        takes the internal representation of an array and returns a bytea
    1580             :  *        containing the array in its external binary format.
    1581             :  */
    1582             : Datum
    1583          46 : array_send(PG_FUNCTION_ARGS)
    1584             : {
    1585          46 :     AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
    1586          46 :     Oid         element_type = AARR_ELEMTYPE(v);
    1587             :     int         typlen;
    1588             :     bool        typbyval;
    1589             :     char        typalign;
    1590             :     int         nitems,
    1591             :                 i;
    1592             :     int         ndim,
    1593             :                *dim,
    1594             :                *lb;
    1595             :     StringInfoData buf;
    1596             :     array_iter  iter;
    1597             :     ArrayMetaState *my_extra;
    1598             : 
    1599             :     /*
    1600             :      * We arrange to look up info about element type, including its send
    1601             :      * conversion proc, only once per series of calls, assuming the element
    1602             :      * type doesn't change underneath us.
    1603             :      */
    1604          46 :     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
    1605          46 :     if (my_extra == NULL)
    1606             :     {
    1607          40 :         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
    1608             :                                                       sizeof(ArrayMetaState));
    1609          40 :         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
    1610          40 :         my_extra->element_type = ~element_type;
    1611             :     }
    1612             : 
    1613          46 :     if (my_extra->element_type != element_type)
    1614             :     {
    1615             :         /* Get info about element type, including its send proc */
    1616          40 :         get_type_io_data(element_type, IOFunc_send,
    1617             :                          &my_extra->typlen, &my_extra->typbyval,
    1618             :                          &my_extra->typalign, &my_extra->typdelim,
    1619             :                          &my_extra->typioparam, &my_extra->typiofunc);
    1620          40 :         if (!OidIsValid(my_extra->typiofunc))
    1621           0 :             ereport(ERROR,
    1622             :                     (errcode(ERRCODE_UNDEFINED_FUNCTION),
    1623             :                      errmsg("no binary output function available for type %s",
    1624             :                             format_type_be(element_type))));
    1625          40 :         fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
    1626          40 :                       fcinfo->flinfo->fn_mcxt);
    1627          40 :         my_extra->element_type = element_type;
    1628             :     }
    1629          46 :     typlen = my_extra->typlen;
    1630          46 :     typbyval = my_extra->typbyval;
    1631          46 :     typalign = my_extra->typalign;
    1632             : 
    1633          46 :     ndim = AARR_NDIM(v);
    1634          46 :     dim = AARR_DIMS(v);
    1635          46 :     lb = AARR_LBOUND(v);
    1636          46 :     nitems = ArrayGetNItems(ndim, dim);
    1637             : 
    1638          46 :     pq_begintypsend(&buf);
    1639             : 
    1640             :     /* Send the array header information */
    1641          46 :     pq_sendint32(&buf, ndim);
    1642          46 :     pq_sendint32(&buf, AARR_HASNULL(v) ? 1 : 0);
    1643          46 :     pq_sendint32(&buf, element_type);
    1644          92 :     for (i = 0; i < ndim; i++)
    1645             :     {
    1646          46 :         pq_sendint32(&buf, dim[i]);
    1647          46 :         pq_sendint32(&buf, lb[i]);
    1648             :     }
    1649             : 
    1650             :     /* Send the array elements using the element's own sendproc */
    1651          46 :     array_iter_setup(&iter, v);
    1652             : 
    1653         184 :     for (i = 0; i < nitems; i++)
    1654             :     {
    1655             :         Datum       itemvalue;
    1656             :         bool        isnull;
    1657             : 
    1658             :         /* Get source element, checking for NULL */
    1659         138 :         itemvalue = array_iter_next(&iter, &isnull, i,
    1660             :                                     typlen, typbyval, typalign);
    1661             : 
    1662         138 :         if (isnull)
    1663             :         {
    1664             :             /* -1 length means a NULL */
    1665           0 :             pq_sendint32(&buf, -1);
    1666             :         }
    1667             :         else
    1668             :         {
    1669             :             bytea      *outputbytes;
    1670             : 
    1671         138 :             outputbytes = SendFunctionCall(&my_extra->proc, itemvalue);
    1672         138 :             pq_sendint32(&buf, VARSIZE(outputbytes) - VARHDRSZ);
    1673         138 :             pq_sendbytes(&buf, VARDATA(outputbytes),
    1674         138 :                          VARSIZE(outputbytes) - VARHDRSZ);
    1675         138 :             pfree(outputbytes);
    1676             :         }
    1677             :     }
    1678             : 
    1679          46 :     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
    1680             : }
    1681             : 
    1682             : /*
    1683             :  * array_ndims :
    1684             :  *        returns the number of dimensions of the array pointed to by "v"
    1685             :  */
    1686             : Datum
    1687        2112 : array_ndims(PG_FUNCTION_ARGS)
    1688             : {
    1689        2112 :     AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
    1690             : 
    1691             :     /* Sanity check: does it look like an array at all? */
    1692        2112 :     if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
    1693          12 :         PG_RETURN_NULL();
    1694             : 
    1695        2100 :     PG_RETURN_INT32(AARR_NDIM(v));
    1696             : }
    1697             : 
    1698             : /*
    1699             :  * array_dims :
    1700             :  *        returns the dimensions of the array pointed to by "v", as a "text"
    1701             :  */
    1702             : Datum
    1703        7838 : array_dims(PG_FUNCTION_ARGS)
    1704             : {
    1705        7838 :     AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
    1706             :     char       *p;
    1707             :     int         i;
    1708             :     int        *dimv,
    1709             :                *lb;
    1710             : 
    1711             :     /*
    1712             :      * 33 since we assume 15 digits per number + ':' +'[]'
    1713             :      *
    1714             :      * +1 for trailing null
    1715             :      */
    1716             :     char        buf[MAXDIM * 33 + 1];
    1717             : 
    1718             :     /* Sanity check: does it look like an array at all? */
    1719        7838 :     if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
    1720          68 :         PG_RETURN_NULL();
    1721             : 
    1722        7770 :     dimv = AARR_DIMS(v);
    1723        7770 :     lb = AARR_LBOUND(v);
    1724             : 
    1725        7770 :     p = buf;
    1726       15648 :     for (i = 0; i < AARR_NDIM(v); i++)
    1727             :     {
    1728        7878 :         sprintf(p, "[%d:%d]", lb[i], dimv[i] + lb[i] - 1);
    1729        7878 :         p += strlen(p);
    1730             :     }
    1731             : 
    1732        7770 :     PG_RETURN_TEXT_P(cstring_to_text(buf));
    1733             : }
    1734             : 
    1735             : /*
    1736             :  * array_lower :
    1737             :  *      returns the lower dimension, of the DIM requested, for
    1738             :  *      the array pointed to by "v", as an int4
    1739             :  */
    1740             : Datum
    1741       25344 : array_lower(PG_FUNCTION_ARGS)
    1742             : {
    1743       25344 :     AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
    1744       25344 :     int         reqdim = PG_GETARG_INT32(1);
    1745             :     int        *lb;
    1746             :     int         result;
    1747             : 
    1748             :     /* Sanity check: does it look like an array at all? */
    1749       25344 :     if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
    1750           0 :         PG_RETURN_NULL();
    1751             : 
    1752             :     /* Sanity check: was the requested dim valid */
    1753       25344 :     if (reqdim <= 0 || reqdim > AARR_NDIM(v))
    1754           0 :         PG_RETURN_NULL();
    1755             : 
    1756       25344 :     lb = AARR_LBOUND(v);
    1757       25344 :     result = lb[reqdim - 1];
    1758             : 
    1759       25344 :     PG_RETURN_INT32(result);
    1760             : }
    1761             : 
    1762             : /*
    1763             :  * array_upper :
    1764             :  *      returns the upper dimension, of the DIM requested, for
    1765             :  *      the array pointed to by "v", as an int4
    1766             :  */
    1767             : Datum
    1768       25776 : array_upper(PG_FUNCTION_ARGS)
    1769             : {
    1770       25776 :     AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
    1771       25776 :     int         reqdim = PG_GETARG_INT32(1);
    1772             :     int        *dimv,
    1773             :                *lb;
    1774             :     int         result;
    1775             : 
    1776             :     /* Sanity check: does it look like an array at all? */
    1777       25776 :     if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
    1778          30 :         PG_RETURN_NULL();
    1779             : 
    1780             :     /* Sanity check: was the requested dim valid */
    1781       25746 :     if (reqdim <= 0 || reqdim > AARR_NDIM(v))
    1782           0 :         PG_RETURN_NULL();
    1783             : 
    1784       25746 :     lb = AARR_LBOUND(v);
    1785       25746 :     dimv = AARR_DIMS(v);
    1786             : 
    1787       25746 :     result = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
    1788             : 
    1789       25746 :     PG_RETURN_INT32(result);
    1790             : }
    1791             : 
    1792             : /*
    1793             :  * array_length :
    1794             :  *      returns the length, of the dimension requested, for
    1795             :  *      the array pointed to by "v", as an int4
    1796             :  */
    1797             : Datum
    1798      108418 : array_length(PG_FUNCTION_ARGS)
    1799             : {
    1800      108418 :     AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
    1801      108418 :     int         reqdim = PG_GETARG_INT32(1);
    1802             :     int        *dimv;
    1803             :     int         result;
    1804             : 
    1805             :     /* Sanity check: does it look like an array at all? */
    1806      108418 :     if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
    1807           0 :         PG_RETURN_NULL();
    1808             : 
    1809             :     /* Sanity check: was the requested dim valid */
    1810      108418 :     if (reqdim <= 0 || reqdim > AARR_NDIM(v))
    1811          12 :         PG_RETURN_NULL();
    1812             : 
    1813      108406 :     dimv = AARR_DIMS(v);
    1814             : 
    1815      108406 :     result = dimv[reqdim - 1];
    1816             : 
    1817      108406 :     PG_RETURN_INT32(result);
    1818             : }
    1819             : 
    1820             : /*
    1821             :  * array_cardinality:
    1822             :  *      returns the total number of elements in an array
    1823             :  */
    1824             : Datum
    1825        2532 : array_cardinality(PG_FUNCTION_ARGS)
    1826             : {
    1827        2532 :     AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
    1828             : 
    1829        2532 :     PG_RETURN_INT32(ArrayGetNItems(AARR_NDIM(v), AARR_DIMS(v)));
    1830             : }
    1831             : 
    1832             : 
    1833             : /*
    1834             :  * array_get_element :
    1835             :  *    This routine takes an array datum and a subscript array and returns
    1836             :  *    the referenced item as a Datum.  Note that for a pass-by-reference
    1837             :  *    datatype, the returned Datum is a pointer into the array object.
    1838             :  *
    1839             :  * This handles both ordinary varlena arrays and fixed-length arrays.
    1840             :  *
    1841             :  * Inputs:
    1842             :  *  arraydatum: the array object (mustn't be NULL)
    1843             :  *  nSubscripts: number of subscripts supplied
    1844             :  *  indx[]: the subscript values
    1845             :  *  arraytyplen: pg_type.typlen for the array type
    1846             :  *  elmlen: pg_type.typlen for the array's element type
    1847             :  *  elmbyval: pg_type.typbyval for the array's element type
    1848             :  *  elmalign: pg_type.typalign for the array's element type
    1849             :  *
    1850             :  * Outputs:
    1851             :  *  The return value is the element Datum.
    1852             :  *  *isNull is set to indicate whether the element is NULL.
    1853             :  */
    1854             : Datum
    1855      703680 : array_get_element(Datum arraydatum,
    1856             :                   int nSubscripts,
    1857             :                   int *indx,
    1858             :                   int arraytyplen,
    1859             :                   int elmlen,
    1860             :                   bool elmbyval,
    1861             :                   char elmalign,
    1862             :                   bool *isNull)
    1863             : {
    1864             :     int         i,
    1865             :                 ndim,
    1866             :                *dim,
    1867             :                *lb,
    1868             :                 offset,
    1869             :                 fixedDim[1],
    1870             :                 fixedLb[1];
    1871             :     char       *arraydataptr,
    1872             :                *retptr;
    1873             :     bits8      *arraynullsptr;
    1874             : 
    1875      703680 :     if (arraytyplen > 0)
    1876             :     {
    1877             :         /*
    1878             :          * fixed-length arrays -- these are assumed to be 1-d, 0-based
    1879             :          */
    1880      206434 :         ndim = 1;
    1881      206434 :         fixedDim[0] = arraytyplen / elmlen;
    1882      206434 :         fixedLb[0] = 0;
    1883      206434 :         dim = fixedDim;
    1884      206434 :         lb = fixedLb;
    1885      206434 :         arraydataptr = (char *) DatumGetPointer(arraydatum);
    1886      206434 :         arraynullsptr = NULL;
    1887             :     }
    1888      497246 :     else if (VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(arraydatum)))
    1889             :     {
    1890             :         /* expanded array: let's do this in a separate function */
    1891        5004 :         return array_get_element_expanded(arraydatum,
    1892             :                                           nSubscripts,
    1893             :                                           indx,
    1894             :                                           arraytyplen,
    1895             :                                           elmlen,
    1896             :                                           elmbyval,
    1897             :                                           elmalign,
    1898             :                                           isNull);
    1899             :     }
    1900             :     else
    1901             :     {
    1902             :         /* detoast array if necessary, producing normal varlena input */
    1903      492242 :         ArrayType  *array = DatumGetArrayTypeP(arraydatum);
    1904             : 
    1905      492242 :         ndim = ARR_NDIM(array);
    1906      492242 :         dim = ARR_DIMS(array);
    1907      492242 :         lb = ARR_LBOUND(array);
    1908      492242 :         arraydataptr = ARR_DATA_PTR(array);
    1909      492242 :         arraynullsptr = ARR_NULLBITMAP(array);
    1910             :     }
    1911             : 
    1912             :     /*
    1913             :      * Return NULL for invalid subscript
    1914             :      */
    1915      698676 :     if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
    1916             :     {
    1917          96 :         *isNull = true;
    1918          96 :         return (Datum) 0;
    1919             :     }
    1920     1344234 :     for (i = 0; i < ndim; i++)
    1921             :     {
    1922      698676 :         if (indx[i] < lb[i] || indx[i] >= (dim[i] + lb[i]))
    1923             :         {
    1924       53022 :             *isNull = true;
    1925       53022 :             return (Datum) 0;
    1926             :         }
    1927             :     }
    1928             : 
    1929             :     /*
    1930             :      * Calculate the element number
    1931             :      */
    1932      645558 :     offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
    1933             : 
    1934             :     /*
    1935             :      * Check for NULL array element
    1936             :      */
    1937      645558 :     if (array_get_isnull(arraynullsptr, offset))
    1938             :     {
    1939          54 :         *isNull = true;
    1940          54 :         return (Datum) 0;
    1941             :     }
    1942             : 
    1943             :     /*
    1944             :      * OK, get the element
    1945             :      */
    1946      645504 :     *isNull = false;
    1947      645504 :     retptr = array_seek(arraydataptr, 0, arraynullsptr, offset,
    1948             :                         elmlen, elmbyval, elmalign);
    1949      645504 :     return ArrayCast(retptr, elmbyval, elmlen);
    1950             : }
    1951             : 
    1952             : /*
    1953             :  * Implementation of array_get_element() for an expanded array
    1954             :  */
    1955             : static Datum
    1956        5004 : array_get_element_expanded(Datum arraydatum,
    1957             :                            int nSubscripts, int *indx,
    1958             :                            int arraytyplen,
    1959             :                            int elmlen, bool elmbyval, char elmalign,
    1960             :                            bool *isNull)
    1961             : {
    1962             :     ExpandedArrayHeader *eah;
    1963             :     int         i,
    1964             :                 ndim,
    1965             :                *dim,
    1966             :                *lb,
    1967             :                 offset;
    1968             :     Datum      *dvalues;
    1969             :     bool       *dnulls;
    1970             : 
    1971        5004 :     eah = (ExpandedArrayHeader *) DatumGetEOHP(arraydatum);
    1972             :     Assert(eah->ea_magic == EA_MAGIC);
    1973             : 
    1974             :     /* sanity-check caller's info against object */
    1975             :     Assert(arraytyplen == -1);
    1976             :     Assert(elmlen == eah->typlen);
    1977             :     Assert(elmbyval == eah->typbyval);
    1978             :     Assert(elmalign == eah->typalign);
    1979             : 
    1980        5004 :     ndim = eah->ndims;
    1981        5004 :     dim = eah->dims;
    1982        5004 :     lb = eah->lbound;
    1983             : 
    1984             :     /*
    1985             :      * Return NULL for invalid subscript
    1986             :      */
    1987        5004 :     if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
    1988             :     {
    1989           6 :         *isNull = true;
    1990           6 :         return (Datum) 0;
    1991             :     }
    1992        9996 :     for (i = 0; i < ndim; i++)
    1993             :     {
    1994        4998 :         if (indx[i] < lb[i] || indx[i] >= (dim[i] + lb[i]))
    1995             :         {
    1996           0 :             *isNull = true;
    1997           0 :             return (Datum) 0;
    1998             :         }
    1999             :     }
    2000             : 
    2001             :     /*
    2002             :      * Calculate the element number
    2003             :      */
    2004        4998 :     offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
    2005             : 
    2006             :     /*
    2007             :      * Deconstruct array if we didn't already.  Note that we apply this even
    2008             :      * if the input is nominally read-only: it should be safe enough.
    2009             :      */
    2010        4998 :     deconstruct_expanded_array(eah);
    2011             : 
    2012        4998 :     dvalues = eah->dvalues;
    2013        4998 :     dnulls = eah->dnulls;
    2014             : 
    2015             :     /*
    2016             :      * Check for NULL array element
    2017             :      */
    2018        4998 :     if (dnulls && dnulls[offset])
    2019             :     {
    2020           0 :         *isNull = true;
    2021           0 :         return (Datum) 0;
    2022             :     }
    2023             : 
    2024             :     /*
    2025             :      * OK, get the element.  It's OK to return a pass-by-ref value as a
    2026             :      * pointer into the expanded array, for the same reason that regular
    2027             :      * array_get_element can return a pointer into flat arrays: the value is
    2028             :      * assumed not to change for as long as the Datum reference can exist.
    2029             :      */
    2030        4998 :     *isNull = false;
    2031        4998 :     return dvalues[offset];
    2032             : }
    2033             : 
    2034             : /*
    2035             :  * array_get_slice :
    2036             :  *         This routine takes an array and a range of indices (upperIndx and
    2037             :  *         lowerIndx), creates a new array structure for the referred elements
    2038             :  *         and returns a pointer to it.
    2039             :  *
    2040             :  * This handles both ordinary varlena arrays and fixed-length arrays.
    2041             :  *
    2042             :  * Inputs:
    2043             :  *  arraydatum: the array object (mustn't be NULL)
    2044             :  *  nSubscripts: number of subscripts supplied (must be same for upper/lower)
    2045             :  *  upperIndx[]: the upper subscript values
    2046             :  *  lowerIndx[]: the lower subscript values
    2047             :  *  upperProvided[]: true for provided upper subscript values
    2048             :  *  lowerProvided[]: true for provided lower subscript values
    2049             :  *  arraytyplen: pg_type.typlen for the array type
    2050             :  *  elmlen: pg_type.typlen for the array's element type
    2051             :  *  elmbyval: pg_type.typbyval for the array's element type
    2052             :  *  elmalign: pg_type.typalign for the array's element type
    2053             :  *
    2054             :  * Outputs:
    2055             :  *  The return value is the new array Datum (it's never NULL)
    2056             :  *
    2057             :  * Omitted upper and lower subscript values are replaced by the corresponding
    2058             :  * array bound.
    2059             :  *
    2060             :  * NOTE: we assume it is OK to scribble on the provided subscript arrays
    2061             :  * lowerIndx[] and upperIndx[]; also, these arrays must be of size MAXDIM
    2062             :  * even when nSubscripts is less.  These are generally just temporaries.
    2063             :  */
    2064             : Datum
    2065         384 : array_get_slice(Datum arraydatum,
    2066             :                 int nSubscripts,
    2067             :                 int *upperIndx,
    2068             :                 int *lowerIndx,
    2069             :                 bool *upperProvided,
    2070             :                 bool *lowerProvided,
    2071             :                 int arraytyplen,
    2072             :                 int elmlen,
    2073             :                 bool elmbyval,
    2074             :                 char elmalign)
    2075             : {
    2076             :     ArrayType  *array;
    2077             :     ArrayType  *newarray;
    2078             :     int         i,
    2079             :                 ndim,
    2080             :                *dim,
    2081             :                *lb,
    2082             :                *newlb;
    2083             :     int         fixedDim[1],
    2084             :                 fixedLb[1];
    2085             :     Oid         elemtype;
    2086             :     char       *arraydataptr;
    2087             :     bits8      *arraynullsptr;
    2088             :     int32       dataoffset;
    2089             :     int         bytes,
    2090             :                 span[MAXDIM];
    2091             : 
    2092         384 :     if (arraytyplen > 0)
    2093             :     {
    2094             :         /*
    2095             :          * fixed-length arrays -- currently, cannot slice these because parser
    2096             :          * labels output as being of the fixed-length array type! Code below
    2097             :          * shows how we could support it if the parser were changed to label
    2098             :          * output as a suitable varlena array type.
    2099             :          */
    2100          24 :         ereport(ERROR,
    2101             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2102             :                  errmsg("slices of fixed-length arrays not implemented")));
    2103             : 
    2104             :         /*
    2105             :          * fixed-length arrays -- these are assumed to be 1-d, 0-based
    2106             :          *
    2107             :          * XXX where would we get the correct ELEMTYPE from?
    2108             :          */
    2109             :         ndim = 1;
    2110             :         fixedDim[0] = arraytyplen / elmlen;
    2111             :         fixedLb[0] = 0;
    2112             :         dim = fixedDim;
    2113             :         lb = fixedLb;
    2114             :         elemtype = InvalidOid;  /* XXX */
    2115             :         arraydataptr = (char *) DatumGetPointer(arraydatum);
    2116             :         arraynullsptr = NULL;
    2117             :     }
    2118             :     else
    2119             :     {
    2120             :         /* detoast input array if necessary */
    2121         360 :         array = DatumGetArrayTypeP(arraydatum);
    2122             : 
    2123         360 :         ndim = ARR_NDIM(array);
    2124         360 :         dim = ARR_DIMS(array);
    2125         360 :         lb = ARR_LBOUND(array);
    2126         360 :         elemtype = ARR_ELEMTYPE(array);
    2127         360 :         arraydataptr = ARR_DATA_PTR(array);
    2128         360 :         arraynullsptr = ARR_NULLBITMAP(array);
    2129             :     }
    2130             : 
    2131             :     /*
    2132             :      * Check provided subscripts.  A slice exceeding the current array limits
    2133             :      * is silently truncated to the array limits.  If we end up with an empty
    2134             :      * slice, return an empty array.
    2135             :      */
    2136         360 :     if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
    2137          96 :         return PointerGetDatum(construct_empty_array(elemtype));
    2138             : 
    2139         606 :     for (i = 0; i < nSubscripts; i++)
    2140             :     {
    2141         354 :         if (!lowerProvided[i] || lowerIndx[i] < lb[i])
    2142         126 :             lowerIndx[i] = lb[i];
    2143         354 :         if (!upperProvided[i] || upperIndx[i] >= (dim[i] + lb[i]))
    2144          72 :             upperIndx[i] = dim[i] + lb[i] - 1;
    2145         354 :         if (lowerIndx[i] > upperIndx[i])
    2146          12 :             return PointerGetDatum(construct_empty_array(elemtype));
    2147             :     }
    2148             :     /* fill any missing subscript positions with full array range */
    2149         294 :     for (; i < ndim; i++)
    2150             :     {
    2151          42 :         lowerIndx[i] = lb[i];
    2152          42 :         upperIndx[i] = dim[i] + lb[i] - 1;
    2153          42 :         if (lowerIndx[i] > upperIndx[i])
    2154           0 :             return PointerGetDatum(construct_empty_array(elemtype));
    2155             :     }
    2156             : 
    2157         252 :     mda_get_range(ndim, span, lowerIndx, upperIndx);
    2158             : 
    2159         252 :     bytes = array_slice_size(arraydataptr, arraynullsptr,
    2160             :                              ndim, dim, lb,
    2161             :                              lowerIndx, upperIndx,
    2162             :                              elmlen, elmbyval, elmalign);
    2163             : 
    2164             :     /*
    2165             :      * Currently, we put a null bitmap in the result if the source has one;
    2166             :      * could be smarter ...
    2167             :      */
    2168         252 :     if (arraynullsptr)
    2169             :     {
    2170          36 :         dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, ArrayGetNItems(ndim, span));
    2171          36 :         bytes += dataoffset;
    2172             :     }
    2173             :     else
    2174             :     {
    2175         216 :         dataoffset = 0;         /* marker for no null bitmap */
    2176         216 :         bytes += ARR_OVERHEAD_NONULLS(ndim);
    2177             :     }
    2178             : 
    2179         252 :     newarray = (ArrayType *) palloc0(bytes);
    2180         252 :     SET_VARSIZE(newarray, bytes);
    2181         252 :     newarray->ndim = ndim;
    2182         252 :     newarray->dataoffset = dataoffset;
    2183         252 :     newarray->elemtype = elemtype;
    2184         252 :     memcpy(ARR_DIMS(newarray), span, ndim * sizeof(int));
    2185             : 
    2186             :     /*
    2187             :      * Lower bounds of the new array are set to 1.  Formerly (before 7.3) we
    2188             :      * copied the given lowerIndx values ... but that seems confusing.
    2189             :      */
    2190         252 :     newlb = ARR_LBOUND(newarray);
    2191         636 :     for (i = 0; i < ndim; i++)
    2192         384 :         newlb[i] = 1;
    2193             : 
    2194         252 :     array_extract_slice(newarray,
    2195             :                         ndim, dim, lb,
    2196             :                         arraydataptr, arraynullsptr,
    2197             :                         lowerIndx, upperIndx,
    2198             :                         elmlen, elmbyval, elmalign);
    2199             : 
    2200         252 :     return PointerGetDatum(newarray);
    2201             : }
    2202             : 
    2203             : /*
    2204             :  * array_set_element :
    2205             :  *        This routine sets the value of one array element (specified by
    2206             :  *        a subscript array) to a new value specified by "dataValue".
    2207             :  *
    2208             :  * This handles both ordinary varlena arrays and fixed-length arrays.
    2209             :  *
    2210             :  * Inputs:
    2211             :  *  arraydatum: the initial array object (mustn't be NULL)
    2212             :  *  nSubscripts: number of subscripts supplied
    2213             :  *  indx[]: the subscript values
    2214             :  *  dataValue: the datum to be inserted at the given position
    2215             :  *  isNull: whether dataValue is NULL
    2216             :  *  arraytyplen: pg_type.typlen for the array type
    2217             :  *  elmlen: pg_type.typlen for the array's element type
    2218             :  *  elmbyval: pg_type.typbyval for the array's element type
    2219             :  *  elmalign: pg_type.typalign for the array's element type
    2220             :  *
    2221             :  * Result:
    2222             :  *        A new array is returned, just like the old except for the one
    2223             :  *        modified entry.  The original array object is not changed,
    2224             :  *        unless what is passed is a read-write reference to an expanded
    2225             :  *        array object; in that case the expanded array is updated in-place.
    2226             :  *
    2227             :  * For one-dimensional arrays only, we allow the array to be extended
    2228             :  * by assigning to a position outside the existing subscript range; any
    2229             :  * positions between the existing elements and the new one are set to NULLs.
    2230             :  * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
    2231             :  *
    2232             :  * NOTE: For assignments, we throw an error for invalid subscripts etc,
    2233             :  * rather than returning a NULL as the fetch operations do.
    2234             :  */
    2235             : Datum
    2236        3674 : array_set_element(Datum arraydatum,
    2237             :                   int nSubscripts,
    2238             :                   int *indx,
    2239             :                   Datum dataValue,
    2240             :                   bool isNull,
    2241             :                   int arraytyplen,
    2242             :                   int elmlen,
    2243             :                   bool elmbyval,
    2244             :                   char elmalign)
    2245             : {
    2246             :     ArrayType  *array;
    2247             :     ArrayType  *newarray;
    2248             :     int         i,
    2249             :                 ndim,
    2250             :                 dim[MAXDIM],
    2251             :                 lb[MAXDIM],
    2252             :                 offset;
    2253             :     char       *elt_ptr;
    2254             :     bool        newhasnulls;
    2255             :     bits8      *oldnullbitmap;
    2256             :     int         oldnitems,
    2257             :                 newnitems,
    2258             :                 olddatasize,
    2259             :                 newsize,
    2260             :                 olditemlen,
    2261             :                 newitemlen,
    2262             :                 overheadlen,
    2263             :                 oldoverheadlen,
    2264             :                 addedbefore,
    2265             :                 addedafter,
    2266             :                 lenbefore,
    2267             :                 lenafter;
    2268             : 
    2269        3674 :     if (arraytyplen > 0)
    2270             :     {
    2271             :         /*
    2272             :          * fixed-length arrays -- these are assumed to be 1-d, 0-based. We
    2273             :          * cannot extend them, either.
    2274             :          */
    2275             :         char       *resultarray;
    2276             : 
    2277          18 :         if (nSubscripts != 1)
    2278           0 :             ereport(ERROR,
    2279             :                     (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    2280             :                      errmsg("wrong number of array subscripts")));
    2281             : 
    2282          18 :         if (indx[0] < 0 || indx[0] >= arraytyplen / elmlen)
    2283           6 :             ereport(ERROR,
    2284             :                     (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    2285             :                      errmsg("array subscript out of range")));
    2286             : 
    2287          12 :         if (isNull)
    2288           0 :             ereport(ERROR,
    2289             :                     (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
    2290             :                      errmsg("cannot assign null value to an element of a fixed-length array")));
    2291             : 
    2292          12 :         resultarray = (char *) palloc(arraytyplen);
    2293          12 :         memcpy(resultarray, DatumGetPointer(arraydatum), arraytyplen);
    2294          12 :         elt_ptr = (char *) resultarray + indx[0] * elmlen;
    2295          12 :         ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign, elt_ptr);
    2296          12 :         return PointerGetDatum(resultarray);
    2297             :     }
    2298             : 
    2299        3656 :     if (nSubscripts <= 0 || nSubscripts > MAXDIM)
    2300           0 :         ereport(ERROR,
    2301             :                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    2302             :                  errmsg("wrong number of array subscripts")));
    2303             : 
    2304             :     /* make sure item to be inserted is not toasted */
    2305        3656 :     if (elmlen == -1 && !isNull)
    2306        2256 :         dataValue = PointerGetDatum(PG_DETOAST_DATUM(dataValue));
    2307             : 
    2308        3656 :     if (VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(arraydatum)))
    2309             :     {
    2310             :         /* expanded array: let's do this in a separate function */
    2311        2004 :         return array_set_element_expanded(arraydatum,
    2312             :                                           nSubscripts,
    2313             :                                           indx,
    2314             :                                           dataValue,
    2315             :                                           isNull,
    2316             :                                           arraytyplen,
    2317             :                                           elmlen,
    2318             :                                           elmbyval,
    2319             :                                           elmalign);
    2320             :     }
    2321             : 
    2322             :     /* detoast input array if necessary */
    2323        1652 :     array = DatumGetArrayTypeP(arraydatum);
    2324             : 
    2325        1652 :     ndim = ARR_NDIM(array);
    2326             : 
    2327             :     /*
    2328             :      * if number of dims is zero, i.e. an empty array, create an array with
    2329             :      * nSubscripts dimensions, and set the lower bounds to the supplied
    2330             :      * subscripts
    2331             :      */
    2332        1652 :     if (ndim == 0)
    2333             :     {
    2334         216 :         Oid         elmtype = ARR_ELEMTYPE(array);
    2335             : 
    2336         434 :         for (i = 0; i < nSubscripts; i++)
    2337             :         {
    2338         218 :             dim[i] = 1;
    2339         218 :             lb[i] = indx[i];
    2340             :         }
    2341             : 
    2342         216 :         return PointerGetDatum(construct_md_array(&dataValue, &isNull,
    2343             :                                                   nSubscripts, dim, lb,
    2344             :                                                   elmtype,
    2345             :                                                   elmlen, elmbyval, elmalign));
    2346             :     }
    2347             : 
    2348        1436 :     if (ndim != nSubscripts)
    2349           6 :         ereport(ERROR,
    2350             :                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    2351             :                  errmsg("wrong number of array subscripts")));
    2352             : 
    2353             :     /* copy dim/lb since we may modify them */
    2354        1430 :     memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
    2355        1430 :     memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
    2356             : 
    2357        1430 :     newhasnulls = (ARR_HASNULL(array) || isNull);
    2358        1430 :     addedbefore = addedafter = 0;
    2359             : 
    2360             :     /*
    2361             :      * Check subscripts
    2362             :      */
    2363        1430 :     if (ndim == 1)
    2364             :     {
    2365        1424 :         if (indx[0] < lb[0])
    2366             :         {
    2367          24 :             addedbefore = lb[0] - indx[0];
    2368          24 :             dim[0] += addedbefore;
    2369          24 :             lb[0] = indx[0];
    2370          24 :             if (addedbefore > 1)
    2371          12 :                 newhasnulls = true; /* will insert nulls */
    2372             :         }
    2373        1424 :         if (indx[0] >= (dim[0] + lb[0]))
    2374             :         {
    2375        1080 :             addedafter = indx[0] - (dim[0] + lb[0]) + 1;
    2376        1080 :             dim[0] += addedafter;
    2377        1080 :             if (addedafter > 1)
    2378          36 :                 newhasnulls = true; /* will insert nulls */
    2379             :         }
    2380             :     }
    2381             :     else
    2382             :     {
    2383             :         /*
    2384             :          * XXX currently we do not support extending multi-dimensional arrays
    2385             :          * during assignment
    2386             :          */
    2387          18 :         for (i = 0; i < ndim; i++)
    2388             :         {
    2389          12 :             if (indx[i] < lb[i] ||
    2390          12 :                 indx[i] >= (dim[i] + lb[i]))
    2391           0 :                 ereport(ERROR,
    2392             :                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    2393             :                          errmsg("array subscript out of range")));
    2394             :         }
    2395             :     }
    2396             : 
    2397             :     /* This checks for overflow of the array dimensions */
    2398        1430 :     newnitems = ArrayGetNItems(ndim, dim);
    2399        1430 :     ArrayCheckBounds(ndim, dim, lb);
    2400             : 
    2401             :     /*
    2402             :      * Compute sizes of items and areas to copy
    2403             :      */
    2404        1430 :     if (newhasnulls)
    2405         134 :         overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, newnitems);
    2406             :     else
    2407        1296 :         overheadlen = ARR_OVERHEAD_NONULLS(ndim);
    2408        1430 :     oldnitems = ArrayGetNItems(ndim, ARR_DIMS(array));
    2409        1430 :     oldnullbitmap = ARR_NULLBITMAP(array);
    2410        1430 :     oldoverheadlen = ARR_DATA_OFFSET(array);
    2411        1430 :     olddatasize = ARR_SIZE(array) - oldoverheadlen;
    2412        1430 :     if (addedbefore)
    2413             :     {
    2414          24 :         offset = 0;
    2415          24 :         lenbefore = 0;
    2416          24 :         olditemlen = 0;
    2417          24 :         lenafter = olddatasize;
    2418             :     }
    2419        1406 :     else if (addedafter)
    2420             :     {
    2421        1080 :         offset = oldnitems;
    2422        1080 :         lenbefore = olddatasize;
    2423        1080 :         olditemlen = 0;
    2424        1080 :         lenafter = 0;
    2425             :     }
    2426             :     else
    2427             :     {
    2428         326 :         offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
    2429         326 :         elt_ptr = array_seek(ARR_DATA_PTR(array), 0, oldnullbitmap, offset,
    2430             :                              elmlen, elmbyval, elmalign);
    2431         326 :         lenbefore = (int) (elt_ptr - ARR_DATA_PTR(array));
    2432         326 :         if (array_get_isnull(oldnullbitmap, offset))
    2433          24 :             olditemlen = 0;
    2434             :         else
    2435             :         {
    2436         302 :             olditemlen = att_addlength_pointer(0, elmlen, elt_ptr);
    2437         302 :             olditemlen = att_align_nominal(olditemlen, elmalign);
    2438             :         }
    2439         326 :         lenafter = (int) (olddatasize - lenbefore - olditemlen);
    2440             :     }
    2441             : 
    2442        1430 :     if (isNull)
    2443          20 :         newitemlen = 0;
    2444             :     else
    2445             :     {
    2446        1410 :         newitemlen = att_addlength_datum(0, elmlen, dataValue);
    2447        1410 :         newitemlen = att_align_nominal(newitemlen, elmalign);
    2448             :     }
    2449             : 
    2450        1430 :     newsize = overheadlen + lenbefore + newitemlen + lenafter;
    2451             : 
    2452             :     /*
    2453             :      * OK, create the new array and fill in header/dimensions
    2454             :      */
    2455        1430 :     newarray = (ArrayType *) palloc0(newsize);
    2456        1430 :     SET_VARSIZE(newarray, newsize);
    2457        1430 :     newarray->ndim = ndim;
    2458        1430 :     newarray->dataoffset = newhasnulls ? overheadlen : 0;
    2459        1430 :     newarray->elemtype = ARR_ELEMTYPE(array);
    2460        1430 :     memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
    2461        1430 :     memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
    2462             : 
    2463             :     /*
    2464             :      * Fill in data
    2465             :      */
    2466        1430 :     memcpy((char *) newarray + overheadlen,
    2467             :            (char *) array + oldoverheadlen,
    2468             :            lenbefore);
    2469        1430 :     if (!isNull)
    2470        1410 :         ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign,
    2471        1410 :                         (char *) newarray + overheadlen + lenbefore);
    2472        1430 :     memcpy((char *) newarray + overheadlen + lenbefore + newitemlen,
    2473        1430 :            (char *) array + oldoverheadlen + lenbefore + olditemlen,
    2474             :            lenafter);
    2475             : 
    2476             :     /*
    2477             :      * Fill in nulls bitmap if needed
    2478             :      *
    2479             :      * Note: it's possible we just replaced the last NULL with a non-NULL, and
    2480             :      * could get rid of the bitmap.  Seems not worth testing for though.
    2481             :      */
    2482        1430 :     if (newhasnulls)
    2483             :     {
    2484         134 :         bits8      *newnullbitmap = ARR_NULLBITMAP(newarray);
    2485             : 
    2486             :         /* palloc0 above already marked any inserted positions as nulls */
    2487             :         /* Fix the inserted value */
    2488         134 :         if (addedafter)
    2489          56 :             array_set_isnull(newnullbitmap, newnitems - 1, isNull);
    2490             :         else
    2491          78 :             array_set_isnull(newnullbitmap, offset, isNull);
    2492             :         /* Fix the copied range(s) */
    2493         134 :         if (addedbefore)
    2494          24 :             array_bitmap_copy(newnullbitmap, addedbefore,
    2495             :                               oldnullbitmap, 0,
    2496             :                               oldnitems);
    2497             :         else
    2498             :         {
    2499         110 :             array_bitmap_copy(newnullbitmap, 0,
    2500             :                               oldnullbitmap, 0,
    2501             :                               offset);
    2502         110 :             if (addedafter == 0)
    2503          54 :                 array_bitmap_copy(newnullbitmap, offset + 1,
    2504             :                                   oldnullbitmap, offset + 1,
    2505          54 :                                   oldnitems - offset - 1);
    2506             :         }
    2507             :     }
    2508             : 
    2509        1430 :     return PointerGetDatum(newarray);
    2510             : }
    2511             : 
    2512             : /*
    2513             :  * Implementation of array_set_element() for an expanded array
    2514             :  *
    2515             :  * Note: as with any operation on a read/write expanded object, we must
    2516             :  * take pains not to leave the object in a corrupt state if we fail partway
    2517             :  * through.
    2518             :  */
    2519             : static Datum
    2520        2004 : array_set_element_expanded(Datum arraydatum,
    2521             :                            int nSubscripts, int *indx,
    2522             :                            Datum dataValue, bool isNull,
    2523             :                            int arraytyplen,
    2524             :                            int elmlen, bool elmbyval, char elmalign)
    2525             : {
    2526             :     ExpandedArrayHeader *eah;
    2527             :     Datum      *dvalues;
    2528             :     bool       *dnulls;
    2529             :     int         i,
    2530             :                 ndim,
    2531             :                 dim[MAXDIM],
    2532             :                 lb[MAXDIM],
    2533             :                 offset;
    2534             :     bool        dimschanged,
    2535             :                 newhasnulls;
    2536             :     int         addedbefore,
    2537             :                 addedafter;
    2538             :     char       *oldValue;
    2539             : 
    2540             :     /* Convert to R/W object if not so already */
    2541        2004 :     eah = DatumGetExpandedArray(arraydatum);
    2542             : 
    2543             :     /* Sanity-check caller's info against object; we don't use it otherwise */
    2544             :     Assert(arraytyplen == -1);
    2545             :     Assert(elmlen == eah->typlen);
    2546             :     Assert(elmbyval == eah->typbyval);
    2547             :     Assert(elmalign == eah->typalign);
    2548             : 
    2549             :     /*
    2550             :      * Copy dimension info into local storage.  This allows us to modify the
    2551             :      * dimensions if needed, while not messing up the expanded value if we
    2552             :      * fail partway through.
    2553             :      */
    2554        2004 :     ndim = eah->ndims;
    2555             :     Assert(ndim >= 0 && ndim <= MAXDIM);
    2556        2004 :     memcpy(dim, eah->dims, ndim * sizeof(int));
    2557        2004 :     memcpy(lb, eah->lbound, ndim * sizeof(int));
    2558        2004 :     dimschanged = false;
    2559             : 
    2560             :     /*
    2561             :      * if number of dims is zero, i.e. an empty array, create an array with
    2562             :      * nSubscripts dimensions, and set the lower bounds to the supplied
    2563             :      * subscripts.
    2564             :      */
    2565        2004 :     if (ndim == 0)
    2566             :     {
    2567             :         /*
    2568             :          * Allocate adequate space for new dimension info.  This is harmless
    2569             :          * if we fail later.
    2570             :          */
    2571             :         Assert(nSubscripts > 0 && nSubscripts <= MAXDIM);
    2572         416 :         eah->dims = (int *) MemoryContextAllocZero(eah->hdr.eoh_context,
    2573             :                                                    nSubscripts * sizeof(int));
    2574         416 :         eah->lbound = (int *) MemoryContextAllocZero(eah->hdr.eoh_context,
    2575             :                                                      nSubscripts * sizeof(int));
    2576             : 
    2577             :         /* Update local copies of dimension info */
    2578         416 :         ndim = nSubscripts;
    2579         832 :         for (i = 0; i < nSubscripts; i++)
    2580             :         {
    2581         416 :             dim[i] = 0;
    2582         416 :             lb[i] = indx[i];
    2583             :         }
    2584         416 :         dimschanged = true;
    2585             :     }
    2586        1588 :     else if (ndim != nSubscripts)
    2587           0 :         ereport(ERROR,
    2588             :                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    2589             :                  errmsg("wrong number of array subscripts")));
    2590             : 
    2591             :     /*
    2592             :      * Deconstruct array if we didn't already.  (Someday maybe add a special
    2593             :      * case path for fixed-length, no-nulls cases, where we can overwrite an
    2594             :      * element in place without ever deconstructing.  But today is not that
    2595             :      * day.)
    2596             :      */
    2597        2004 :     deconstruct_expanded_array(eah);
    2598             : 
    2599             :     /*
    2600             :      * Copy new element into array's context, if needed (we assume it's
    2601             :      * already detoasted, so no junk should be created).  Doing this before
    2602             :      * we've made any significant changes ensures that our behavior is sane
    2603             :      * even when the source is a reference to some element of this same array.
    2604             :      * If we fail further down, this memory is leaked, but that's reasonably
    2605             :      * harmless.
    2606             :      */
    2607        2004 :     if (!eah->typbyval && !isNull)
    2608             :     {
    2609         898 :         MemoryContext oldcxt = MemoryContextSwitchTo(eah->hdr.eoh_context);
    2610             : 
    2611         898 :         dataValue = datumCopy(dataValue, false, eah->typlen);
    2612         898 :         MemoryContextSwitchTo(oldcxt);
    2613             :     }
    2614             : 
    2615        2004 :     dvalues = eah->dvalues;
    2616        2004 :     dnulls = eah->dnulls;
    2617             : 
    2618        2004 :     newhasnulls = ((dnulls != NULL) || isNull);
    2619        2004 :     addedbefore = addedafter = 0;
    2620             : 
    2621             :     /*
    2622             :      * Check subscripts (this logic matches original array_set_element)
    2623             :      */
    2624        2004 :     if (ndim == 1)
    2625             :     {
    2626        2004 :         if (indx[0] < lb[0])
    2627             :         {
    2628          78 :             addedbefore = lb[0] - indx[0];
    2629          78 :             dim[0] += addedbefore;
    2630          78 :             lb[0] = indx[0];
    2631          78 :             dimschanged = true;
    2632          78 :             if (addedbefore > 1)
    2633           0 :                 newhasnulls = true; /* will insert nulls */
    2634             :         }
    2635        2004 :         if (indx[0] >= (dim[0] + lb[0]))
    2636             :         {
    2637        1876 :             addedafter = indx[0] - (dim[0] + lb[0]) + 1;
    2638        1876 :             dim[0] += addedafter;
    2639        1876 :             dimschanged = true;
    2640        1876 :             if (addedafter > 1)
    2641           0 :                 newhasnulls = true; /* will insert nulls */
    2642             :         }
    2643             :     }
    2644             :     else
    2645             :     {
    2646             :         /*
    2647             :          * XXX currently we do not support extending multi-dimensional arrays
    2648             :          * during assignment
    2649             :          */
    2650           0 :         for (i = 0; i < ndim; i++)
    2651             :         {
    2652           0 :             if (indx[i] < lb[i] ||
    2653           0 :                 indx[i] >= (dim[i] + lb[i]))
    2654           0 :                 ereport(ERROR,
    2655             :                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    2656             :                          errmsg("array subscript out of range")));
    2657             :         }
    2658             :     }
    2659             : 
    2660             :     /* Check for overflow of the array dimensions */
    2661        2004 :     if (dimschanged)
    2662             :     {
    2663        1954 :         (void) ArrayGetNItems(ndim, dim);
    2664        1954 :         ArrayCheckBounds(ndim, dim, lb);
    2665             :     }
    2666             : 
    2667             :     /* Now we can calculate linear offset of target item in array */
    2668        2004 :     offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
    2669             : 
    2670             :     /* Physically enlarge existing dvalues/dnulls arrays if needed */
    2671        2004 :     if (dim[0] > eah->dvalueslen)
    2672             :     {
    2673             :         /* We want some extra space if we're enlarging */
    2674        1942 :         int         newlen = dim[0] + dim[0] / 8;
    2675             : 
    2676        1942 :         newlen = Max(newlen, dim[0]);   /* integer overflow guard */
    2677        1942 :         eah->dvalues = dvalues = (Datum *)
    2678        1942 :             repalloc(dvalues, newlen * sizeof(Datum));
    2679        1942 :         if (dnulls)
    2680           0 :             eah->dnulls = dnulls = (bool *)
    2681           0 :                 repalloc(dnulls, newlen * sizeof(bool));
    2682        1942 :         eah->dvalueslen = newlen;
    2683             :     }
    2684             : 
    2685             :     /*
    2686             :      * If we need a nulls bitmap and don't already have one, create it, being
    2687             :      * sure to mark all existing entries as not null.
    2688             :      */
    2689        2004 :     if (newhasnulls && dnulls == NULL)
    2690           0 :         eah->dnulls = dnulls = (bool *)
    2691           0 :             MemoryContextAllocZero(eah->hdr.eoh_context,
    2692           0 :                                    eah->dvalueslen * sizeof(bool));
    2693             : 
    2694             :     /*
    2695             :      * We now have all the needed space allocated, so we're ready to make
    2696             :      * irreversible changes.  Be very wary of allowing failure below here.
    2697             :      */
    2698             : 
    2699             :     /* Flattened value will no longer represent array accurately */
    2700        2004 :     eah->fvalue = NULL;
    2701             :     /* And we don't know the flattened size either */
    2702        2004 :     eah->flat_size = 0;
    2703             : 
    2704             :     /* Update dimensionality info if needed */
    2705        2004 :     if (dimschanged)
    2706             :     {
    2707        1954 :         eah->ndims = ndim;
    2708        1954 :         memcpy(eah->dims, dim, ndim * sizeof(int));
    2709        1954 :         memcpy(eah->lbound, lb, ndim * sizeof(int));
    2710             :     }
    2711             : 
    2712             :     /* Reposition items if needed, and fill addedbefore items with nulls */
    2713        2004 :     if (addedbefore > 0)
    2714             :     {
    2715          78 :         memmove(dvalues + addedbefore, dvalues, eah->nelems * sizeof(Datum));
    2716         156 :         for (i = 0; i < addedbefore; i++)
    2717          78 :             dvalues[i] = (Datum) 0;
    2718          78 :         if (dnulls)
    2719             :         {
    2720           0 :             memmove(dnulls + addedbefore, dnulls, eah->nelems * sizeof(bool));
    2721           0 :             for (i = 0; i < addedbefore; i++)
    2722           0 :                 dnulls[i] = true;
    2723             :         }
    2724          78 :         eah->nelems += addedbefore;
    2725             :     }
    2726             : 
    2727             :     /* fill addedafter items with nulls */
    2728        2004 :     if (addedafter > 0)
    2729             :     {
    2730        3752 :         for (i = 0; i < addedafter; i++)
    2731        1876 :             dvalues[eah->nelems + i] = (Datum) 0;
    2732        1876 :         if (dnulls)
    2733             :         {
    2734           0 :             for (i = 0; i < addedafter; i++)
    2735           0 :                 dnulls[eah->nelems + i] = true;
    2736             :         }
    2737        1876 :         eah->nelems += addedafter;
    2738             :     }
    2739             : 
    2740             :     /* Grab old element value for pfree'ing, if needed. */
    2741        2004 :     if (!eah->typbyval && (dnulls == NULL || !dnulls[offset]))
    2742         898 :         oldValue = (char *) DatumGetPointer(dvalues[offset]);
    2743             :     else
    2744        1106 :         oldValue = NULL;
    2745             : 
    2746             :     /* And finally we can insert the new element. */
    2747        2004 :     dvalues[offset] = dataValue;
    2748        2004 :     if (dnulls)
    2749           0 :         dnulls[offset] = isNull;
    2750             : 
    2751             :     /*
    2752             :      * Free old element if needed; this keeps repeated element replacements
    2753             :      * from bloating the array's storage.  If the pfree somehow fails, it
    2754             :      * won't corrupt the array.
    2755             :      */
    2756        2004 :     if (oldValue)
    2757             :     {
    2758             :         /* Don't try to pfree a part of the original flat array */
    2759           2 :         if (oldValue < eah->fstartptr || oldValue >= eah->fendptr)
    2760           0 :             pfree(oldValue);
    2761             :     }
    2762             : 
    2763             :     /* Done, return standard TOAST pointer for object */
    2764        2004 :     return EOHPGetRWDatum(&eah->hdr);
    2765             : }
    2766             : 
    2767             : /*
    2768             :  * array_set_slice :
    2769             :  *        This routine sets the value of a range of array locations (specified
    2770             :  *        by upper and lower subscript values) to new values passed as
    2771             :  *        another array.
    2772             :  *
    2773             :  * This handles both ordinary varlena arrays and fixed-length arrays.
    2774             :  *
    2775             :  * Inputs:
    2776             :  *  arraydatum: the initial array object (mustn't be NULL)
    2777             :  *  nSubscripts: number of subscripts supplied (must be same for upper/lower)
    2778             :  *  upperIndx[]: the upper subscript values
    2779             :  *  lowerIndx[]: the lower subscript values
    2780             :  *  upperProvided[]: true for provided upper subscript values
    2781             :  *  lowerProvided[]: true for provided lower subscript values
    2782             :  *  srcArrayDatum: the source for the inserted values
    2783             :  *  isNull: indicates whether srcArrayDatum is NULL
    2784             :  *  arraytyplen: pg_type.typlen for the array type
    2785             :  *  elmlen: pg_type.typlen for the array's element type
    2786             :  *  elmbyval: pg_type.typbyval for the array's element type
    2787             :  *  elmalign: pg_type.typalign for the array's element type
    2788             :  *
    2789             :  * Result:
    2790             :  *        A new array is returned, just like the old except for the
    2791             :  *        modified range.  The original array object is not changed.
    2792             :  *
    2793             :  * Omitted upper and lower subscript values are replaced by the corresponding
    2794             :  * array bound.
    2795             :  *
    2796             :  * For one-dimensional arrays only, we allow the array to be extended
    2797             :  * by assigning to positions outside the existing subscript range; any
    2798             :  * positions between the existing elements and the new ones are set to NULLs.
    2799             :  * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
    2800             :  *
    2801             :  * NOTE: we assume it is OK to scribble on the provided index arrays
    2802             :  * lowerIndx[] and upperIndx[]; also, these arrays must be of size MAXDIM
    2803             :  * even when nSubscripts is less.  These are generally just temporaries.
    2804             :  *
    2805             :  * NOTE: For assignments, we throw an error for silly subscripts etc,
    2806             :  * rather than returning a NULL or empty array as the fetch operations do.
    2807             :  */
    2808             : Datum
    2809         244 : array_set_slice(Datum arraydatum,
    2810             :                 int nSubscripts,
    2811             :                 int *upperIndx,
    2812             :                 int *lowerIndx,
    2813             :                 bool *upperProvided,
    2814             :                 bool *lowerProvided,
    2815             :                 Datum srcArrayDatum,
    2816             :                 bool isNull,
    2817             :                 int arraytyplen,
    2818             :                 int elmlen,
    2819             :                 bool elmbyval,
    2820             :                 char elmalign)
    2821             : {
    2822             :     ArrayType  *array;
    2823             :     ArrayType  *srcArray;
    2824             :     ArrayType  *newarray;
    2825             :     int         i,
    2826             :                 ndim,
    2827             :                 dim[MAXDIM],
    2828             :                 lb[MAXDIM],
    2829             :                 span[MAXDIM];
    2830             :     bool        newhasnulls;
    2831             :     int         nitems,
    2832             :                 nsrcitems,
    2833             :                 olddatasize,
    2834             :                 newsize,
    2835             :                 olditemsize,
    2836             :                 newitemsize,
    2837             :                 overheadlen,
    2838             :                 oldoverheadlen,
    2839             :                 addedbefore,
    2840             :                 addedafter,
    2841             :                 lenbefore,
    2842             :                 lenafter,
    2843             :                 itemsbefore,
    2844             :                 itemsafter,
    2845             :                 nolditems;
    2846             : 
    2847             :     /* Currently, assignment from a NULL source array is a no-op */
    2848         244 :     if (isNull)
    2849           0 :         return arraydatum;
    2850             : 
    2851         244 :     if (arraytyplen > 0)
    2852             :     {
    2853             :         /*
    2854             :          * fixed-length arrays -- not got round to doing this...
    2855             :          */
    2856           0 :         ereport(ERROR,
    2857             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2858             :                  errmsg("updates on slices of fixed-length arrays not implemented")));
    2859             :     }
    2860             : 
    2861             :     /* detoast arrays if necessary */
    2862         244 :     array = DatumGetArrayTypeP(arraydatum);
    2863         244 :     srcArray = DatumGetArrayTypeP(srcArrayDatum);
    2864             : 
    2865             :     /* note: we assume srcArray contains no toasted elements */
    2866             : 
    2867         244 :     ndim = ARR_NDIM(array);
    2868             : 
    2869             :     /*
    2870             :      * if number of dims is zero, i.e. an empty array, create an array with
    2871             :      * nSubscripts dimensions, and set the upper and lower bounds to the
    2872             :      * supplied subscripts
    2873             :      */
    2874         244 :     if (ndim == 0)
    2875             :     {
    2876             :         Datum      *dvalues;
    2877             :         bool       *dnulls;
    2878             :         int         nelems;
    2879          44 :         Oid         elmtype = ARR_ELEMTYPE(array);
    2880             : 
    2881          44 :         deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
    2882             :                           &dvalues, &dnulls, &nelems);
    2883             : 
    2884         100 :         for (i = 0; i < nSubscripts; i++)
    2885             :         {
    2886          62 :             if (!upperProvided[i] || !lowerProvided[i])
    2887           6 :                 ereport(ERROR,
    2888             :                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    2889             :                          errmsg("array slice subscript must provide both boundaries"),
    2890             :                          errdetail("When assigning to a slice of an empty array value,"
    2891             :                                    " slice boundaries must be fully specified.")));
    2892             : 
    2893          56 :             dim[i] = 1 + upperIndx[i] - lowerIndx[i];
    2894          56 :             lb[i] = lowerIndx[i];
    2895             :         }
    2896             : 
    2897             :         /* complain if too few source items; we ignore extras, however */
    2898          38 :         if (nelems < ArrayGetNItems(nSubscripts, dim))
    2899           0 :             ereport(ERROR,
    2900             :                     (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    2901             :                      errmsg("source array too small")));
    2902             : 
    2903          38 :         return PointerGetDatum(construct_md_array(dvalues, dnulls, nSubscripts,
    2904             :                                                   dim, lb, elmtype,
    2905             :                                                   elmlen, elmbyval, elmalign));
    2906             :     }
    2907             : 
    2908         200 :     if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
    2909           0 :         ereport(ERROR,
    2910             :                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    2911             :                  errmsg("wrong number of array subscripts")));
    2912             : 
    2913             :     /* copy dim/lb since we may modify them */
    2914         200 :     memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
    2915         200 :     memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
    2916             : 
    2917         200 :     newhasnulls = (ARR_HASNULL(array) || ARR_HASNULL(srcArray));
    2918         200 :     addedbefore = addedafter = 0;
    2919             : 
    2920             :     /*
    2921             :      * Check subscripts
    2922             :      */
    2923         200 :     if (ndim == 1)
    2924             :     {
    2925             :         Assert(nSubscripts == 1);
    2926         170 :         if (!lowerProvided[0])
    2927          36 :             lowerIndx[0] = lb[0];
    2928         170 :         if (!upperProvided[0])
    2929          42 :             upperIndx[0] = dim[0] + lb[0] - 1;
    2930         170 :         if (lowerIndx[0] > upperIndx[0])
    2931           0 :             ereport(ERROR,
    2932             :                     (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    2933             :                      errmsg("upper bound cannot be less than lower bound")));
    2934         170 :         if (lowerIndx[0] < lb[0])
    2935             :         {
    2936          48 :             if (upperIndx[0] < lb[0] - 1)
    2937          12 :                 newhasnulls = true; /* will insert nulls */
    2938          48 :             addedbefore = lb[0] - lowerIndx[0];
    2939          48 :             dim[0] += addedbefore;
    2940          48 :             lb[0] = lowerIndx[0];
    2941             :         }
    2942         170 :         if (upperIndx[0] >= (dim[0] + lb[0]))
    2943             :         {
    2944          56 :             if (lowerIndx[0] > (dim[0] + lb[0]))
    2945          12 :                 newhasnulls = true; /* will insert nulls */
    2946          56 :             addedafter = upperIndx[0] - (dim[0] + lb[0]) + 1;
    2947          56 :             dim[0] += addedafter;
    2948             :         }
    2949             :     }
    2950             :     else
    2951             :     {
    2952             :         /*
    2953             :          * XXX currently we do not support extending multi-dimensional arrays
    2954             :          * during assignment
    2955             :          */
    2956         102 :         for (i = 0; i < nSubscripts; i++)
    2957             :         {
    2958          72 :             if (!lowerProvided[i])
    2959          12 :                 lowerIndx[i] = lb[i];
    2960          72 :             if (!upperProvided[i])
    2961          24 :                 upperIndx[i] = dim[i] + lb[i] - 1;
    2962          72 :             if (lowerIndx[i] > upperIndx[i])
    2963           0 :                 ereport(ERROR,
    2964             :                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    2965             :                          errmsg("upper bound cannot be less than lower bound")));
    2966          72 :             if (lowerIndx[i] < lb[i] ||
    2967          72 :                 upperIndx[i] >= (dim[i] + lb[i]))
    2968           0 :                 ereport(ERROR,
    2969             :                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    2970             :                          errmsg("array subscript out of range")));
    2971             :         }
    2972             :         /* fill any missing subscript positions with full array range */
    2973          30 :         for (; i < ndim; i++)
    2974             :         {
    2975           0 :             lowerIndx[i] = lb[i];
    2976           0 :             upperIndx[i] = dim[i] + lb[i] - 1;
    2977           0 :             if (lowerIndx[i] > upperIndx[i])
    2978           0 :                 ereport(ERROR,
    2979             :                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    2980             :                          errmsg("upper bound cannot be less than lower bound")));
    2981             :         }
    2982             :     }
    2983             : 
    2984             :     /* Do this mainly to check for overflow */
    2985         200 :     nitems = ArrayGetNItems(ndim, dim);
    2986         200 :     ArrayCheckBounds(ndim, dim, lb);
    2987             : 
    2988             :     /*
    2989             :      * Make sure source array has enough entries.  Note we ignore the shape of
    2990             :      * the source array and just read entries serially.
    2991             :      */
    2992         200 :     mda_get_range(ndim, span, lowerIndx, upperIndx);
    2993         200 :     nsrcitems = ArrayGetNItems(ndim, span);
    2994         200 :     if (nsrcitems > ArrayGetNItems(ARR_NDIM(srcArray), ARR_DIMS(srcArray)))
    2995           6 :         ereport(ERROR,
    2996             :                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    2997             :                  errmsg("source array too small")));
    2998             : 
    2999             :     /*
    3000             :      * Compute space occupied by new entries, space occupied by replaced
    3001             :      * entries, and required space for new array.
    3002             :      */
    3003         194 :     if (newhasnulls)
    3004          96 :         overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
    3005             :     else
    3006          98 :         overheadlen = ARR_OVERHEAD_NONULLS(ndim);
    3007         194 :     newitemsize = array_nelems_size(ARR_DATA_PTR(srcArray), 0,
    3008         194 :                                     ARR_NULLBITMAP(srcArray), nsrcitems,
    3009             :                                     elmlen, elmbyval, elmalign);
    3010         194 :     oldoverheadlen = ARR_DATA_OFFSET(array);
    3011         194 :     olddatasize = ARR_SIZE(array) - oldoverheadlen;
    3012         194 :     if (ndim > 1)
    3013             :     {
    3014             :         /*
    3015             :          * here we do not need to cope with extension of the array; it would
    3016             :          * be a lot more complicated if we had to do so...
    3017             :          */
    3018          30 :         olditemsize = array_slice_size(ARR_DATA_PTR(array),
    3019          30 :                                        ARR_NULLBITMAP(array),
    3020             :                                        ndim, dim, lb,
    3021             :                                        lowerIndx, upperIndx,
    3022             :                                        elmlen, elmbyval, elmalign);
    3023          30 :         lenbefore = lenafter = 0;   /* keep compiler quiet */
    3024          30 :         itemsbefore = itemsafter = nolditems = 0;
    3025             :     }
    3026             :     else
    3027             :     {
    3028             :         /*
    3029             :          * here we must allow for possibility of slice larger than orig array
    3030             :          * and/or not adjacent to orig array subscripts
    3031             :          */
    3032         164 :         int         oldlb = ARR_LBOUND(array)[0];
    3033         164 :         int         oldub = oldlb + ARR_DIMS(array)[0] - 1;
    3034         164 :         int         slicelb = Max(oldlb, lowerIndx[0]);
    3035         164 :         int         sliceub = Min(oldub, upperIndx[0]);
    3036         164 :         char       *oldarraydata = ARR_DATA_PTR(array);
    3037         164 :         bits8      *oldarraybitmap = ARR_NULLBITMAP(array);
    3038             : 
    3039             :         /* count/size of old array entries that will go before the slice */
    3040         164 :         itemsbefore = Min(slicelb, oldub + 1) - oldlb;
    3041         164 :         lenbefore = array_nelems_size(oldarraydata, 0, oldarraybitmap,
    3042             :                                       itemsbefore,
    3043             :                                       elmlen, elmbyval, elmalign);
    3044             :         /* count/size of old array entries that will be replaced by slice */
    3045         164 :         if (slicelb > sliceub)
    3046             :         {
    3047          54 :             nolditems = 0;
    3048          54 :             olditemsize = 0;
    3049             :         }
    3050             :         else
    3051             :         {
    3052         110 :             nolditems = sliceub - slicelb + 1;
    3053         110 :             olditemsize = array_nelems_size(oldarraydata + lenbefore,
    3054             :                                             itemsbefore, oldarraybitmap,
    3055             :                                             nolditems,
    3056             :                                             elmlen, elmbyval, elmalign);
    3057             :         }
    3058             :         /* count/size of old array entries that will go after the slice */
    3059         164 :         itemsafter = oldub + 1 - Max(sliceub + 1, oldlb);
    3060         164 :         lenafter = olddatasize - lenbefore - olditemsize;
    3061             :     }
    3062             : 
    3063         194 :     newsize = overheadlen + olddatasize - olditemsize + newitemsize;
    3064             : 
    3065         194 :     newarray = (ArrayType *) palloc0(newsize);
    3066         194 :     SET_VARSIZE(newarray, newsize);
    3067         194 :     newarray->ndim = ndim;
    3068         194 :     newarray->dataoffset = newhasnulls ? overheadlen : 0;
    3069         194 :     newarray->elemtype = ARR_ELEMTYPE(array);
    3070         194 :     memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
    3071         194 :     memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
    3072             : 
    3073         194 :     if (ndim > 1)
    3074             :     {
    3075             :         /*
    3076             :          * here we do not need to cope with extension of the array; it would
    3077             :          * be a lot more complicated if we had to do so...
    3078             :          */
    3079          30 :         array_insert_slice(newarray, array, srcArray,
    3080             :                            ndim, dim, lb,
    3081             :                            lowerIndx, upperIndx,
    3082             :                            elmlen, elmbyval, elmalign);
    3083             :     }
    3084             :     else
    3085             :     {
    3086             :         /* fill in data */
    3087         164 :         memcpy((char *) newarray + overheadlen,
    3088             :                (char *) array + oldoverheadlen,
    3089             :                lenbefore);
    3090         328 :         memcpy((char *) newarray + overheadlen + lenbefore,
    3091         164 :                ARR_DATA_PTR(srcArray),
    3092             :                newitemsize);
    3093         164 :         memcpy((char *) newarray + overheadlen + lenbefore + newitemsize,
    3094         164 :                (char *) array + oldoverheadlen + lenbefore + olditemsize,
    3095             :                lenafter);
    3096             :         /* fill in nulls bitmap if needed */
    3097         164 :         if (newhasnulls)
    3098             :         {
    3099          96 :             bits8      *newnullbitmap = ARR_NULLBITMAP(newarray);
    3100          96 :             bits8      *oldnullbitmap = ARR_NULLBITMAP(array);
    3101             : 
    3102             :             /* palloc0 above already marked any inserted positions as nulls */
    3103          96 :             array_bitmap_copy(newnullbitmap, addedbefore,
    3104             :                               oldnullbitmap, 0,
    3105             :                               itemsbefore);
    3106          96 :             array_bitmap_copy(newnullbitmap, lowerIndx[0] - lb[0],
    3107          96 :                               ARR_NULLBITMAP(srcArray), 0,
    3108             :                               nsrcitems);
    3109          96 :             array_bitmap_copy(newnullbitmap, addedbefore + itemsbefore + nolditems,
    3110             :                               oldnullbitmap, itemsbefore + nolditems,
    3111             :                               itemsafter);
    3112             :         }
    3113             :     }
    3114             : 
    3115         194 :     return PointerGetDatum(newarray);
    3116             : }
    3117             : 
    3118             : /*
    3119             :  * array_ref : backwards compatibility wrapper for array_get_element
    3120             :  *
    3121             :  * This only works for detoasted/flattened varlena arrays, since the array
    3122             :  * argument is declared as "ArrayType *".  However there's enough code like
    3123             :  * that to justify preserving this API.
    3124             :  */
    3125             : Datum
    3126       40672 : array_ref(ArrayType *array, int nSubscripts, int *indx,
    3127             :           int arraytyplen, int elmlen, bool elmbyval, char elmalign,
    3128             :           bool *isNull)
    3129             : {
    3130       40672 :     return array_get_element(PointerGetDatum(array), nSubscripts, indx,
    3131             :                              arraytyplen, elmlen, elmbyval, elmalign,
    3132             :                              isNull);
    3133             : }
    3134             : 
    3135             : /*
    3136             :  * array_set : backwards compatibility wrapper for array_set_element
    3137             :  *
    3138             :  * This only works for detoasted/flattened varlena arrays, since the array
    3139             :  * argument and result are declared as "ArrayType *".  However there's enough
    3140             :  * code like that to justify preserving this API.
    3141             :  */
    3142             : ArrayType *
    3143         918 : array_set(ArrayType *array, int nSubscripts, int *indx,
    3144             :           Datum dataValue, bool isNull,
    3145             :           int arraytyplen, int elmlen, bool elmbyval, char elmalign)
    3146             : {
    3147         918 :     return DatumGetArrayTypeP(array_set_element(PointerGetDatum(array),
    3148             :                                                 nSubscripts, indx,
    3149             :                                                 dataValue, isNull,
    3150             :                                                 arraytyplen,
    3151             :                                                 elmlen, elmbyval, elmalign));
    3152             : }
    3153             : 
    3154             : /*
    3155             :  * array_map()
    3156             :  *
    3157             :  * Map an array through an arbitrary expression.  Return a new array with
    3158             :  * the same dimensions and each source element transformed by the given,
    3159             :  * already-compiled expression.  Each source element is placed in the
    3160             :  * innermost_caseval/innermost_casenull fields of the ExprState.
    3161             :  *
    3162             :  * Parameters are:
    3163             :  * * arrayd: Datum representing array argument.
    3164             :  * * exprstate: ExprState representing the per-element transformation.
    3165             :  * * econtext: context for expression evaluation.
    3166             :  * * retType: OID of element type of output array.  This must be the same as,
    3167             :  *   or binary-compatible with, the result type of the expression.  It might
    3168             :  *   be different from the input array's element type.
    3169             :  * * amstate: workspace for array_map.  Must be zeroed by caller before
    3170             :  *   first call, and not touched after that.
    3171             :  *
    3172             :  * It is legitimate to pass a freshly-zeroed ArrayMapState on each call,
    3173             :  * but better performance can be had if the state can be preserved across
    3174             :  * a series of calls.
    3175             :  *
    3176             :  * NB: caller must assure that input array is not NULL.  NULL elements in
    3177             :  * the array are OK however.
    3178             :  * NB: caller should be running in econtext's per-tuple memory context.
    3179             :  */
    3180             : Datum
    3181         408 : array_map(Datum arrayd,
    3182             :           ExprState *exprstate, ExprContext *econtext,
    3183             :           Oid retType, ArrayMapState *amstate)
    3184             : {
    3185         408 :     AnyArrayType *v = DatumGetAnyArrayP(arrayd);
    3186             :     ArrayType  *result;
    3187             :     Datum      *values;
    3188             :     bool       *nulls;
    3189             :     int        *dim;
    3190             :     int         ndim;
    3191             :     int         nitems;
    3192             :     int         i;
    3193         408 :     int32       nbytes = 0;
    3194             :     int32       dataoffset;
    3195             :     bool        hasnulls;
    3196             :     Oid         inpType;
    3197             :     int         inp_typlen;
    3198             :     bool        inp_typbyval;
    3199             :     char        inp_typalign;
    3200             :     int         typlen;
    3201             :     bool        typbyval;
    3202             :     char        typalign;
    3203             :     array_iter  iter;
    3204             :     ArrayMetaState *inp_extra;
    3205             :     ArrayMetaState *ret_extra;
    3206         408 :     Datum      *transform_source = exprstate->innermost_caseval;
    3207         408 :     bool       *transform_source_isnull = exprstate->innermost_casenull;
    3208             : 
    3209         408 :     inpType = AARR_ELEMTYPE(v);
    3210         408 :     ndim = AARR_NDIM(v);
    3211         408 :     dim = AARR_DIMS(v);
    3212         408 :     nitems = ArrayGetNItems(ndim, dim);
    3213             : 
    3214             :     /* Check for empty array */
    3215         408 :     if (nitems <= 0)
    3216             :     {
    3217             :         /* Return empty array */
    3218          12 :         return PointerGetDatum(construct_empty_array(retType));
    3219             :     }
    3220             : 
    3221             :     /*
    3222             :      * We arrange to look up info about input and return element types only
    3223             :      * once per series of calls, assuming the element type doesn't change
    3224             :      * underneath us.
    3225             :      */
    3226         396 :     inp_extra = &amstate->inp_extra;
    3227         396 :     ret_extra = &amstate->ret_extra;
    3228             : 
    3229         396 :     if (inp_extra->element_type != inpType)
    3230             :     {
    3231         396 :         get_typlenbyvalalign(inpType,
    3232             :                              &inp_extra->typlen,
    3233             :                              &inp_extra->typbyval,
    3234             :                              &inp_extra->typalign);
    3235         396 :         inp_extra->element_type = inpType;
    3236             :     }
    3237         396 :     inp_typlen = inp_extra->typlen;
    3238         396 :     inp_typbyval = inp_extra->typbyval;
    3239         396 :     inp_typalign = inp_extra->typalign;
    3240             : 
    3241         396 :     if (ret_extra->element_type != retType)
    3242             :     {
    3243         396 :         get_typlenbyvalalign(retType,
    3244             :                              &ret_extra->typlen,
    3245             :                              &ret_extra->typbyval,
    3246             :                              &ret_extra->typalign);
    3247         396 :         ret_extra->element_type = retType;
    3248             :     }
    3249         396 :     typlen = ret_extra->typlen;
    3250         396 :     typbyval = ret_extra->typbyval;
    3251         396 :     typalign = ret_extra->typalign;
    3252             : 
    3253             :     /* Allocate temporary arrays for new values */
    3254         396 :     values = (Datum *) palloc(nitems * sizeof(Datum));
    3255         396 :     nulls = (bool *) palloc(nitems * sizeof(bool));
    3256             : 
    3257             :     /* Loop over source data */
    3258         396 :     array_iter_setup(&iter, v);
    3259         396 :     hasnulls = false;
    3260             : 
    3261        3148 :     for (i = 0; i < nitems; i++)
    3262             :     {
    3263             :         /* Get source element, checking for NULL */
    3264        2784 :         *transform_source =
    3265        2784 :             array_iter_next(&iter, transform_source_isnull, i,
    3266             :                             inp_typlen, inp_typbyval, inp_typalign);
    3267             : 
    3268             :         /* Apply the given expression to source element */
    3269        2784 :         values[i] = ExecEvalExpr(exprstate, econtext, &nulls[i]);
    3270             : 
    3271        2752 :         if (nulls[i])
    3272          12 :             hasnulls = true;
    3273             :         else
    3274             :         {
    3275             :             /* Ensure data is not toasted */
    3276        2740 :             if (typlen == -1)
    3277         378 :                 values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
    3278             :             /* Update total result size */
    3279        2740 :             nbytes = att_addlength_datum(nbytes, typlen, values[i]);
    3280        2740 :             nbytes = att_align_nominal(nbytes, typalign);
    3281             :             /* check for overflow of total request */
    3282        2740 :             if (!AllocSizeIsValid(nbytes))
    3283           0 :                 ereport(ERROR,
    3284             :                         (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    3285             :                          errmsg("array size exceeds the maximum allowed (%d)",
    3286             :                                 (int) MaxAllocSize)));
    3287             :         }
    3288             :     }
    3289             : 
    3290             :     /* Allocate and fill the result array */
    3291         364 :     if (hasnulls)
    3292             :     {
    3293           6 :         dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
    3294           6 :         nbytes += dataoffset;
    3295             :     }
    3296             :     else
    3297             :     {
    3298         358 :         dataoffset = 0;         /* marker for no null bitmap */
    3299         358 :         nbytes += ARR_OVERHEAD_NONULLS(ndim);
    3300             :     }
    3301         364 :     result = (ArrayType *) palloc0(nbytes);
    3302         364 :     SET_VARSIZE(result, nbytes);
    3303         364 :     result->ndim = ndim;
    3304         364 :     result->dataoffset = dataoffset;
    3305         364 :     result->elemtype = retType;
    3306         364 :     memcpy(ARR_DIMS(result), AARR_DIMS(v), ndim * sizeof(int));
    3307         364 :     memcpy(ARR_LBOUND(result), AARR_LBOUND(v), ndim * sizeof(int));
    3308             : 
    3309         364 :     CopyArrayEls(result,
    3310             :                  values, nulls, nitems,
    3311             :                  typlen, typbyval, typalign,
    3312             :                  false);
    3313             : 
    3314             :     /*
    3315             :      * Note: do not risk trying to pfree the results of the called expression
    3316             :      */
    3317         364 :     pfree(values);
    3318         364 :     pfree(nulls);
    3319             : 
    3320         364 :     return PointerGetDatum(result);
    3321             : }
    3322             : 
    3323             : /*
    3324             :  * construct_array  --- simple method for constructing an array object
    3325             :  *
    3326             :  * elems: array of Datum items to become the array contents
    3327             :  *        (NULL element values are not supported).
    3328             :  * nelems: number of items
    3329             :  * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
    3330             :  *
    3331             :  * A palloc'd 1-D array object is constructed and returned.  Note that
    3332             :  * elem values will be copied into the object even if pass-by-ref type.
    3333             :  * Also note the result will be 0-D not 1-D if nelems = 0.
    3334             :  *
    3335             :  * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
    3336             :  * from the system catalogs, given the elmtype.  However, the caller is
    3337             :  * in a better position to cache this info across multiple uses, or even
    3338             :  * to hard-wire values if the element type is hard-wired.
    3339             :  */
    3340             : ArrayType *
    3341      894668 : construct_array(Datum *elems, int nelems,
    3342             :                 Oid elmtype,
    3343             :                 int elmlen, bool elmbyval, char elmalign)
    3344             : {
    3345             :     int         dims[1];
    3346             :     int         lbs[1];
    3347             : 
    3348      894668 :     dims[0] = nelems;
    3349      894668 :     lbs[0] = 1;
    3350             : 
    3351      894668 :     return construct_md_array(elems, NULL, 1, dims, lbs,
    3352             :                               elmtype, elmlen, elmbyval, elmalign);
    3353             : }
    3354             : 
    3355             : /*
    3356             :  * Like construct_array(), where elmtype must be a built-in type, and
    3357             :  * elmlen/elmbyval/elmalign is looked up from hardcoded data.  This is often
    3358             :  * useful when manipulating arrays from/for system catalogs.
    3359             :  */
    3360             : ArrayType *
    3361      588662 : construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
    3362             : {
    3363             :     int         elmlen;
    3364             :     bool        elmbyval;
    3365             :     char        elmalign;
    3366             : 
    3367      588662 :     switch (elmtype)
    3368             :     {
    3369        8006 :         case CHAROID:
    3370        8006 :             elmlen = 1;
    3371        8006 :             elmbyval = true;
    3372        8006 :             elmalign = TYPALIGN_CHAR;
    3373        8006 :             break;
    3374             : 
    3375        8842 :         case CSTRINGOID:
    3376        8842 :             elmlen = -2;
    3377        8842 :             elmbyval = false;
    3378        8842 :             elmalign = TYPALIGN_CHAR;
    3379        8842 :             break;
    3380             : 
    3381      432892 :         case FLOAT4OID:
    3382      432892 :             elmlen = sizeof(float4);
    3383      432892 :             elmbyval = true;
    3384      432892 :             elmalign = TYPALIGN_INT;
    3385      432892 :             break;
    3386             : 
    3387       82950 :         case INT2OID:
    3388       82950 :             elmlen = sizeof(int16);
    3389       82950 :             elmbyval = true;
    3390       82950 :             elmalign = TYPALIGN_SHORT;
    3391       82950 :             break;
    3392             : 
    3393        6584 :         case INT4OID:
    3394        6584 :             elmlen = sizeof(int32);
    3395        6584 :             elmbyval = true;
    3396        6584 :             elmalign = TYPALIGN_INT;
    3397        6584 :             break;
    3398             : 
    3399           4 :         case INT8OID:
    3400           4 :             elmlen = sizeof(int64);
    3401           4 :             elmbyval = FLOAT8PASSBYVAL;
    3402           4 :             elmalign = TYPALIGN_DOUBLE;
    3403           4 :             break;
    3404             : 
    3405         500 :         case NAMEOID:
    3406         500 :             elmlen = NAMEDATALEN;
    3407         500 :             elmbyval = false;
    3408         500 :             elmalign = TYPALIGN_CHAR;
    3409         500 :             break;
    3410             : 
    3411       18384 :         case OIDOID:
    3412             :         case REGTYPEOID:
    3413       18384 :             elmlen = sizeof(Oid);
    3414       18384 :             elmbyval = true;
    3415       18384 :             elmalign = TYPALIGN_INT;
    3416       18384 :             break;
    3417             : 
    3418       30458 :         case TEXTOID:
    3419       30458 :             elmlen = -1;
    3420       30458 :             elmbyval = false;
    3421       30458 :             elmalign = TYPALIGN_INT;
    3422       30458 :             break;
    3423             : 
    3424          42 :         case TIDOID:
    3425          42 :             elmlen = sizeof(ItemPointerData);
    3426          42 :             elmbyval = false;
    3427          42 :             elmalign = TYPALIGN_SHORT;
    3428          42 :             break;
    3429             : 
    3430           0 :         default:
    3431           0 :             elog(ERROR, "type %u not supported by construct_array_builtin()", elmtype);
    3432             :             /* keep compiler quiet */
    3433             :             elmlen = 0;
    3434             :             elmbyval = false;
    3435             :             elmalign = 0;
    3436             :     }
    3437             : 
    3438      588662 :     return construct_array(elems, nelems, elmtype, elmlen, elmbyval, elmalign);
    3439             : }
    3440             : 
    3441             : /*
    3442             :  * construct_md_array   --- simple method for constructing an array object
    3443             :  *                          with arbitrary dimensions and possible NULLs
    3444             :  *
    3445             :  * elems: array of Datum items to become the array contents
    3446             :  * nulls: array of is-null flags (can be NULL if no nulls)
    3447             :  * ndims: number of dimensions
    3448             :  * dims: integer array with size of each dimension
    3449             :  * lbs: integer array with lower bound of each dimension
    3450             :  * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
    3451             :  *
    3452             :  * A palloc'd ndims-D array object is constructed and returned.  Note that
    3453             :  * elem values will be copied into the object even if pass-by-ref type.
    3454             :  * Also note the result will be 0-D not ndims-D if any dims[i] = 0.
    3455             :  *
    3456             :  * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
    3457             :  * from the system catalogs, given the elmtype.  However, the caller is
    3458             :  * in a better position to cache this info across multiple uses, or even
    3459             :  * to hard-wire values if the element type is hard-wired.
    3460             :  */
    3461             : ArrayType *
    3462     1968328 : construct_md_array(Datum *elems,
    3463             :                    bool *nulls,
    3464             :                    int ndims,
    3465             :                    int *dims,
    3466             :                    int *lbs,
    3467             :                    Oid elmtype, int elmlen, bool elmbyval, char elmalign)
    3468             : {
    3469             :     ArrayType  *result;
    3470             :     bool        hasnulls;
    3471             :     int32       nbytes;
    3472             :     int32       dataoffset;
    3473             :     int         i;
    3474             :     int         nelems;
    3475             : 
    3476     1968328 :     if (ndims < 0)               /* we do allow zero-dimension arrays */
    3477           0 :         ereport(ERROR,
    3478             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    3479             :                  errmsg("invalid number of dimensions: %d", ndims)));
    3480     1968328 :     if (ndims > MAXDIM)
    3481           0 :         ereport(ERROR,
    3482             :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    3483             :                  errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
    3484             :                         ndims, MAXDIM)));
    3485             : 
    3486             :     /* This checks for overflow of the array dimensions */
    3487     1968328 :     nelems = ArrayGetNItems(ndims, dims);
    3488     1968328 :     ArrayCheckBounds(ndims, dims, lbs);
    3489             : 
    3490             :     /* if ndims <= 0 or any dims[i] == 0, return empty array */
    3491     1968328 :     if (nelems <= 0)
    3492       43668 :         return construct_empty_array(elmtype);
    3493             : 
    3494             :     /* compute required space */
    3495     1924660 :     nbytes = 0;
    3496     1924660 :     hasnulls = false;
    3497    17603224 :     for (i = 0; i < nelems; i++)
    3498             :     {
    3499    15678564 :         if (nulls && nulls[i])
    3500             :         {
    3501       32662 :             hasnulls = true;
    3502       32662 :             continue;
    3503             :         }
    3504             :         /* make sure data is not toasted */
    3505    15645902 :         if (elmlen == -1)
    3506     3564910 :             elems[i] = PointerGetDatum(PG_DETOAST_DATUM(elems[i]));
    3507    15645902 :         nbytes = att_addlength_datum(nbytes, elmlen, elems[i]);
    3508    15645902 :         nbytes = att_align_nominal(nbytes, elmalign);
    3509             :         /* check for overflow of total request */
    3510    15645902 :         if (!AllocSizeIsValid(nbytes))
    3511           0 :             ereport(ERROR,
    3512             :                     (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    3513             :                      errmsg("array size exceeds the maximum allowed (%d)",
    3514             :                             (int) MaxAllocSize)));
    3515             :     }
    3516             : 
    3517             :     /* Allocate and initialize result array */
    3518     1924660 :     if (hasnulls)
    3519             :     {
    3520       17440 :         dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nelems);
    3521       17440 :         nbytes += dataoffset;
    3522             :     }
    3523             :     else
    3524             :     {
    3525     1907220 :         dataoffset = 0;         /* marker for no null bitmap */
    3526     1907220 :         nbytes += ARR_OVERHEAD_NONULLS(ndims);
    3527             :     }
    3528     1924660 :     result = (ArrayType *) palloc0(nbytes);
    3529     1924660 :     SET_VARSIZE(result, nbytes);
    3530     1924660 :     result->ndim = ndims;
    3531     1924660 :     result->dataoffset = dataoffset;
    3532     1924660 :     result->elemtype = elmtype;
    3533     1924660 :     memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
    3534     1924660 :     memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
    3535             : 
    3536     1924660 :     CopyArrayEls(result,
    3537             :                  elems, nulls, nelems,
    3538             :                  elmlen, elmbyval, elmalign,
    3539             :                  false);
    3540             : 
    3541     1924660 :     return result;
    3542             : }
    3543             : 
    3544             : /*
    3545             :  * construct_empty_array    --- make a zero-dimensional array of given type
    3546             :  */
    3547             : ArrayType *
    3548     2328712 : construct_empty_array(Oid elmtype)
    3549             : {
    3550             :     ArrayType  *result;
    3551             : 
    3552     2328712 :     result = (ArrayType *) palloc0(sizeof(ArrayType));
    3553     2328712 :     SET_VARSIZE(result, sizeof(ArrayType));
    3554     2328712 :     result->ndim = 0;
    3555     2328712 :     result->dataoffset = 0;
    3556     2328712 :     result->elemtype = elmtype;
    3557     2328712 :     return result;
    3558             : }
    3559             : 
    3560             : /*
    3561             :  * construct_empty_expanded_array: make an empty expanded array
    3562             :  * given only type information.  (metacache can be NULL if not needed.)
    3563             :  */
    3564             : ExpandedArrayHeader *
    3565          24 : construct_empty_expanded_array(Oid element_type,
    3566             :                                MemoryContext parentcontext,
    3567             :                                ArrayMetaState *metacache)
    3568             : {
    3569          24 :     ArrayType  *array = construct_empty_array(element_type);
    3570             :     Datum       d;
    3571             : 
    3572          24 :     d = expand_array(PointerGetDatum(array), parentcontext, metacache);
    3573          24 :     pfree(array);
    3574          24 :     return (ExpandedArrayHeader *) DatumGetEOHP(d);
    3575             : }
    3576             : 
    3577             : /*
    3578             :  * deconstruct_array  --- simple method for extracting data from an array
    3579             :  *
    3580             :  * array: array object to examine (must not be NULL)
    3581             :  * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
    3582             :  * elemsp: return value, set to point to palloc'd array of Datum values
    3583             :  * nullsp: return value, set to point to palloc'd array of isnull markers
    3584             :  * nelemsp: return value, set to number of extracted values
    3585             :  *
    3586             :  * The caller may pass nullsp == NULL if it does not support NULLs in the
    3587             :  * array.  Note that this produces a very uninformative error message,
    3588             :  * so do it only in cases where a NULL is really not expected.
    3589             :  *
    3590             :  * If array elements are pass-by-ref data type, the returned Datums will
    3591             :  * be pointers into the array object.
    3592             :  *
    3593             :  * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
    3594             :  * from the system catalogs, given the elmtype.  However, the caller is
    3595             :  * in a better position to cache this info across multiple uses, or even
    3596             :  * to hard-wire values if the element type is hard-wired.
    3597             :  */
    3598             : void
    3599     3062430 : deconstruct_array(ArrayType *array,
    3600             :                   Oid elmtype,
    3601             :                   int elmlen, bool elmbyval, char elmalign,
    3602             :                   Datum **elemsp, bool **nullsp, int *nelemsp)
    3603             : {
    3604             :     Datum      *elems;
    3605             :     bool       *nulls;
    3606             :     int         nelems;
    3607             :     char       *p;
    3608             :     bits8      *bitmap;
    3609             :     int         bitmask;
    3610             :     int         i;
    3611             : 
    3612             :     Assert(ARR_ELEMTYPE(array) == elmtype);
    3613             : 
    3614     3062430 :     nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
    3615     3062430 :     *elemsp = elems = (Datum *) palloc(nelems * sizeof(Datum));
    3616     3062430 :     if (nullsp)
    3617     1924376 :         *nullsp = nulls = (bool *) palloc0(nelems * sizeof(bool));
    3618             :     else
    3619     1138054 :         nulls = NULL;
    3620     3062430 :     *nelemsp = nelems;
    3621             : 
    3622     3062430 :     p = ARR_DATA_PTR(array);
    3623     3062430 :     bitmap = ARR_NULLBITMAP(array);
    3624     3062430 :     bitmask = 1;
    3625             : 
    3626    45141482 :     for (i = 0; i < nelems; i++)
    3627             :     {
    3628             :         /* Get source element, checking for NULL */
    3629    42079052 :         if (bitmap && (*bitmap & bitmask) == 0)
    3630             :         {
    3631        3166 :             elems[i] = (Datum) 0;
    3632        3166 :             if (nulls)
    3633        3166 :                 nulls[i] = true;
    3634             :             else
    3635           0 :                 ereport(ERROR,
    3636             :                         (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
    3637             :                          errmsg("null array element not allowed in this context")));
    3638             :         }
    3639             :         else
    3640             :         {
    3641    42075886 :             elems[i] = fetch_att(p, elmbyval, elmlen);
    3642    42075886 :             p = att_addlength_pointer(p, elmlen, p);
    3643    42075886 :             p = (char *) att_align_nominal(p, elmalign);
    3644             :         }
    3645             : 
    3646             :         /* advance bitmap pointer if any */
    3647    42079052 :         if (bitmap)
    3648             :         {
    3649        4940 :             bitmask <<= 1;
    3650        4940 :             if (bitmask == 0x100)
    3651             :             {
    3652          66 :                 bitmap++;
    3653          66 :                 bitmask = 1;
    3654             :             }
    3655             :         }
    3656             :     }
    3657     3062430 : }
    3658             : 
    3659             : /*
    3660             :  * Like deconstruct_array(), where elmtype must be a built-in type, and
    3661             :  * elmlen/elmbyval/elmalign is looked up from hardcoded data.  This is often
    3662             :  * useful when manipulating arrays from/for system catalogs.
    3663             :  */
    3664             : void
    3665      628112 : deconstruct_array_builtin(ArrayType *array,
    3666             :                           Oid elmtype,
    3667             :                           Datum **elemsp, bool **nullsp, int *nelemsp)
    3668             : {
    3669             :     int         elmlen;
    3670             :     bool        elmbyval;
    3671             :     char        elmalign;
    3672             : 
    3673      628112 :     switch (elmtype)
    3674             :     {
    3675          18 :         case CHAROID:
    3676          18 :             elmlen = 1;
    3677          18 :             elmbyval = true;
    3678          18 :             elmalign = TYPALIGN_CHAR;
    3679          18 :             break;
    3680             : 
    3681        8842 :         case CSTRINGOID:
    3682        8842 :             elmlen = -2;
    3683        8842 :             elmbyval = false;
    3684        8842 :             elmalign = TYPALIGN_CHAR;
    3685        8842 :             break;
    3686             : 
    3687          30 :         case FLOAT8OID:
    3688          30 :             elmlen = sizeof(float8);
    3689          30 :             elmbyval = FLOAT8PASSBYVAL;
    3690          30 :             elmalign = TYPALIGN_DOUBLE;
    3691          30 :             break;
    3692             : 
    3693        3638 :         case INT2OID:
    3694        3638 :             elmlen = sizeof(int16);
    3695        3638 :             elmbyval = true;
    3696        3638 :             elmalign = TYPALIGN_SHORT;
    3697        3638 :             break;
    3698             : 
    3699         226 :         case OIDOID:
    3700         226 :             elmlen = sizeof(Oid);
    3701         226 :             elmbyval = true;
    3702         226 :             elmalign = TYPALIGN_INT;
    3703         226 :             break;
    3704             : 
    3705      615334 :         case TEXTOID:
    3706      615334 :             elmlen = -1;
    3707      615334 :             elmbyval = false;
    3708      615334 :             elmalign = TYPALIGN_INT;
    3709      615334 :             break;
    3710             : 
    3711          24 :         case TIDOID:
    3712          24 :             elmlen = sizeof(ItemPointerData);
    3713          24 :             elmbyval = false;
    3714          24 :             elmalign = TYPALIGN_SHORT;
    3715          24 :             break;
    3716             : 
    3717           0 :         default:
    3718           0 :             elog(ERROR, "type %u not supported by deconstruct_array_builtin()", elmtype);
    3719             :             /* keep compiler quiet */
    3720             :             elmlen = 0;
    3721             :             elmbyval = false;
    3722             :             elmalign = 0;
    3723             :     }
    3724             : 
    3725      628112 :     deconstruct_array(array, elmtype, elmlen, elmbyval, elmalign, elemsp, nullsp, nelemsp);
    3726      628112 : }
    3727             : 
    3728             : /*
    3729             :  * array_contains_nulls --- detect whether an array has any null elements
    3730             :  *
    3731             :  * This gives an accurate answer, whereas testing ARR_HASNULL only tells
    3732             :  * if the array *might* contain a null.
    3733             :  */
    3734             : bool
    3735       74388 : array_contains_nulls(ArrayType *array)
    3736             : {
    3737             :     int         nelems;
    3738             :     bits8      *bitmap;
    3739             :     int         bitmask;
    3740             : 
    3741             :     /* Easy answer if there's no null bitmap */
    3742       74388 :     if (!ARR_HASNULL(array))
    3743       74338 :         return false;
    3744             : 
    3745          50 :     nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
    3746             : 
    3747          50 :     bitmap = ARR_NULLBITMAP(array);
    3748             : 
    3749             :     /* check whole bytes of the bitmap byte-at-a-time */
    3750          50 :     while (nelems >= 8)
    3751             :     {
    3752          12 :         if (*bitmap != 0xFF)
    3753          12 :             return true;
    3754           0 :         bitmap++;
    3755           0 :         nelems -= 8;
    3756             :     }
    3757             : 
    3758             :     /* check last partial byte */
    3759          38 :     bitmask = 1;
    3760          86 :     while (nelems > 0)
    3761             :     {
    3762          86 :         if ((*bitmap & bitmask) == 0)
    3763          38 :             return true;
    3764          48 :         bitmask <<= 1;
    3765          48 :         nelems--;
    3766             :     }
    3767             : 
    3768           0 :     return false;
    3769             : }
    3770             : 
    3771             : 
    3772             : /*
    3773             :  * array_eq :
    3774             :  *        compares two arrays for equality
    3775             :  * result :
    3776             :  *        returns true if the arrays are equal, false otherwise.
    3777             :  *
    3778             :  * Note: we do not use array_cmp here, since equality may be meaningful in
    3779             :  * datatypes that don't have a total ordering (and hence no btree support).
    3780             :  */
    3781             : Datum
    3782      524252 : array_eq(PG_FUNCTION_ARGS)
    3783             : {
    3784      524252 :     LOCAL_FCINFO(locfcinfo, 2);
    3785      524252 :     AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
    3786      524252 :     AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
    3787      524252 :     Oid         collation = PG_GET_COLLATION();
    3788      524252 :     int         ndims1 = AARR_NDIM(array1);
    3789      524252 :     int         ndims2 = AARR_NDIM(array2);
    3790      524252 :     int        *dims1 = AARR_DIMS(array1);
    3791      524252 :     int        *dims2 = AARR_DIMS(array2);
    3792      524252 :     int        *lbs1 = AARR_LBOUND(array1);
    3793      524252 :     int        *lbs2 = AARR_LBOUND(array2);
    3794      524252 :     Oid         element_type = AARR_ELEMTYPE(array1);
    3795      524252 :     bool        result = true;
    3796             :     int         nitems;
    3797             :     TypeCacheEntry *typentry;
    3798             :     int         typlen;
    3799             :     bool        typbyval;
    3800             :     char        typalign;
    3801             :     array_iter  it1;
    3802             :     array_iter  it2;
    3803             :     int         i;
    3804             : 
    3805      524252 :     if (element_type != AARR_ELEMTYPE(array2))
    3806           0 :         ereport(ERROR,
    3807             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    3808             :                  errmsg("cannot compare arrays of different element types")));
    3809             : 
    3810             :     /* fast path if the arrays do not have the same dimensionality */
    3811      524252 :     if (ndims1 != ndims2 ||
    3812      522968 :         memcmp(dims1, dims2, ndims1 * sizeof(int)) != 0 ||
    3813      407194 :         memcmp(lbs1, lbs2, ndims1 * sizeof(int)) != 0)
    3814      117058 :         result = false;
    3815             :     else
    3816             :     {
    3817             :         /*
    3818             :          * We arrange to look up the equality function only once per series of
    3819             :          * calls, assuming the element type doesn't change underneath us.  The
    3820             :          * typcache is used so that we have no memory leakage when being used
    3821             :          * as an index support function.
    3822             :          */
    3823      407194 :         typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
    3824      407194 :         if (typentry == NULL ||
    3825      402766 :             typentry->type_id != element_type)
    3826             :         {
    3827        4428 :             typentry = lookup_type_cache(element_type,
    3828             :                                          TYPECACHE_EQ_OPR_FINFO);
    3829        4428 :             if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
    3830           0 :                 ereport(ERROR,
    3831             :                         (errcode(ERRCODE_UNDEFINED_FUNCTION),
    3832             :                          errmsg("could not identify an equality operator for type %s",
    3833             :                                 format_type_be(element_type))));
    3834        4428 :             fcinfo->flinfo->fn_extra = (void *) typentry;
    3835             :         }
    3836      407194 :         typlen = typentry->typlen;
    3837      407194 :         typbyval = typentry->typbyval;
    3838      407194 :         typalign = typentry->typalign;
    3839             : 
    3840             :         /*
    3841             :          * apply the operator to each pair of array elements.
    3842             :          */
    3843      407194 :         InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
    3844             :                                  collation, NULL, NULL);
    3845             : 
    3846             :         /* Loop over source data */
    3847      407194 :         nitems = ArrayGetNItems(ndims1, dims1);
    3848      407194 :         array_iter_setup(&it1, array1);
    3849      407194 :         array_iter_setup(&it2, array2);
    3850             : 
    3851      997226 :         for (i = 0; i < nitems; i++)
    3852             :         {
    3853             :             Datum       elt1;
    3854             :             Datum       elt2;
    3855             :             bool        isnull1;
    3856             :             bool        isnull2;
    3857             :             bool        oprresult;
    3858             : 
    3859             :             /* Get elements, checking for NULL */
    3860      649672 :             elt1 = array_iter_next(&it1, &isnull1, i,
    3861             :                                    typlen, typbyval, typalign);
    3862      649672 :             elt2 = array_iter_next(&it2, &isnull2, i,
    3863             :                                    typlen, typbyval, typalign);
    3864             : 
    3865             :             /*
    3866             :              * We consider two NULLs equal; NULL and not-NULL are unequal.
    3867             :              */
    3868      649672 :             if (isnull1 && isnull2)
    3869          22 :                 continue;
    3870      649650 :             if (isnull1 || isnull2)
    3871             :             {
    3872         108 :                 result = false;
    3873       59640 :                 break;
    3874             :             }
    3875             : 
    3876             :             /*
    3877             :              * Apply the operator to the element pair; treat NULL as false
    3878             :              */
    3879      649542 :             locfcinfo->args[0].value = elt1;
    3880      649542 :             locfcinfo->args[0].isnull = false;
    3881      649542 :             locfcinfo->args[1].value = elt2;
    3882      649542 :             locfcinfo->args[1].isnull = false;
    3883      649542 :             locfcinfo->isnull = false;
    3884      649542 :             oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
    3885      649542 :             if (locfcinfo->isnull || !oprresult)
    3886             :             {
    3887       59532 :                 result = false;
    3888       59532 :                 break;
    3889             :             }
    3890             :         }
    3891             :     }
    3892             : 
    3893             :     /* Avoid leaking memory when handed toasted input. */
    3894      524252 :     AARR_FREE_IF_COPY(array1, 0);
    3895      524252 :     AARR_FREE_IF_COPY(array2, 1);
    3896             : 
    3897      524252 :     PG_RETURN_BOOL(result);
    3898             : }
    3899             : 
    3900             : 
    3901             : /*-----------------------------------------------------------------------------
    3902             :  * array-array bool operators:
    3903             :  *      Given two arrays, iterate comparison operators
    3904             :  *      over the array. Uses logic similar to text comparison
    3905             :  *      functions, except element-by-element instead of
    3906             :  *      character-by-character.
    3907             :  *----------------------------------------------------------------------------
    3908             :  */
    3909             : 
    3910             : Datum
    3911         900 : array_ne(PG_FUNCTION_ARGS)
    3912             : {
    3913         900 :     PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo)));
    3914             : }
    3915             : 
    3916             : Datum
    3917          18 : array_lt(PG_FUNCTION_ARGS)
    3918             : {
    3919          18 :     PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
    3920             : }
    3921             : 
    3922             : Datum
    3923          18 : array_gt(PG_FUNCTION_ARGS)
    3924             : {
    3925          18 :     PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
    3926             : }
    3927             : 
    3928             : Datum
    3929          18 : array_le(PG_FUNCTION_ARGS)
    3930             : {
    3931          18 :     PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
    3932             : }
    3933             : 
    3934             : Datum
    3935          18 : array_ge(PG_FUNCTION_ARGS)
    3936             : {
    3937          18 :     PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
    3938             : }
    3939             : 
    3940             : Datum
    3941    10868456 : btarraycmp(PG_FUNCTION_ARGS)
    3942             : {
    3943    10868456 :     PG_RETURN_INT32(array_cmp(fcinfo));
    3944             : }
    3945             : 
    3946             : /*
    3947             :  * array_cmp()
    3948             :  * Internal comparison function for arrays.
    3949             :  *
    3950             :  * Returns -1, 0 or 1
    3951             :  */
    3952             : static int
    3953    10869074 : array_cmp(FunctionCallInfo fcinfo)
    3954             : {
    3955    10869074 :     LOCAL_FCINFO(locfcinfo, 2);
    3956    10869074 :     AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
    3957    10869074 :     AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
    3958    10869074 :     Oid         collation = PG_GET_COLLATION();
    3959    10869074 :     int         ndims1 = AARR_NDIM(array1);
    3960    10869074 :     int         ndims2 = AARR_NDIM(array2);
    3961    10869074 :     int        *dims1 = AARR_DIMS(array1);
    3962    10869074 :     int        *dims2 = AARR_DIMS(array2);
    3963    10869074 :     int         nitems1 = ArrayGetNItems(ndims1, dims1);
    3964    10869074 :     int         nitems2 = ArrayGetNItems(ndims2, dims2);
    3965    10869074 :     Oid         element_type = AARR_ELEMTYPE(array1);
    3966    10869074 :     int         result = 0;
    3967             :     TypeCacheEntry *typentry;
    3968             :     int         typlen;
    3969             :     bool        typbyval;
    3970             :     char        typalign;
    3971             :     int         min_nitems;
    3972             :     array_iter  it1;
    3973             :     array_iter  it2;
    3974             :     int         i;
    3975             : 
    3976    10869074 :     if (element_type != AARR_ELEMTYPE(array2))
    3977           6 :         ereport(ERROR,
    3978             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    3979             :                  errmsg("cannot compare arrays of different element types")));
    3980             : 
    3981             :     /*
    3982             :      * We arrange to look up the comparison function only once per series of
    3983             :      * calls, assuming the element type doesn't change underneath us. The
    3984             :      * typcache is used so that we have no memory leakage when being used as
    3985             :      * an index support function.
    3986             :      */
    3987    10869068 :     typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
    3988    10869068 :     if (typentry == NULL ||
    3989    10863736 :         typentry->type_id != element_type)
    3990             :     {
    3991        5332 :         typentry = lookup_type_cache(element_type,
    3992             :                                      TYPECACHE_CMP_PROC_FINFO);
    3993        5332 :         if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
    3994           0 :             ereport(ERROR,
    3995             :                     (errcode(ERRCODE_UNDEFINED_FUNCTION),
    3996             :                      errmsg("could not identify a comparison function for type %s",
    3997             :                             format_type_be(element_type))));
    3998        5332 :         fcinfo->flinfo->fn_extra = (void *) typentry;
    3999             :     }
    4000    10869068 :     typlen = typentry->typlen;
    4001    10869068 :     typbyval = typentry->typbyval;
    4002    10869068 :     typalign = typentry->typalign;
    4003             : 
    4004             :     /*
    4005             :      * apply the operator to each pair of array elements.
    4006             :      */
    4007    10869068 :     InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
    4008             :                              collation, NULL, NULL);
    4009             : 
    4010             :     /* Loop over source data */
    4011    10869068 :     min_nitems = Min(nitems1, nitems2);
    4012    10869068 :     array_iter_setup(&it1, array1);
    4013    10869068 :     array_iter_setup(&it2, array2);
    4014             : 
    4015    23302350 :     for (i = 0; i < min_nitems; i++)
    4016             :     {
    4017             :         Datum       elt1;
    4018             :         Datum       elt2;
    4019             :         bool        isnull1;
    4020             :         bool        isnull2;
    4021             :         int32       cmpresult;
    4022             : 
    4023             :         /* Get elements, checking for NULL */
    4024    19462782 :         elt1 = array_iter_next(&it1, &isnull1, i, typlen, typbyval, typalign);
    4025    19462782 :         elt2 = array_iter_next(&it2, &isnull2, i, typlen, typbyval, typalign);
    4026             : 
    4027             :         /*
    4028             :          * We consider two NULLs equal; NULL > not-NULL.
    4029             :          */
    4030    19462782 :         if (isnull1 && isnull2)
    4031    12433282 :             continue;
    4032    19462772 :         if (isnull1)
    4033             :         {
    4034             :             /* arg1 is greater than arg2 */
    4035         160 :             result = 1;
    4036     7029500 :             break;
    4037             :         }
    4038    19462612 :         if (isnull2)
    4039             :         {
    4040             :             /* arg1 is less than arg2 */
    4041         300 :             result = -1;
    4042         300 :             break;
    4043             :         }
    4044             : 
    4045             :         /* Compare the pair of elements */
    4046    19462312 :         locfcinfo->args[0].value = elt1;
    4047    19462312 :         locfcinfo->args[0].isnull = false;
    4048    19462312 :         locfcinfo->args[1].value = elt2;
    4049    19462312 :         locfcinfo->args[1].isnull = false;
    4050    19462312 :         cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
    4051             : 
    4052             :         /* We don't expect comparison support functions to return null */
    4053             :         Assert(!locfcinfo->isnull);
    4054             : 
    4055    19462312 :         if (cmpresult == 0)
    4056    12433272 :             continue;           /* equal */
    4057             : 
    4058     7029040 :         if (cmpresult < 0)
    4059             :         {
    4060             :             /* arg1 is less than arg2 */
    4061     3804580 :             result = -1;
    4062     3804580 :             break;
    4063             :         }
    4064             :         else
    4065             :         {
    4066             :             /* arg1 is greater than arg2 */
    4067     3224460 :             result = 1;
    4068     3224460 :             break;
    4069             :         }
    4070             :     }
    4071             : 
    4072             :     /*
    4073             :      * If arrays contain same data (up to end of shorter one), apply
    4074             :      * additional rules to sort by dimensionality.  The relative significance
    4075             :      * of the different bits of information is historical; mainly we just care
    4076             :      * that we don't say "equal" for arrays of different dimensionality.
    4077             :      */
    4078    10869068 :     if (result == 0)
    4079             :     {
    4080     3839568 :         if (nitems1 != nitems2)
    4081      592914 :             result = (nitems1 < nitems2) ? -1 : 1;
    4082     3246654 :         else if (ndims1 != ndims2)
    4083           0 :             result = (ndims1 < ndims2) ? -1 : 1;
    4084             :         else
    4085             :         {
    4086     6493266 :             for (i = 0; i < ndims1; i++)
    4087             :             {
    4088     3246612 :                 if (dims1[i] != dims2[i])
    4089             :                 {
    4090           0 :                     result = (dims1[i] < dims2[i]) ? -1 : 1;
    4091           0 :                     break;
    4092             :                 }
    4093             :             }
    4094     3246654 :             if (result == 0)
    4095             :             {
    4096     3246654 :                 int        *lbound1 = AARR_LBOUND(array1);
    4097     3246654 :                 int        *lbound2 = AARR_LBOUND(array2);
    4098             : 
    4099     6493266 :                 for (i = 0; i < ndims1; i++)
    4100             :                 {
    4101     3246612 :                     if (lbound1[i] != lbound2[i])
    4102             :                     {
    4103           0 :                         result = (lbound1[i] < lbound2[i]) ? -1 : 1;
    4104           0 :                         break;
    4105             :                     }
    4106             :                 }
    4107             :             }
    4108             :         }
    4109             :     }
    4110             : 
    4111             :     /* Avoid leaking memory when handed toasted input. */
    4112    10869068 :     AARR_FREE_IF_COPY(array1, 0);
    4113    10869068 :     AARR_FREE_IF_COPY(array2, 1);
    4114             : 
    4115    10869068 :     return result;
    4116             : }
    4117             : 
    4118             : 
    4119             : /*-----------------------------------------------------------------------------
    4120             :  * array hashing
    4121             :  *      Hash the elements and combine the results.
    4122             :  *----------------------------------------------------------------------------
    4123             :  */
    4124             : 
    4125             : Datum
    4126       35792 : hash_array(PG_FUNCTION_ARGS)
    4127             : {
    4128       35792 :     LOCAL_FCINFO(locfcinfo, 1);
    4129       35792 :     AnyArrayType *array = PG_GETARG_ANY_ARRAY_P(0);
    4130       35792 :     int         ndims = AARR_NDIM(array);
    4131       35792 :     int        *dims = AARR_DIMS(array);
    4132       35792 :     Oid         element_type = AARR_ELEMTYPE(array);
    4133       35792 :     uint32      result = 1;
    4134             :     int         nitems;
    4135             :     TypeCacheEntry *typentry;
    4136             :     int         typlen;
    4137             :     bool        typbyval;
    4138             :     char        typalign;
    4139             :     int         i;
    4140             :     array_iter  iter;
    4141             : 
    4142             :     /*
    4143             :      * We arrange to look up the hash function only once per series of calls,
    4144             :      * assuming the element type doesn't change underneath us.  The typcache
    4145             :      * is used so that we have no memory leakage when being used as an index
    4146             :      * support function.
    4147             :      */
    4148       35792 :     typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
    4149       35792 :     if (typentry == NULL ||
    4150       35496 :         typentry->type_id != element_type)
    4151             :     {
    4152         296 :         typentry = lookup_type_cache(element_type,
    4153             :                                      TYPECACHE_HASH_PROC_FINFO);
    4154         296 :         if (!OidIsValid(typentry->hash_proc_finfo.fn_oid) && element_type != RECORDOID)
    4155           6 :             ereport(ERROR,
    4156             :                     (errcode(ERRCODE_UNDEFINED_FUNCTION),
    4157             :                      errmsg("could not identify a hash function for type %s",
    4158             :                             format_type_be(element_type))));
    4159             : 
    4160             :         /*
    4161             :          * The type cache doesn't believe that record is hashable (see
    4162             :          * cache_record_field_properties()), but since we're here, we're
    4163             :          * committed to hashing, so we can assume it does.  Worst case, if any
    4164             :          * components of the record don't support hashing, we will fail at
    4165             :          * execution.
    4166             :          */
    4167         290 :         if (element_type == RECORDOID)
    4168             :         {
    4169             :             MemoryContext oldcontext;
    4170             :             TypeCacheEntry *record_typentry;
    4171             : 
    4172          18 :             oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
    4173             : 
    4174             :             /*
    4175             :              * Make fake type cache entry structure.  Note that we can't just
    4176             :              * modify typentry, since that points directly into the type
    4177             :              * cache.
    4178             :              */
    4179          18 :             record_typentry = palloc0(sizeof(*record_typentry));
    4180          18 :             record_typentry->type_id = element_type;
    4181             : 
    4182             :             /* fill in what we need below */
    4183          18 :             record_typentry->typlen = typentry->typlen;
    4184          18 :             record_typentry->typbyval = typentry->typbyval;
    4185          18 :             record_typentry->typalign = typentry->typalign;
    4186          18 :             fmgr_info(F_HASH_RECORD, &record_typentry->hash_proc_finfo);
    4187             : 
    4188          18 :             MemoryContextSwitchTo(oldcontext);
    4189             : 
    4190          18 :             typentry = record_typentry;
    4191             :         }
    4192             : 
    4193         290 :         fcinfo->flinfo->fn_extra = (void *) typentry;
    4194             :     }
    4195             : 
    4196       35786 :     typlen = typentry->typlen;
    4197       35786 :     typbyval = typentry->typbyval;
    4198       35786 :     typalign = typentry->typalign;
    4199             : 
    4200             :     /*
    4201             :      * apply the hash function to each array element.
    4202             :      */
    4203       35786 :     InitFunctionCallInfoData(*locfcinfo, &typentry->hash_proc_finfo, 1,
    4204             :                              PG_GET_COLLATION(), NULL, NULL);
    4205             : 
    4206             :     /* Loop over source data */
    4207       35786 :     nitems = ArrayGetNItems(ndims, dims);
    4208       35786 :     array_iter_setup(&iter, array);
    4209             : 
    4210      107466 :     for (i = 0; i < nitems; i++)
    4211             :     {
    4212             :         Datum       elt;
    4213             :         bool        isnull;
    4214             :         uint32      elthash;
    4215             : 
    4216             :         /* Get element, checking for NULL */
    4217       71680 :         elt = array_iter_next(&iter, &isnull, i, typlen, typbyval, typalign);
    4218             : 
    4219       71680 :         if (isnull)
    4220             :         {
    4221             :             /* Treat nulls as having hashvalue 0 */
    4222           0 :             elthash = 0;
    4223             :         }
    4224             :         else
    4225             :         {
    4226             :             /* Apply the hash function */
    4227       71680 :             locfcinfo->args[0].value = elt;
    4228       71680 :             locfcinfo->args[0].isnull = false;
    4229       71680 :             elthash = DatumGetUInt32(FunctionCallInvoke(locfcinfo));
    4230             :             /* We don't expect hash functions to return null */
    4231             :             Assert(!locfcinfo->isnull);
    4232             :         }
    4233             : 
    4234             :         /*
    4235             :          * Combine hash values of successive elements by multiplying the
    4236             :          * current value by 31 and adding on the new element's hash value.
    4237             :          *
    4238             :          * The result is a sum in which each element's hash value is
    4239             :          * multiplied by a different power of 31. This is modulo 2^32
    4240             :          * arithmetic, and the powers of 31 modulo 2^32 form a cyclic group of
    4241             :          * order 2^27. So for arrays of up to 2^27 elements, each element's
    4242             :          * hash value is multiplied by a different (odd) number, resulting in
    4243             :          * a good mixing of all the elements' hash values.
    4244             :          */
    4245       71680 :         result = (result << 5) - result + elthash;
    4246             :     }
    4247             : 
    4248             :     /* Avoid leaking memory when handed toasted input. */
    4249       35786 :     AARR_FREE_IF_COPY(array, 0);
    4250             : 
    4251       35786 :     PG_RETURN_UINT32(result);
    4252             : }
    4253             : 
    4254             : /*
    4255             :  * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
    4256             :  * Otherwise, similar to hash_array.
    4257             :  */
    4258             : Datum
    4259         144 : hash_array_extended(PG_FUNCTION_ARGS)
    4260             : {
    4261         144 :     LOCAL_FCINFO(locfcinfo, 2);
    4262         144 :     AnyArrayType *array = PG_GETARG_ANY_ARRAY_P(0);
    4263         144 :     uint64      seed = PG_GETARG_INT64(1);
    4264         144 :     int         ndims = AARR_NDIM(array);
    4265         144 :     int        *dims = AARR_DIMS(array);
    4266         144 :     Oid         element_type = AARR_ELEMTYPE(array);
    4267         144 :     uint64      result = 1;
    4268             :     int         nitems;
    4269             :     TypeCacheEntry *typentry;
    4270             :     int         typlen;
    4271             :     bool        typbyval;
    4272             :     char        typalign;
    4273             :     int         i;
    4274             :     array_iter  iter;
    4275             : 
    4276         144 :     typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
    4277         144 :     if (typentry == NULL ||
    4278          84 :         typentry->type_id != element_type)
    4279             :     {
    4280          60 :         typentry = lookup_type_cache(element_type,
    4281             :                                      TYPECACHE_HASH_EXTENDED_PROC_FINFO);
    4282          60 :         if (!OidIsValid(typentry->hash_extended_proc_finfo.fn_oid))
    4283           6 :             ereport(ERROR,
    4284             :                     (errcode(ERRCODE_UNDEFINED_FUNCTION),
    4285             :                      errmsg("could not identify an extended hash function for type %s",
    4286             :                             format_type_be(element_type))));
    4287          54 :         fcinfo->flinfo->fn_extra = (void *) typentry;
    4288             :     }
    4289         138 :     typlen = typentry->typlen;
    4290         138 :     typbyval = typentry->typbyval;
    4291         138 :     typalign = typentry->typalign;
    4292             : 
    4293         138 :     InitFunctionCallInfoData(*locfcinfo, &typentry->hash_extended_proc_finfo, 2,
    4294             :                              PG_GET_COLLATION(), NULL, NULL);
    4295             : 
    4296             :     /* Loop over source data */
    4297         138 :     nitems = ArrayGetNItems(ndims, dims);
    4298         138 :     array_iter_setup(&iter, array);
    4299             : 
    4300         456 :     for (i = 0; i < nitems; i++)
    4301             :     {
    4302             :         Datum       elt;
    4303             :         bool        isnull;
    4304             :         uint64      elthash;
    4305             : 
    4306             :         /* Get element, checking for NULL */
    4307         318 :         elt = array_iter_next(&iter, &isnull, i, typlen, typbyval, typalign);
    4308             : 
    4309         318 :         if (isnull)
    4310             :         {
    4311           0 :             elthash = 0;
    4312             :         }
    4313             :         else
    4314             :         {
    4315             :             /* Apply the hash function */
    4316         318 :             locfcinfo->args[0].value = elt;
    4317         318 :             locfcinfo->args[0].isnull = false;
    4318         318 :             locfcinfo->args[1].value = Int64GetDatum(seed);
    4319         318 :             locfcinfo->args[1].isnull = false;
    4320         318 :             elthash = DatumGetUInt64(FunctionCallInvoke(locfcinfo));
    4321             :             /* We don't expect hash functions to return null */
    4322             :             Assert(!locfcinfo->isnull);
    4323             :         }
    4324             : 
    4325         318 :         result = (result << 5) - result + elthash;
    4326             :     }
    4327             : 
    4328         138 :     AARR_FREE_IF_COPY(array, 0);
    4329             : 
    4330         138 :     PG_RETURN_UINT64(result);
    4331             : }
    4332             : 
    4333             : 
    4334             : /*-----------------------------------------------------------------------------
    4335             :  * array overlap/containment comparisons
    4336             :  *      These use the same methods of comparing array elements as array_eq.
    4337             :  *      We consider only the elements of the arrays, ignoring dimensionality.
    4338             :  *----------------------------------------------------------------------------
    4339             :  */
    4340             : 
    4341             : /*
    4342             :  * array_contain_compare :
    4343             :  *        compares two arrays for overlap/containment
    4344             :  *
    4345             :  * When matchall is true, return true if all members of array1 are in array2.
    4346             :  * When matchall is false, return true if any members of array1 are in array2.
    4347             :  */
    4348             : static bool
    4349       22452 : array_contain_compare(AnyArrayType *array1, AnyArrayType *array2, Oid collation,
    4350             :                       bool matchall, void **fn_extra)
    4351             : {
    4352       22452 :     LOCAL_FCINFO(locfcinfo, 2);
    4353       22452 :     bool        result = matchall;
    4354       22452 :     Oid         element_type = AARR_ELEMTYPE(array1);
    4355             :     TypeCacheEntry *typentry;
    4356             :     int         nelems1;
    4357             :     Datum      *values2;
    4358             :     bool       *nulls2;
    4359             :     int         nelems2;
    4360             :     int         typlen;
    4361             :     bool        typbyval;
    4362             :     char        typalign;
    4363             :     int         i;
    4364             :     int         j;
    4365             :     array_iter  it1;
    4366             : 
    4367       22452 :     if (element_type != AARR_ELEMTYPE(array2))
    4368           0 :         ereport(ERROR,
    4369             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    4370             :                  errmsg("cannot compare arrays of different element types")));
    4371             : 
    4372             :     /*
    4373             :      * We arrange to look up the equality function only once per series of
    4374             :      * calls, assuming the element type doesn't change underneath us.  The
    4375             :      * typcache is used so that we have no memory leakage when being used as
    4376             :      * an index support function.
    4377             :      */
    4378       22452 :     typentry = (TypeCacheEntry *) *fn_extra;
    4379       22452 :     if (typentry == NULL ||
    4380       21922 :         typentry->type_id != element_type)
    4381             :     {
    4382         530 :         typentry = lookup_type_cache(element_type,
    4383             :                                      TYPECACHE_EQ_OPR_FINFO);
    4384         530 :         if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
    4385           0 :             ereport(ERROR,
    4386             :                     (errcode(ERRCODE_UNDEFINED_FUNCTION),
    4387             :                      errmsg("could not identify an equality operator for type %s",
    4388             :                             format_type_be(element_type))));
    4389         530 :         *fn_extra = (void *) typentry;
    4390             :     }
    4391       22452 :     typlen = typentry->typlen;
    4392       22452 :     typbyval = typentry->typbyval;
    4393       22452 :     typalign = typentry->typalign;
    4394             : 
    4395             :     /*
    4396             :      * Since we probably will need to scan array2 multiple times, it's
    4397             :      * worthwhile to use deconstruct_array on it.  We scan array1 the hard way
    4398             :      * however, since we very likely won't need to look at all of it.
    4399             :      */
    4400       22452 :     if (VARATT_IS_EXPANDED_HEADER(array2))
    4401             :     {
    4402             :         /* This should be safe even if input is read-only */
    4403        5112 :         deconstruct_expanded_array(&(array2->xpn));
    4404        5112 :         values2 = array2->xpn.dvalues;
    4405        5112 :         nulls2 = array2->xpn.dnulls;
    4406        5112 :         nelems2 = array2->xpn.nelems;
    4407             :     }
    4408             :     else
    4409       17340 :         deconstruct_array((ArrayType *) array2,
    4410             :                           element_type, typlen, typbyval, typalign,
    4411             :                           &values2, &nulls2, &nelems2);
    4412             : 
    4413             :     /*
    4414             :      * Apply the comparison operator to each pair of array elements.
    4415             :      */
    4416       22452 :     InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
    4417             :                              collation, NULL, NULL);
    4418             : 
    4419             :     /* Loop over source data */
    4420       22452 :     nelems1 = ArrayGetNItems(AARR_NDIM(array1), AARR_DIMS(array1));
    4421       22452 :     array_iter_setup(&it1, array1);
    4422             : 
    4423      420322 :     for (i = 0; i < nelems1; i++)
    4424             :     {
    4425             :         Datum       elt1;
    4426             :         bool        isnull1;
    4427             : 
    4428             :         /* Get element, checking for NULL */
    4429      407316 :         elt1 = array_iter_next(&it1, &isnull1, i, typlen, typbyval, typalign);
    4430             : 
    4431             :         /*
    4432             :          * We assume that the comparison operator is strict, so a NULL can't
    4433             :          * match anything.  XXX this diverges from the "NULL=NULL" behavior of
    4434             :          * array_eq, should we act like that?
    4435             :          */
    4436      407316 :         if (isnull1)
    4437             :         {
    4438        1320 :             if (matchall)
    4439             :             {
    4440        1260 :                 result = false;
    4441        9446 :                 break;
    4442             :             }
    4443          60 :             continue;
    4444             :         }
    4445             : 
    4446    18416352 :         for (j = 0; j < nelems2; j++)
    4447             :         {
    4448    18376948 :             Datum       elt2 = values2[j];
    4449    18376948 :             bool        isnull2 = nulls2 ? nulls2[j] : false;
    4450             :             bool        oprresult;
    4451             : 
    4452    18376948 :             if (isnull2)
    4453        7212 :                 continue;       /* can't match */
    4454             : 
    4455             :             /*
    4456             :              * Apply the operator to the element pair; treat NULL as false
    4457             :              */
    4458    18369736 :             locfcinfo->args[0].value = elt1;
    4459    18369736 :             locfcinfo->args[0].isnull = false;
    4460    18369736 :             locfcinfo->args[1].value = elt2;
    4461    18369736 :             locfcinfo->args[1].isnull = false;
    4462    18369736 :             locfcinfo->isnull = false;
    4463    18369736 :             oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
    4464    18369736 :             if (!locfcinfo->isnull && oprresult)
    4465      366592 :                 break;
    4466             :         }
    4467             : 
    4468      405996 :         if (j < nelems2)
    4469             :         {
    4470             :             /* found a match for elt1 */
    4471      366592 :             if (!matchall)
    4472             :             {
    4473         228 :                 result = true;
    4474         228 :                 break;
    4475             :             }
    4476             :         }
    4477             :         else
    4478             :         {
    4479             :             /* no match for elt1 */
    4480       39404 :             if (matchall)
    4481             :             {
    4482        7958 :                 result = false;
    4483        7958 :                 break;
    4484             :             }
    4485             :         }
    4486             :     }
    4487             : 
    4488       22452 :     return result;
    4489             : }
    4490             : 
    4491             : Datum
    4492        6120 : arrayoverlap(PG_FUNCTION_ARGS)
    4493             : {
    4494        6120 :     AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
    4495        6120 :     AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
    4496        6120 :     Oid         collation = PG_GET_COLLATION();
    4497             :     bool        result;
    4498             : 
    4499        6120 :     result = array_contain_compare(array1, array2, collation, false,
    4500        6120 :                                    &fcinfo->flinfo->fn_extra);
    4501             : 
    4502             :     /* Avoid leaking memory when handed toasted input. */
    4503        6120 :     AARR_FREE_IF_COPY(array1, 0);
    4504        6120 :     AARR_FREE_IF_COPY(array2, 1);
    4505             : 
    4506        6120 :     PG_RETURN_BOOL(result);
    4507             : }
    4508             : 
    4509             : Datum
    4510        9816 : arraycontains(PG_FUNCTION_ARGS)
    4511             : {
    4512        9816 :     AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
    4513        9816 :     AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
    4514        9816 :     Oid         collation = PG_GET_COLLATION();
    4515             :     bool        result;
    4516             : 
    4517        9816 :     result = array_contain_compare(array2, array1, collation, true,
    4518        9816 :                                    &fcinfo->flinfo->fn_extra);
    4519             : 
    4520             :     /* Avoid leaking memory when handed toasted input. */
    4521        9816 :     AARR_FREE_IF_COPY(array1, 0);
    4522        9816 :     AARR_FREE_IF_COPY(array2, 1);
    4523             : 
    4524        9816 :     PG_RETURN_BOOL(result);
    4525             : }
    4526             : 
    4527             : Datum
    4528        6516 : arraycontained(PG_FUNCTION_ARGS)
    4529             : {
    4530        6516 :     AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
    4531        6516 :     AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
    4532        6516 :     Oid         collation = PG_GET_COLLATION();
    4533             :     bool        result;
    4534             : 
    4535        6516 :     result = array_contain_compare(array1, array2, collation, true,
    4536        6516 :                                    &fcinfo->flinfo->fn_extra);
    4537             : 
    4538             :     /* Avoid leaking memory when handed toasted input. */
    4539        6516 :     AARR_FREE_IF_COPY(array1, 0);
    4540        6516 :     AARR_FREE_IF_COPY(array2, 1);
    4541             : 
    4542        6516 :     PG_RETURN_BOOL(result);
    4543             : }
    4544             : 
    4545             : 
    4546             : /*-----------------------------------------------------------------------------
    4547             :  * Array iteration functions
    4548             :  *      These functions are used to iterate efficiently through arrays
    4549             :  *-----------------------------------------------------------------------------
    4550             :  */
    4551             : 
    4552             : /*
    4553             :  * array_create_iterator --- set up to iterate through an array
    4554             :  *
    4555             :  * If slice_ndim is zero, we will iterate element-by-element; the returned
    4556             :  * datums are of the array's element type.
    4557             :  *
    4558             :  * If slice_ndim is 1..ARR_NDIM(arr), we will iterate by slices: the
    4559             :  * returned datums are of the same array type as 'arr', but of size
    4560             :  * equal to the rightmost N dimensions of 'arr'.
    4561             :  *
    4562             :  * The passed-in array must remain valid for the lifetime of the iterator.
    4563             :  */
    4564             : ArrayIterator
    4565         330 : array_create_iterator(ArrayType *arr, int slice_ndim, ArrayMetaState *mstate)
    4566             : {
    4567         330 :     ArrayIterator iterator = palloc0(sizeof(ArrayIteratorData));
    4568             : 
    4569             :     /*
    4570             :      * Sanity-check inputs --- caller should have got this right already
    4571             :      */
    4572             :     Assert(PointerIsValid(arr));
    4573         330 :     if (slice_ndim < 0 || slice_ndim > ARR_NDIM(arr))
    4574           0 :         elog(ERROR, "invalid arguments to array_create_iterator");
    4575             : 
    4576             :     /*
    4577             :      * Remember basic info about the array and its element type
    4578             :      */
    4579         330 :     iterator->arr = arr;
    4580         330 :     iterator->nullbitmap = ARR_NULLBITMAP(arr);
    4581         330 :     iterator->nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
    4582             : 
    4583         330 :     if (mstate != NULL)
    4584             :     {
    4585             :         Assert(mstate->element_type == ARR_ELEMTYPE(arr));
    4586             : 
    4587         258 :         iterator->typlen = mstate->typlen;
    4588         258 :         iterator->typbyval = mstate->typbyval;
    4589         258 :         iterator->typalign = mstate->typalign;
    4590             :     }
    4591             :     else
    4592          72 :         get_typlenbyvalalign(ARR_ELEMTYPE(arr),
    4593             :                              &iterator->typlen,
    4594             :                              &iterator->typbyval,
    4595             :                              &iterator->typalign);
    4596             : 
    4597             :     /*
    4598             :      * Remember the slicing parameters.
    4599             :      */
    4600         330 :     iterator->slice_ndim = slice_ndim;
    4601             : 
    4602         330 :     if (slice_ndim > 0)
    4603             :     {
    4604             :         /*
    4605             :          * Get pointers into the array's dims and lbound arrays to represent
    4606             :          * the dims/lbound arrays of a slice.  These are the same as the
    4607             :          * rightmost N dimensions of the array.
    4608             :          */
    4609          36 :         iterator->slice_dims = ARR_DIMS(arr) + ARR_NDIM(arr) - slice_ndim;
    4610          36 :         iterator->slice_lbound = ARR_LBOUND(arr) + ARR_NDIM(arr) - slice_ndim;
    4611             : 
    4612             :         /*
    4613             :          * Compute number of elements in a slice.
    4614             :          */
    4615          72 :         iterator->slice_len = ArrayGetNItems(slice_ndim,
    4616          36 :                                              iterator->slice_dims);
    4617             : 
    4618             :         /*
    4619             :          * Create workspace for building sub-arrays.
    4620             :          */
    4621          36 :         iterator->slice_values = (Datum *)
    4622          36 :             palloc(iterator->slice_len * sizeof(Datum));
    4623          36 :         iterator->slice_nulls = (bool *)
    4624          36 :             palloc(iterator->slice_len * sizeof(bool));
    4625             :     }
    4626             : 
    4627             :     /*
    4628             :      * Initialize our data pointer and linear element number.  These will
    4629             :      * advance through the array during array_iterate().
    4630             :      */
    4631         330 :     iterator->data_ptr = ARR_DATA_PTR(arr);
    4632         330 :     iterator->current_item = 0;
    4633             : 
    4634         330 :     return iterator;
    4635             : }
    4636             : 
    4637             : /*
    4638             :  * Iterate through the array referenced by 'iterator'.
    4639             :  *
    4640             :  * As long as there is another element (or slice), return it into
    4641             :  * *value / *isnull, and return true.  Return false when no more data.
    4642             :  */
    4643             : bool
    4644        1692 : array_iterate(ArrayIterator iterator, Datum *value, bool *isnull)
    4645             : {
    4646             :     /* Done if we have reached the end of the array */
    4647        1692 :     if (iterator->current_item >= iterator->nitems)
    4648         150 :         return false;
    4649             : 
    4650        1542 :     if (iterator->slice_ndim == 0)
    4651             :     {
    4652             :         /*
    4653             :          * Scalar case: return one element.
    4654             :          */
    4655        1488 :         if (array_get_isnull(iterator->nullbitmap, iterator->current_item++))
    4656             :         {
    4657          24 :             *isnull = true;
    4658          24 :             *value = (Datum) 0;
    4659             :         }
    4660             :         else
    4661             :         {
    4662             :             /* non-NULL, so fetch the individual Datum to return */
    4663        1464 :             char       *p = iterator->data_ptr;
    4664             : 
    4665        1464 :             *isnull = false;
    4666        1464 :             *value = fetch_att(p, iterator->typbyval, iterator->typlen);
    4667             : 
    4668             :             /* Move our data pointer forward to the next element */
    4669        1464 :             p = att_addlength_pointer(p, iterator->typlen, p);
    4670        1464 :             p = (char *) att_align_nominal(p, iterator->typalign);
    4671        1464 :             iterator->data_ptr = p;
    4672             :         }
    4673             :     }
    4674             :     else
    4675             :     {
    4676             :         /*
    4677             :          * Slice case: build and return an array of the requested size.
    4678             :          */
    4679             :         ArrayType  *result;
    4680          54 :         Datum      *values = iterator->slice_values;
    4681          54 :         bool       *nulls = iterator->slice_nulls;
    4682          54 :         char       *p = iterator->data_ptr;
    4683             :         int         i;
    4684             : 
    4685         192 :         for (i = 0; i < iterator->slice_len; i++)
    4686             :         {
    4687         138 :             if (array_get_isnull(iterator->nullbitmap,
    4688         138 :                                  iterator->current_item++))
    4689             :             {
    4690           0 :                 nulls[i] = true;
    4691           0 :                 values[i] = (Datum) 0;
    4692             :             }
    4693             :             else
    4694             :             {
    4695         138 :                 nulls[i] = false;
    4696         138 :                 values[i] = fetch_att(p, iterator->typbyval, iterator->typlen);
    4697             : 
    4698             :                 /* Move our data pointer forward to the next element */
    4699         138 :                 p = att_addlength_pointer(p, iterator->typlen, p);
    4700         138 :                 p = (char *) att_align_nominal(p, iterator->typalign);
    4701             :             }
    4702             :         }
    4703             : 
    4704          54 :         iterator->data_ptr = p;
    4705             : 
    4706          54 :         result = construct_md_array(values,
    4707             :                                     nulls,
    4708             :                                     iterator->slice_ndim,
    4709             :                                     iterator->slice_dims,
    4710             :                                     iterator->slice_lbound,
    4711          54 :                                     ARR_ELEMTYPE(iterator->arr),
    4712          54 :                                     iterator->typlen,
    4713          54 :                                     iterator->typbyval,
    4714          54 :                                     iterator->typalign);
    4715             : 
    4716          54 :         *isnull = false;
    4717          54 :         *value = PointerGetDatum(result);
    4718             :     }
    4719             : 
    4720        1542 :     return true;
    4721             : }
    4722             : 
    4723             : /*
    4724             :  * Release an ArrayIterator data structure
    4725             :  */
    4726             : void
    4727         258 : array_free_iterator(ArrayIterator iterator)
    4728             : {
    4729         258 :     if (iterator->slice_ndim > 0)
    4730             :     {
    4731           0 :         pfree(iterator->slice_values);
    4732           0 :         pfree(iterator->slice_nulls);
    4733             :     }
    4734         258 :     pfree(iterator);
    4735         258 : }
    4736             : 
    4737             : 
    4738             : /***************************************************************************/
    4739             : /******************|          Support  Routines           |*****************/
    4740             : /***************************************************************************/
    4741             : 
    4742             : /*
    4743             :  * Check whether a specific array element is NULL
    4744             :  *
    4745             :  * nullbitmap: pointer to array's null bitmap (NULL if none)
    4746             :  * offset: 0-based linear element number of array element
    4747             :  */
    4748             : static bool
    4749      647768 : array_get_isnull(const bits8 *nullbitmap, int offset)
    4750             : {
    4751      647768 :     if (nullbitmap == NULL)
    4752      647242 :         return false;           /* assume not null */
    4753         526 :     if (nullbitmap[offset / 8] & (1 << (offset % 8)))
    4754         412 :         return false;           /* not null */
    4755         114 :     return true;
    4756             : }
    4757             : 
    4758             : /*
    4759             :  * Set a specific array element's null-bitmap entry
    4760             :  *
    4761             :  * nullbitmap: pointer to array's null bitmap (mustn't be NULL)
    4762             :  * offset: 0-based linear element number of array element
    4763             :  * isNull: null status to set
    4764             :  */
    4765             : static void
    4766         134 : array_set_isnull(bits8 *nullbitmap, int offset, bool isNull)
    4767             : {
    4768             :     int         bitmask;
    4769             : 
    4770         134 :     nullbitmap += offset / 8;
    4771         134 :     bitmask = 1 << (offset % 8);
    4772         134 :     if (isNull)
    4773          20 :         *nullbitmap &= ~bitmask;
    4774             :     else
    4775         114 :         *nullbitmap |= bitmask;
    4776         134 : }
    4777             : 
    4778             : /*
    4779             :  * Fetch array element at pointer, converted correctly to a Datum
    4780             :  *
    4781             :  * Caller must have handled case of NULL element
    4782             :  */
    4783             : static Datum
    4784      645504 : ArrayCast(char *value, bool byval, int len)
    4785             : {
    4786      645504 :     return fetch_att(value, byval, len);
    4787             : }
    4788             : 
    4789             : /*
    4790             :  * Copy datum to *dest and return total space used (including align padding)
    4791             :  *
    4792             :  * Caller must have handled case of NULL element
    4793             :  */
    4794             : static int
    4795    17432808 : ArrayCastAndSet(Datum src,
    4796             :                 int typlen,
    4797             :                 bool typbyval,
    4798             :                 char typalign,
    4799             :                 char *dest)
    4800             : {
    4801             :     int         inc;
    4802             : 
    4803    17432808 :     if (typlen > 0)
    4804             :     {
    4805    13002576 :         if (typbyval)
    4806    11371622 :             store_att_byval(dest, src, typlen);
    4807             :         else
    4808     1630954 :             memmove(dest, DatumGetPointer(src), typlen);
    4809    13002576 :         inc = att_align_nominal(typlen, typalign);
    4810             :     }
    4811             :     else
    4812             :     {
    4813             :         Assert(!typbyval);
    4814     4430232 :         inc = att_addlength_datum(0, typlen, src);
    4815     4430232 :         memmove(dest, DatumGetPointer(src), inc);
    4816     4430232 :         inc = att_align_nominal(inc, typalign);
    4817             :     }
    4818             : 
    4819    17432808 :     return inc;
    4820             : }
    4821             : 
    4822             : /*
    4823             :  * Advance ptr over nitems array elements
    4824             :  *
    4825             :  * ptr: starting location in array
    4826             :  * offset: 0-based linear element number of first element (the one at *ptr)
    4827             :  * nullbitmap: start of array's null bitmap, or NULL if none
    4828             :  * nitems: number of array elements to advance over (>= 0)
    4829             :  * typlen, typbyval, typalign: storage parameters of array element datatype
    4830             :  *
    4831             :  * It is caller's responsibility to ensure that nitems is within range
    4832             :  */
    4833             : static char *
    4834      647840 : array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
    4835             :            int typlen, bool typbyval, char typalign)
    4836             : {
    4837             :     int         bitmask;
    4838             :     int         i;
    4839             : 
    4840             :     /* easy if fixed-size elements and no NULLs */
    4841      647840 :     if (typlen > 0 && !nullbitmap)
    4842      393120 :         return ptr + nitems * ((Size) att_align_nominal(typlen, typalign));
    4843             : 
    4844             :     /* seems worth having separate loops for NULL and no-NULLs cases */
    4845      254720 :     if (nullbitmap)
    4846             :     {
    4847         604 :         nullbitmap += offset / 8;
    4848         604 :         bitmask = 1 << (offset % 8);
    4849             : 
    4850        1852 :         for (i = 0; i < nitems; i++)
    4851             :         {
    4852        1248 :             if (*nullbitmap & bitmask)
    4853             :             {
    4854         888 :                 ptr = att_addlength_pointer(ptr, typlen, ptr);
    4855         888 :                 ptr = (char *) att_align_nominal(ptr, typalign);
    4856             :             }
    4857        1248 :             bitmask <<= 1;
    4858        1248 :             if (bitmask == 0x100)
    4859             :             {
    4860          48 :                 nullbitmap++;
    4861          48 :                 bitmask = 1;
    4862             :             }
    4863             :         }
    4864             :     }
    4865             :     else
    4866             :     {
    4867     1158788 :         for (i = 0; i < nitems; i++)
    4868             :         {
    4869      904672 :             ptr = att_addlength_pointer(ptr, typlen, ptr);
    4870      904672 :             ptr = (char *) att_align_nominal(ptr, typalign);
    4871             :         }
    4872             :     }
    4873      254720 :     return ptr;
    4874             : }
    4875             : 
    4876             : /*
    4877             :  * Compute total size of the nitems array elements starting at *ptr
    4878             :  *
    4879             :  * Parameters same as for array_seek
    4880             :  */
    4881             : static int
    4882        1578 : array_nelems_size(char *ptr, int offset, bits8 *nullbitmap, int nitems,
    4883             :                   int typlen, bool typbyval, char typalign)
    4884             : {
    4885        1578 :     return array_seek(ptr, offset, nullbitmap, nitems,
    4886        1578 :                       typlen, typbyval, typalign) - ptr;
    4887             : }
    4888             : 
    4889             : /*
    4890             :  * Copy nitems array elements from srcptr to destptr
    4891             :  *
    4892             :  * destptr: starting destination location (must be enough room!)
    4893             :  * nitems: number of array elements to copy (>= 0)
    4894             :  * srcptr: starting location in source array
    4895             :  * offset: 0-based linear element number of first element (the one at *srcptr)
    4896             :  * nullbitmap: start of source array's null bitmap, or NULL if none
    4897             :  * typlen, typbyval, typalign: storage parameters of array element datatype
    4898             :  *
    4899             :  * Returns number of bytes copied
    4900             :  *
    4901             :  * NB: this does not take care of setting up the destination's null bitmap!
    4902             :  */
    4903             : static int
    4904        1110 : array_copy(char *destptr, int nitems,
    4905             :            char *srcptr, int offset, bits8 *nullbitmap,
    4906             :            int typlen, bool typbyval, char typalign)
    4907             : {
    4908             :     int         numbytes;
    4909             : 
    4910        1110 :     numbytes = array_nelems_size(srcptr, offset, nullbitmap, nitems,
    4911             :                                  typlen, typbyval, typalign);
    4912        1110 :     memcpy(destptr, srcptr, numbytes);
    4913        1110 :     return numbytes;
    4914             : }
    4915             : 
    4916             : /*
    4917             :  * Copy nitems null-bitmap bits from source to destination
    4918             :  *
    4919             :  * destbitmap: start of destination array's null bitmap (mustn't be NULL)
    4920             :  * destoffset: 0-based linear element number of first dest element
    4921             :  * srcbitmap: start of source array's null bitmap, or NULL if none
    4922             :  * srcoffset: 0-based linear element number of first source element
    4923             :  * nitems: number of bits to copy (>= 0)
    4924             :  *
    4925             :  * If srcbitmap is NULL then we assume the source is all-non-NULL and
    4926             :  * fill 1's into the destination bitmap.  Note that only the specified
    4927             :  * bits in the destination map are changed, not any before or after.
    4928             :  *
    4929             :  * Note: this could certainly be optimized using standard bitblt methods.
    4930             :  * However, it's not clear that the typical Postgres array has enough elements
    4931             :  * to make it worth worrying too much.  For the moment, KISS.
    4932             :  */
    4933             : void
    4934       30852 : array_bitmap_copy(bits8 *destbitmap, int destoffset,
    4935             :                   const bits8 *srcbitmap, int srcoffset,
    4936             :                   int nitems)
    4937             : {
    4938             :     int         destbitmask,
    4939             :                 destbitval,
    4940             :                 srcbitmask,
    4941             :                 srcbitval;
    4942             : 
    4943             :     Assert(destbitmap);
    4944       30852 :     if (nitems <= 0)
    4945         156 :         return;                 /* don't risk fetch off end of memory */
    4946       30696 :     destbitmap += destoffset / 8;
    4947       30696 :     destbitmask = 1 << (destoffset % 8);
    4948       30696 :     destbitval = *destbitmap;
    4949       30696 :     if (srcbitmap)
    4950             :     {
    4951       15626 :         srcbitmap += srcoffset / 8;
    4952       15626 :         srcbitmask = 1 << (srcoffset % 8);
    4953       15626 :         srcbitval = *srcbitmap;
    4954       66804 :         while (nitems-- > 0)
    4955             :         {
    4956       51178 :             if (srcbitval & srcbitmask)
    4957       18422 :                 destbitval |= destbitmask;
    4958             :             else
    4959       32756 :                 destbitval &= ~destbitmask;
    4960       51178 :             destbitmask <<= 1;
    4961       51178 :             if (destbitmask == 0x100)
    4962             :             {
    4963        5870 :                 *destbitmap++ = destbitval;
    4964        5870 :                 destbitmask = 1;
    4965        5870 :                 if (nitems > 0)
    4966        4346 :                     destbitval = *destbitmap;
    4967             :             }
    4968       51178 :             srcbitmask <<= 1;
    4969       51178 :             if (srcbitmask == 0x100)
    4970             :             {
    4971        4344 :                 srcbitmap++;
    4972        4344 :                 srcbitmask = 1;
    4973        4344 :                 if (nitems > 0)
    4974        4308 :                     srcbitval = *srcbitmap;
    4975             :             }
    4976             :         }
    4977       15626 :         if (destbitmask != 1)
    4978       14102 :             *destbitmap = destbitval;
    4979             :     }
    4980             :     else
    4981             :     {
    4982       30264 :         while (nitems-- > 0)
    4983             :         {
    4984       15194 :             destbitval |= destbitmask;
    4985       15194 :             destbitmask <<= 1;
    4986       15194 :             if (destbitmask == 0x100)
    4987             :             {
    4988        2232 :                 *destbitmap++ = destbitval;
    4989        2232 :                 destbitmask = 1;
    4990        2232 :                 if (nitems > 0)
    4991           0 :                     destbitval = *destbitmap;
    4992             :             }
    4993             :         }
    4994       15070 :         if (destbitmask != 1)
    4995       12838 :             *destbitmap = destbitval;
    4996             :     }
    4997             : }
    4998             : 
    4999             : /*
    5000             :  * Compute space needed for a slice of an array
    5001             :  *
    5002             :  * We assume the caller has verified that the slice coordinates are valid.
    5003             :  */
    5004             : static int
    5005         282 : array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
    5006             :                  int ndim, int *dim, int *lb,
    5007             :                  int *st, int *endp,
    5008             :                  int typlen, bool typbyval, char typalign)
    5009             : {
    5010             :     int         src_offset,
    5011             :                 span[MAXDIM],
    5012             :                 prod[MAXDIM],
    5013             :                 dist[MAXDIM],
    5014             :                 indx[MAXDIM];
    5015             :     char       *ptr;
    5016             :     int         i,
    5017             :                 j,
    5018             :                 inc;
    5019         282 :     int         count = 0;
    5020             : 
    5021         282 :     mda_get_range(ndim, span, st, endp);
    5022             : 
    5023             :     /* Pretty easy for fixed element length without nulls ... */
    5024         282 :     if (typlen > 0 && !arraynullsptr)
    5025         204 :         return ArrayGetNItems(ndim, span) * att_align_nominal(typlen, typalign);
    5026             : 
    5027             :     /* Else gotta do it the hard way */
    5028          78 :     src_offset = ArrayGetOffset(ndim, dim, lb, st);
    5029          78 :     ptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
    5030             :                      typlen, typbyval, typalign);
    5031          78 :     mda_get_prod(ndim, dim, prod);
    5032          78 :     mda_get_offset_values(ndim, dist, prod, span);
    5033         198 :     for (i = 0; i < ndim; i++)
    5034         120 :         indx[i] = 0;
    5035          78 :     j = ndim - 1;
    5036             :     do
    5037             :     {
    5038         258 :         if (dist[j])
    5039             :         {
    5040           0 :             ptr = array_seek(ptr, src_offset, arraynullsptr, dist[j],
    5041             :                              typlen, typbyval, typalign);
    5042           0 :             src_offset += dist[j];
    5043             :         }
    5044         258 :         if (!array_get_isnull(arraynullsptr, src_offset))
    5045             :         {
    5046         246 :             inc = att_addlength_pointer(0, typlen, ptr);
    5047         246 :             inc = att_align_nominal(inc, typalign);
    5048         246 :             ptr += inc;
    5049         246 :             count += inc;
    5050             :         }
    5051         258 :         src_offset++;
    5052         258 :     } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
    5053          78 :     return count;
    5054             : }
    5055             : 
    5056             : /*
    5057             :  * Extract a slice of an array into consecutive elements in the destination
    5058             :  * array.
    5059             :  *
    5060             :  * We assume the caller has verified that the slice coordinates are valid,
    5061             :  * allocated enough storage for the result, and initialized the header
    5062             :  * of the new array.
    5063             :  */
    5064             : static void
    5065         252 : array_extract_slice(ArrayType *newarray,
    5066             :                     int ndim,
    5067             :                     int *dim,
    5068             :                     int *lb,
    5069             :                     char *arraydataptr,
    5070             :                     bits8 *arraynullsptr,
    5071             :                     int *st,
    5072             :                     int *endp,
    5073             :                     int typlen,
    5074             :                     bool typbyval,
    5075             :                     char typalign)
    5076             : {
    5077         252 :     char       *destdataptr = ARR_DATA_PTR(newarray);
    5078         252 :     bits8      *destnullsptr = ARR_NULLBITMAP(newarray);
    5079             :     char       *srcdataptr;
    5080             :     int         src_offset,
    5081             :                 dest_offset,
    5082             :                 prod[MAXDIM],
    5083             :                 span[MAXDIM],
    5084             :                 dist[MAXDIM],
    5085             :                 indx[MAXDIM];
    5086             :     int         i,
    5087             :                 j,
    5088             :                 inc;
    5089             : 
    5090         252 :     src_offset = ArrayGetOffset(ndim, dim, lb, st);
    5091         252 :     srcdataptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
    5092             :                             typlen, typbyval, typalign);
    5093         252 :     mda_get_prod(ndim, dim, prod);
    5094         252 :     mda_get_range(ndim, span, st, endp);
    5095         252 :     mda_get_offset_values(ndim, dist, prod, span);
    5096         636 :     for (i = 0; i < ndim; i++)
    5097         384 :         indx[i] = 0;
    5098         252 :     dest_offset = 0;
    5099         252 :     j = ndim - 1;
    5100             :     do
    5101             :     {
    5102         954 :         if (dist[j])
    5103             :         {
    5104             :             /* skip unwanted elements */
    5105          24 :             srcdataptr = array_seek(srcdataptr, src_offset, arraynullsptr,
    5106             :                                     dist[j],
    5107             :                                     typlen, typbyval, typalign);
    5108          24 :             src_offset += dist[j];
    5109             :         }
    5110         954 :         inc = array_copy(destdataptr, 1,
    5111             :                          srcdataptr, src_offset, arraynullsptr,
    5112             :                          typlen, typbyval, typalign);
    5113         954 :         if (destnullsptr)
    5114         180 :             array_bitmap_copy(destnullsptr, dest_offset,
    5115             :                               arraynullsptr, src_offset,
    5116             :                               1);
    5117         954 :         destdataptr += inc;
    5118         954 :         srcdataptr += inc;
    5119         954 :         src_offset++;
    5120         954 :         dest_offset++;
    5121         954 :     } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
    5122         252 : }
    5123             : 
    5124             : /*
    5125             :  * Insert a slice into an array.
    5126             :  *
    5127             :  * ndim/dim[]/lb[] are dimensions of the original array.  A new array with
    5128             :  * those same dimensions is to be constructed.  destArray must already
    5129             :  * have been allocated and its header initialized.
    5130             :  *
    5131             :  * st[]/endp[] identify the slice to be replaced.  Elements within the slice
    5132             :  * volume are taken from consecutive elements of the srcArray; elements
    5133             :  * outside it are copied from origArray.
    5134             :  *
    5135             :  * We assume the caller has verified that the slice coordinates are valid.
    5136             :  */
    5137             : static void
    5138          30 : array_insert_slice(ArrayType *destArray,
    5139             :                    ArrayType *origArray,
    5140             :                    ArrayType *srcArray,
    5141             :                    int ndim,
    5142             :                    int *dim,
    5143             :                    int *lb,
    5144             :                    int *st,
    5145             :                    int *endp,
    5146             :                    int typlen,
    5147             :                    bool typbyval,
    5148             :                    char typalign)
    5149             : {
    5150          30 :     char       *destPtr = ARR_DATA_PTR(destArray);
    5151          30 :     char       *origPtr = ARR_DATA_PTR(origArray);
    5152          30 :     char       *srcPtr = ARR_DATA_PTR(srcArray);
    5153          30 :     bits8      *destBitmap = ARR_NULLBITMAP(destArray);
    5154          30 :     bits8      *origBitmap = ARR_NULLBITMAP(origArray);
    5155          30 :     bits8      *srcBitmap = ARR_NULLBITMAP(srcArray);
    5156          30 :     int         orignitems = ArrayGetNItems(ARR_NDIM(origArray),
    5157             :                                             ARR_DIMS(origArray));
    5158             :     int         dest_offset,
    5159             :                 orig_offset,
    5160             :                 src_offset,
    5161             :                 prod[MAXDIM],
    5162             :                 span[MAXDIM],
    5163             :                 dist[MAXDIM],
    5164             :                 indx[MAXDIM];
    5165             :     int         i,
    5166             :                 j,
    5167             :                 inc;
    5168             : 
    5169          30 :     dest_offset = ArrayGetOffset(ndim, dim, lb, st);
    5170             :     /* copy items before the slice start */
    5171          30 :     inc = array_copy(destPtr, dest_offset,
    5172             :                      origPtr, 0, origBitmap,
    5173             :                      typlen, typbyval, typalign);
    5174          30 :     destPtr += inc;
    5175          30 :     origPtr += inc;
    5176          30 :     if (destBitmap)
    5177           0 :         array_bitmap_copy(destBitmap, 0, origBitmap, 0, dest_offset);
    5178          30 :     orig_offset = dest_offset;
    5179          30 :     mda_get_prod(ndim, dim, prod);
    5180          30 :     mda_get_range(ndim, span, st, endp);
    5181          30 :     mda_get_offset_values(ndim, dist, prod, span);
    5182         102 :     for (i = 0; i < ndim; i++)
    5183          72 :         indx[i] = 0;
    5184          30 :     src_offset = 0;
    5185          30 :     j = ndim - 1;
    5186             :     do
    5187             :     {
    5188             :         /* Copy/advance over elements between here and next part of slice */
    5189          78 :         if (dist[j])
    5190             :         {
    5191          18 :             inc = array_copy(destPtr, dist[j],
    5192             :                              origPtr, orig_offset, origBitmap,
    5193             :                              typlen, typbyval, typalign);
    5194          18 :             destPtr += inc;
    5195          18 :             origPtr += inc;
    5196          18 :             if (destBitmap)
    5197           0 :                 array_bitmap_copy(destBitmap, dest_offset,
    5198             :                                   origBitmap, orig_offset,
    5199             :                                   dist[j]);
    5200          18 :             dest_offset += dist[j];
    5201          18 :             orig_offset += dist[j];
    5202             :         }
    5203             :         /* Copy new element at this slice position */
    5204          78 :         inc = array_copy(destPtr, 1,
    5205             :                          srcPtr, src_offset, srcBitmap,
    5206             :                          typlen, typbyval, typalign);
    5207          78 :         if (destBitmap)
    5208           0 :             array_bitmap_copy(destBitmap, dest_offset,
    5209             :                               srcBitmap, src_offset,
    5210             :                               1);
    5211          78 :         destPtr += inc;
    5212          78 :         srcPtr += inc;
    5213          78 :         dest_offset++;
    5214          78 :         src_offset++;
    5215             :         /* Advance over old element at this slice position */
    5216          78 :         origPtr = array_seek(origPtr, orig_offset, origBitmap, 1,
    5217             :                              typlen, typbyval, typalign);
    5218          78 :         orig_offset++;
    5219          78 :     } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
    5220             : 
    5221             :     /* don't miss any data at the end */
    5222          30 :     array_copy(destPtr, orignitems - orig_offset,
    5223             :                origPtr, orig_offset, origBitmap,
    5224             :                typlen, typbyval, typalign);
    5225          30 :     if (destBitmap)
    5226           0 :         array_bitmap_copy(destBitmap, dest_offset,
    5227             :                           origBitmap, orig_offset,
    5228             :                           orignitems - orig_offset);
    5229          30 : }
    5230             : 
    5231             : /*
    5232             :  * initArrayResult - initialize an empty ArrayBuildState
    5233             :  *
    5234             :  *  element_type is the array element type (must be a valid array element type)
    5235             :  *  rcontext is where to keep working state
    5236             :  *  subcontext is a flag determining whether to use a separate memory context
    5237             :  *
    5238             :  * Note: there are two common schemes for using accumArrayResult().
    5239             :  * In the older scheme, you start with a NULL ArrayBuildState pointer, and
    5240             :  * call accumArrayResult once per element.  In this scheme you end up with
    5241             :  * a NULL pointer if there were no elements, which you need to special-case.
    5242             :  * In the newer scheme, call initArrayResult and then call accumArrayResult
    5243             :  * once per element.  In this scheme you always end with a non-NULL pointer
    5244             :  * that you can pass to makeArrayResult; you get an empty array if there
    5245             :  * were no elements.  This is preferred if an empty array is what you want.
    5246             :  *
    5247             :  * It's possible to choose whether to create a separate memory context for the
    5248             :  * array build state, or whether to allocate it directly within rcontext.
    5249             :  *
    5250             :  * When there are many concurrent small states (e.g. array_agg() using hash
    5251             :  * aggregation of many small groups), using a separate memory context for each
    5252             :  * one may result in severe memory bloat. In such cases, use the same memory
    5253             :  * context to initialize all such array build states, and pass
    5254             :  * subcontext=false.
    5255             :  *
    5256             :  * In cases when the array build states have different lifetimes, using a
    5257             :  * single memory context is impractical. Instead, pass subcontext=true so that
    5258             :  * the array build states can be freed individually.
    5259             :  */
    5260             : ArrayBuildState *
    5261      347966 : initArrayResult(Oid element_type, MemoryContext rcontext, bool subcontext)
    5262             : {
    5263             :     /*
    5264             :      * When using a subcontext, we can afford to start with a somewhat larger
    5265             :      * initial array size.  Without subcontexts, we'd better hope that most of
    5266             :      * the states stay small ...
    5267             :      */
    5268      347966 :     return initArrayResultWithSize(element_type, rcontext, subcontext,
    5269             :                                    subcontext ? 64 : 8);
    5270             : }
    5271             : 
    5272             : /*
    5273             :  * initArrayResultWithSize
    5274             :  *      As initArrayResult, but allow the initial size of the allocated arrays
    5275             :  *      to be specified.
    5276             :  */
    5277             : ArrayBuildState *
    5278      348106 : initArrayResultWithSize(Oid element_type, MemoryContext rcontext,
    5279             :                         bool subcontext, int initsize)
    5280             : {
    5281             :     ArrayBuildState *astate;
    5282      348106 :     MemoryContext arr_context = rcontext;
    5283             : 
    5284             :     /* Make a temporary context to hold all the junk */
    5285      348106 :     if (subcontext)
    5286      247092 :         arr_context = AllocSetContextCreate(rcontext,
    5287             :                                             "accumArrayResult",
    5288             :                                             ALLOCSET_DEFAULT_SIZES);
    5289             : 
    5290             :     astate = (ArrayBuildState *)
    5291      348106 :         MemoryContextAlloc(arr_context, sizeof(ArrayBuildState));
    5292      348106 :     astate->mcontext = arr_context;
    5293      348106 :     astate->private_cxt = subcontext;
    5294      348106 :     astate->alen = initsize;
    5295      348106 :     astate->dvalues = (Datum *)
    5296      348106 :         MemoryContextAlloc(arr_context, astate->alen * sizeof(Datum));
    5297      348106 :     astate->dnulls = (bool *)
    5298      348106 :         MemoryContextAlloc(arr_context, astate->alen * sizeof(bool));
    5299      348106 :     astate->nelems = 0;
    5300      348106 :     astate->element_type = element_type;
    5301      348106 :     get_typlenbyvalalign(element_type,
    5302             :                          &astate->typlen,
    5303             :                          &astate->typbyval,
    5304             :                          &astate->typalign);
    5305             : 
    5306      348106 :     return astate;
    5307             : }
    5308             : 
    5309             : /*
    5310             :  * accumArrayResult - accumulate one (more) Datum for an array result
    5311             :  *
    5312             :  *  astate is working state (can be NULL on first call)
    5313             :  *  dvalue/disnull represent the new Datum to append to the array
    5314             :  *  element_type is the Datum's type (must be a valid array element type)
    5315             :  *  rcontext is where to keep working state
    5316             :  */
    5317             : ArrayBuildState *
    5318     2845472 : accumArrayResult(ArrayBuildState *astate,
    5319             :                  Datum dvalue, bool disnull,
    5320             :                  Oid element_type,
    5321             :                  MemoryContext rcontext)
    5322             : {
    5323             :     MemoryContext oldcontext;
    5324             : 
    5325     2845472 :     if (astate == NULL)
    5326             :     {
    5327             :         /* First time through --- initialize */
    5328      205560 :         astate = initArrayResult(element_type, rcontext, true);
    5329             :     }
    5330             :     else
    5331             :     {
    5332             :         Assert(astate->element_type == element_type);
    5333             :     }
    5334             : 
    5335     2845472 :     oldcontext = MemoryContextSwitchTo(astate->mcontext);
    5336             : 
    5337             :     /* enlarge dvalues[]/dnulls[] if needed */
    5338     2845472 :     if (astate->nelems >= astate->alen)
    5339             :     {
    5340       17852 :         astate->alen *= 2;
    5341       17852 :         astate->dvalues = (Datum *)
    5342       17852 :             repalloc(astate->dvalues, astate->alen * sizeof(Datum));
    5343       17852 :         astate->dnulls = (bool *)
    5344       17852 :             repalloc(astate->dnulls, astate->alen * sizeof(bool));
    5345             :     }
    5346             : 
    5347             :     /*
    5348             :      * Ensure pass-by-ref stuff is copied into mcontext; and detoast it too if
    5349             :      * it's varlena.  (You might think that detoasting is not needed here
    5350             :      * because construct_md_array can detoast the array elements later.
    5351             :      * However, we must not let construct_md_array modify the ArrayBuildState
    5352             :      * because that would mean array_agg_finalfn damages its input, which is
    5353             :      * verboten.  Also, this way frequently saves one copying step.)
    5354             :      */
    5355     2845472 :     if (!disnull && !astate->typbyval)
    5356             :     {
    5357     2517742 :         if (astate->typlen == -1)
    5358     1759870 :             dvalue = PointerGetDatum(PG_DETOAST_DATUM_COPY(dvalue));
    5359             :         else
    5360      757872 :             dvalue = datumCopy(dvalue, astate->typbyval, astate->typlen);
    5361             :     }
    5362             : 
    5363     2845472 :     astate->dvalues[astate->nelems] = dvalue;
    5364     2845472 :     astate->dnulls[astate->nelems] = disnull;
    5365     2845472 :     astate->nelems++;
    5366             : 
    5367     2845472 :     MemoryContextSwitchTo(oldcontext);
    5368             : 
    5369     2845472 :     return astate;
    5370             : }
    5371             : 
    5372             : /*
    5373             :  * makeArrayResult - produce 1-D final result of accumArrayResult
    5374             :  *
    5375             :  * Note: only releases astate if it was initialized within a separate memory
    5376             :  * context (i.e. using subcontext=true when calling initArrayResult).
    5377             :  *
    5378             :  *  astate is working state (must not be NULL)
    5379             :  *  rcontext is where to construct result
    5380             :  */
    5381             : Datum
    5382      205742 : makeArrayResult(ArrayBuildState *astate,
    5383             :                 MemoryContext rcontext)
    5384             : {
    5385             :     int         ndims;
    5386             :     int         dims[1];
    5387             :     int         lbs[1];
    5388             : 
    5389             :     /* If no elements were presented, we want to create an empty array */
    5390      205742 :     ndims = (astate->nelems > 0) ? 1 : 0;
    5391      205742 :     dims[0] = astate->nelems;
    5392      205742 :     lbs[0] = 1;
    5393             : 
    5394      411484 :     return makeMdArrayResult(astate, ndims, dims, lbs, rcontext,
    5395      205742 :                              astate->private_cxt);
    5396             : }
    5397             : 
    5398             : /*
    5399             :  * makeMdArrayResult - produce multi-D final result of accumArrayResult
    5400             :  *
    5401             :  * beware: no check that specified dimensions match the number of values
    5402             :  * accumulated.
    5403             :  *
    5404             :  * Note: if the astate was not initialized within a separate memory context
    5405             :  * (that is, initArrayResult was called with subcontext=false), then using
    5406             :  * release=true is illegal. Instead, release astate along with the rest of its
    5407             :  * context when appropriate.
    5408             :  *
    5409             :  *  astate is working state (must not be NULL)
    5410             :  *  rcontext is where to construct result
    5411             :  *  release is true if okay to release working state
    5412             :  */
    5413             : Datum
    5414      347556 : makeMdArrayResult(ArrayBuildState *astate,
    5415             :                   int ndims,
    5416             :                   int *dims,
    5417             :                   int *lbs,
    5418             :                   MemoryContext rcontext,
    5419             :                   bool release)
    5420             : {
    5421             :     ArrayType  *result;
    5422             :     MemoryContext oldcontext;
    5423             : 
    5424             :     /* Build the final array result in rcontext */
    5425      347556 :     oldcontext = MemoryContextSwitchTo(rcontext);
    5426             : 
    5427      347556 :     result = construct_md_array(astate->dvalues,
    5428             :                                 astate->dnulls,
    5429             :                                 ndims,
    5430             :                                 dims,
    5431             :                                 lbs,
    5432             :                                 astate->element_type,
    5433      347556 :                                 astate->typlen,
    5434      347556 :                                 astate->typbyval,
    5435      347556 :                                 astate->typalign);
    5436             : 
    5437      347556 :     MemoryContextSwitchTo(oldcontext);
    5438             : 
    5439             :     /* Clean up all the junk */
    5440      347556 :     if (release)
    5441             :     {
    5442             :         Assert(astate->private_cxt);
    5443      246788 :         MemoryContextDelete(astate->mcontext);
    5444             :     }
    5445             : 
    5446      347556 :     return PointerGetDatum(result);
    5447             : }
    5448             : 
    5449             : /*
    5450             :  * The following three functions provide essentially the same API as
    5451             :  * initArrayResult/accumArrayResult/makeArrayResult, but instead of accepting
    5452             :  * inputs that are array elements, they accept inputs that are arrays and
    5453             :  * produce an output array having N+1 dimensions.  The inputs must all have
    5454             :  * identical dimensionality as well as element type.
    5455             :  */
    5456             : 
    5457             : /*
    5458             :  * initArrayResultArr - initialize an empty ArrayBuildStateArr
    5459             :  *
    5460             :  *  array_type is the array type (must be a valid varlena array type)
    5461             :  *  element_type is the type of the array's elements (lookup if InvalidOid)
    5462             :  *  rcontext is where to keep working state
    5463             :  *  subcontext is a flag determining whether to use a separate memory context
    5464             :  */
    5465             : ArrayBuildStateArr *
    5466         352 : initArrayResultArr(Oid array_type, Oid element_type, MemoryContext rcontext,
    5467             :                    bool subcontext)
    5468             : {
    5469             :     ArrayBuildStateArr *astate;
    5470         352 :     MemoryContext arr_context = rcontext;   /* by default use the parent ctx */
    5471             : 
    5472             :     /* Lookup element type, unless element_type already provided */
    5473         352 :     if (!OidIsValid(element_type))
    5474             :     {
    5475         272 :         element_type = get_element_type(array_type);
    5476             : 
    5477         272 :         if (!OidIsValid(element_type))
    5478           0 :             ereport(ERROR,
    5479             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
    5480             :                      errmsg("data type %s is not an array type",
    5481             :                             format_type_be(array_type))));
    5482             :     }
    5483             : 
    5484             :     /* Make a temporary context to hold all the junk */
    5485         352 :     if (subcontext)
    5486          12 :         arr_context = AllocSetContextCreate(rcontext,
    5487             :                                             "accumArrayResultArr",
    5488             :                                             ALLOCSET_DEFAULT_SIZES);
    5489             : 
    5490             :     /* Note we initialize all fields to zero */
    5491             :     astate = (ArrayBuildStateArr *)
    5492         352 :         MemoryContextAllocZero(arr_context, sizeof(ArrayBuildStateArr));
    5493         352 :     astate->mcontext = arr_context;
    5494         352 :     astate->private_cxt = subcontext;
    5495             : 
    5496             :     /* Save relevant datatype information */
    5497         352 :     astate->array_type = array_type;
    5498         352 :     astate->element_type = element_type;
    5499             : 
    5500         352 :     return astate;
    5501             : }
    5502             : 
    5503             : /*
    5504             :  * accumArrayResultArr - accumulate one (more) sub-array for an array result
    5505             :  *
    5506             :  *  astate is working state (can be NULL on first call)
    5507             :  *  dvalue/disnull represent the new sub-array to append to the array
    5508             :  *  array_type is the array type (must be a valid varlena array type)
    5509             :  *  rcontext is where to keep working state
    5510             :  */
    5511             : ArrayBuildStateArr *
    5512       60198 : accumArrayResultArr(ArrayBuildStateArr *astate,
    5513             :                     Datum dvalue, bool disnull,
    5514             :                     Oid array_type,
    5515             :                     MemoryContext rcontext)
    5516             : {
    5517             :     ArrayType  *arg;
    5518             :     MemoryContext oldcontext;
    5519             :     int        *dims,
    5520             :                *lbs,
    5521             :                 ndims,
    5522             :                 nitems,
    5523             :                 ndatabytes;
    5524             :     char       *data;
    5525             :     int         i;
    5526             : 
    5527             :     /*
    5528             :      * We disallow accumulating null subarrays.  Another plausible definition
    5529             :      * is to ignore them, but callers that want that can just skip calling
    5530             :      * this function.
    5531             :      */
    5532       60198 :     if (disnull)
    5533           6 :         ereport(ERROR,
    5534             :                 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
    5535             :                  errmsg("cannot accumulate null arrays")));
    5536             : 
    5537             :     /* Detoast input array in caller's context */
    5538       60192 :     arg = DatumGetArrayTypeP(dvalue);
    5539             : 
    5540       60192 :     if (astate == NULL)
    5541           0 :         astate = initArrayResultArr(array_type, InvalidOid, rcontext, true);
    5542             :     else
    5543             :         Assert(astate->array_type == array_type);
    5544             : 
    5545       60192 :     oldcontext = MemoryContextSwitchTo(astate->mcontext);
    5546             : 
    5547             :     /* Collect this input's dimensions */
    5548       60192 :     ndims = ARR_NDIM(arg);
    5549       60192 :     dims = ARR_DIMS(arg);
    5550       60192 :     lbs = ARR_LBOUND(arg);
    5551       60192 :     data = ARR_DATA_PTR(arg);
    5552       60192 :     nitems = ArrayGetNItems(ndims, dims);
    5553       60192 :     ndatabytes = ARR_SIZE(arg) - ARR_DATA_OFFSET(arg);
    5554             : 
    5555       60192 :     if (astate->ndims == 0)
    5556             :     {
    5557             :         /* First input; check/save the dimensionality info */
    5558             : 
    5559             :         /* Should we allow empty inputs and just produce an empty output? */
    5560         206 :         if (ndims == 0)
    5561           6 :             ereport(ERROR,
    5562             :                     (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    5563             :                      errmsg("cannot accumulate empty arrays")));
    5564         200 :         if (ndims + 1 > MAXDIM)
    5565           0 :             ereport(ERROR,
    5566             :                     (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    5567             :                      errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
    5568             :                             ndims + 1, MAXDIM)));
    5569             : 
    5570             :         /*
    5571             :          * The output array will have n+1 dimensions, with the ones after the
    5572             :          * first matching the input's dimensions.
    5573             :          */
    5574         200 :         astate->ndims = ndims + 1;
    5575         200 :         astate->dims[0] = 0;
    5576         200 :         memcpy(&astate->dims[1], dims, ndims * sizeof(int));
    5577         200 :         astate->lbs[0] = 1;
    5578         200 :         memcpy(&astate->lbs[1], lbs, ndims * sizeof(int));
    5579             : 
    5580             :         /* Allocate at least enough data space for this item */
    5581         200 :         astate->abytes = pg_nextpower2_32(Max(1024, ndatabytes + 1));
    5582         200 :         astate->data = (char *) palloc(astate->abytes);
    5583             :     }
    5584             :     else
    5585             :     {
    5586             :         /* Second or later input: must match first input's dimensionality */
    5587       59986 :         if (astate->ndims != ndims + 1)
    5588           0 :             ereport(ERROR,
    5589             :                     (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    5590             :                      errmsg("cannot accumulate arrays of different dimensionality")));
    5591      119966 :         for (i = 0; i < ndims; i++)
    5592             :         {
    5593       59986 :             if (astate->dims[i + 1] != dims[i] || astate->lbs[i + 1] != lbs[i])
    5594           6 :                 ereport(ERROR,
    5595             :                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    5596             :                          errmsg("cannot accumulate arrays of different dimensionality")));
    5597             :         }
    5598             : 
    5599             :         /* Enlarge data space if needed */
    5600       59980 :         if (astate->nbytes + ndatabytes > astate->abytes)
    5601             :         {
    5602          60 :             astate->abytes = Max(astate->abytes * 2,
    5603             :                                  astate->nbytes + ndatabytes);
    5604          60 :             astate->data = (char *) repalloc(astate->data, astate->abytes);
    5605             :         }
    5606             :     }
    5607             : 
    5608             :     /*
    5609             :      * Copy the data portion of the sub-array.  Note we assume that the
    5610             :      * advertised data length of the sub-array is properly aligned.  We do not
    5611             :      * have to worry about detoasting elements since whatever's in the
    5612             :      * sub-array should be OK already.
    5613             :      */
    5614       60180 :     memcpy(astate->data + astate->nbytes, data, ndatabytes);
    5615       60180 :     astate->nbytes += ndatabytes;
    5616             : 
    5617             :     /* Deal with null bitmap if needed */
    5618       60180 :     if (astate->nullbitmap || ARR_HASNULL(arg))
    5619             :     {
    5620       29984 :         int         newnitems = astate->nitems + nitems;
    5621             : 
    5622       29984 :         if (astate->nullbitmap == NULL)
    5623             :         {
    5624             :             /*
    5625             :              * First input with nulls; we must retrospectively handle any
    5626             :              * previous inputs by marking all their items non-null.
    5627             :              */
    5628          76 :             astate->aitems = pg_nextpower2_32(Max(256, newnitems + 1));
    5629          76 :             astate->nullbitmap = (bits8 *) palloc((astate->aitems + 7) / 8);
    5630          76 :             array_bitmap_copy(astate->nullbitmap, 0,
    5631             :                               NULL, 0,
    5632             :                               astate->nitems);
    5633             :         }
    5634       29908 :         else if (newnitems > astate->aitems)
    5635             :         {
    5636          60 :             astate->aitems = Max(astate->aitems * 2, newnitems);
    5637          60 :             astate->nullbitmap = (bits8 *)
    5638          60 :                 repalloc(astate->nullbitmap, (astate->aitems + 7) / 8);
    5639             :         }
    5640       29984 :         array_bitmap_copy(astate->nullbitmap, astate->nitems,
    5641       29984 :                           ARR_NULLBITMAP(arg), 0,
    5642             :                           nitems);
    5643             :     }
    5644             : 
    5645       60180 :     astate->nitems += nitems;
    5646       60180 :     astate->dims[0] += 1;
    5647             : 
    5648       60180 :     MemoryContextSwitchTo(oldcontext);
    5649             : 
    5650             :     /* Release detoasted copy if any */
    5651       60180 :     if ((Pointer) arg != DatumGetPointer(dvalue))
    5652          54 :         pfree(arg);
    5653             : 
    5654       60180 :     return astate;
    5655             : }
    5656             : 
    5657             : /*
    5658             :  * makeArrayResultArr - produce N+1-D final result of accumArrayResultArr
    5659             :  *
    5660             :  *  astate is working state (must not be NULL)
    5661             :  *  rcontext is where to construct result
    5662             :  *  release is true if okay to release working state
    5663             :  */
    5664             : Datum
    5665         174 : makeArrayResultArr(ArrayBuildStateArr *astate,
    5666             :                    MemoryContext rcontext,
    5667             :                    bool release)
    5668             : {
    5669             :     ArrayType  *result;
    5670             :     MemoryContext oldcontext;
    5671             : 
    5672             :     /* Build the final array result in rcontext */
    5673         174 :     oldcontext = MemoryContextSwitchTo(rcontext);
    5674             : 
    5675         174 :     if (astate->ndims == 0)
    5676             :     {
    5677             :         /* No inputs, return empty array */
    5678           0 :         result = construct_empty_array(astate->element_type);
    5679             :     }
    5680             :     else
    5681             :     {
    5682             :         int         dataoffset,
    5683             :                     nbytes;
    5684             : 
    5685             :         /* Check for overflow of the array dimensions */
    5686         174 :         (void) ArrayGetNItems(astate->ndims, astate->dims);
    5687         174 :         ArrayCheckBounds(astate->ndims, astate->dims, astate->lbs);
    5688             : 
    5689             :         /* Compute required space */
    5690         174 :         nbytes = astate->nbytes;
    5691         174 :         if (astate->nullbitmap != NULL)
    5692             :         {
    5693          66 :             dataoffset = ARR_OVERHEAD_WITHNULLS(astate->ndims, astate->nitems);
    5694          66 :             nbytes += dataoffset;
    5695             :         }
    5696             :         else
    5697             :         {
    5698         108 :             dataoffset = 0;
    5699         108 :             nbytes += ARR_OVERHEAD_NONULLS(astate->ndims);
    5700             :         }
    5701             : 
    5702         174 :         result = (ArrayType *) palloc0(nbytes);
    5703         174 :         SET_VARSIZE(result, nbytes);
    5704         174 :         result->ndim = astate->ndims;
    5705         174 :         result->dataoffset = dataoffset;
    5706         174 :         result->elemtype = astate->element_type;
    5707             : 
    5708         174 :         memcpy(ARR_DIMS(result), astate->dims, astate->ndims * sizeof(int));
    5709         174 :         memcpy(ARR_LBOUND(result), astate->lbs, astate->ndims * sizeof(int));
    5710         174 :         memcpy(ARR_DATA_PTR(result), astate->data, astate->nbytes);
    5711             : 
    5712         174 :         if (astate->nullbitmap != NULL)
    5713          66 :             array_bitmap_copy(ARR_NULLBITMAP(result), 0,
    5714          66 :                               astate->nullbitmap, 0,
    5715             :                               astate->nitems);
    5716             :     }
    5717             : 
    5718         174 :     MemoryContextSwitchTo(oldcontext);
    5719             : 
    5720             :     /* Clean up all the junk */
    5721         174 :     if (release)
    5722             :     {
    5723             :         Assert(astate->private_cxt);
    5724          12 :         MemoryContextDelete(astate->mcontext);
    5725             :     }
    5726             : 
    5727         174 :     return PointerGetDatum(result);
    5728             : }
    5729             : 
    5730             : /*
    5731             :  * The following three functions provide essentially the same API as
    5732             :  * initArrayResult/accumArrayResult/makeArrayResult, but can accept either
    5733             :  * scalar or array inputs, invoking the appropriate set of functions above.
    5734             :  */
    5735             : 
    5736             : /*
    5737             :  * initArrayResultAny - initialize an empty ArrayBuildStateAny
    5738             :  *
    5739             :  *  input_type is the input datatype (either element or array type)
    5740             :  *  rcontext is where to keep working state
    5741             :  *  subcontext is a flag determining whether to use a separate memory context
    5742             :  */
    5743             : ArrayBuildStateAny *
    5744       39470 : initArrayResultAny(Oid input_type, MemoryContext rcontext, bool subcontext)
    5745             : {
    5746             :     ArrayBuildStateAny *astate;
    5747       39470 :     Oid         element_type = get_element_type(input_type);
    5748             : 
    5749       39470 :     if (OidIsValid(element_type))
    5750             :     {
    5751             :         /* Array case */
    5752             :         ArrayBuildStateArr *arraystate;
    5753             : 
    5754          12 :         arraystate = initArrayResultArr(input_type, InvalidOid, rcontext, subcontext);
    5755             :         astate = (ArrayBuildStateAny *)
    5756          12 :             MemoryContextAlloc(arraystate->mcontext,
    5757             :                                sizeof(ArrayBuildStateAny));
    5758          12 :         astate->scalarstate = NULL;
    5759          12 :         astate->arraystate = arraystate;
    5760             :     }
    5761             :     else
    5762             :     {
    5763             :         /* Scalar case */
    5764             :         ArrayBuildState *scalarstate;
    5765             : 
    5766             :         /* Let's just check that we have a type that can be put into arrays */
    5767             :         Assert(OidIsValid(get_array_type(input_type)));
    5768             : 
    5769       39458 :         scalarstate = initArrayResult(input_type, rcontext, subcontext);
    5770             :         astate = (ArrayBuildStateAny *)
    5771       39458 :             MemoryContextAlloc(scalarstate->mcontext,
    5772             :                                sizeof(ArrayBuildStateAny));
    5773       39458 :         astate->scalarstate = scalarstate;
    5774       39458 :         astate->arraystate = NULL;
    5775             :     }
    5776             : 
    5777       39470 :     return astate;
    5778             : }
    5779             : 
    5780             : /*
    5781             :  * accumArrayResultAny - accumulate one (more) input for an array result
    5782             :  *
    5783             :  *  astate is working state (can be NULL on first call)
    5784             :  *  dvalue/disnull represent the new input to append to the array
    5785             :  *  input_type is the input datatype (either element or array type)
    5786             :  *  rcontext is where to keep working state
    5787             :  */
    5788             : ArrayBuildStateAny *
    5789       15000 : accumArrayResultAny(ArrayBuildStateAny *astate,
    5790             :                     Datum dvalue, bool disnull,
    5791             :                     Oid input_type,
    5792             :                     MemoryContext rcontext)
    5793             : {
    5794       15000 :     if (astate == NULL)
    5795           0 :         astate = initArrayResultAny(input_type, rcontext, true);
    5796             : 
    5797       15000 :     if (astate->scalarstate)
    5798       14952 :         (void) accumArrayResult(astate->scalarstate,
    5799             :                                 dvalue, disnull,
    5800             :                                 input_type, rcontext);
    5801             :     else
    5802          48 :         (void) accumArrayResultArr(astate->arraystate,
    5803             :                                    dvalue, disnull,
    5804             :                                    input_type, rcontext);
    5805             : 
    5806       15000 :     return astate;
    5807             : }
    5808             : 
    5809             : /*
    5810             :  * makeArrayResultAny - produce final result of accumArrayResultAny
    5811             :  *
    5812             :  *  astate is working state (must not be NULL)
    5813             :  *  rcontext is where to construct result
    5814             :  *  release is true if okay to release working state
    5815             :  */
    5816             : Datum
    5817       39470 : makeArrayResultAny(ArrayBuildStateAny *astate,
    5818             :                    MemoryContext rcontext, bool release)
    5819             : {
    5820             :     Datum       result;
    5821             : 
    5822       39470 :     if (astate->scalarstate)
    5823             :     {
    5824             :         /* Must use makeMdArrayResult to support "release" parameter */
    5825             :         int         ndims;
    5826             :         int         dims[1];
    5827             :         int         lbs[1];
    5828             : 
    5829             :         /* If no elements were presented, we want to create an empty array */
    5830       39458 :         ndims = (astate->scalarstate->nelems > 0) ? 1 : 0;
    5831       39458 :         dims[0] = astate->scalarstate->nelems;
    5832       39458 :         lbs[0] = 1;
    5833             : 
    5834       39458 :         result = makeMdArrayResult(astate->scalarstate, ndims, dims, lbs,
    5835             :                                    rcontext, release);
    5836             :     }
    5837             :     else
    5838             :     {
    5839          12 :         result = makeArrayResultArr(astate->arraystate,
    5840             :                                     rcontext, release);
    5841             :     }
    5842       39470 :     return result;
    5843             : }
    5844             : 
    5845             : 
    5846             : Datum
    5847         288 : array_larger(PG_FUNCTION_ARGS)
    5848             : {
    5849         288 :     if (array_cmp(fcinfo) > 0)
    5850         144 :         PG_RETURN_DATUM(PG_GETARG_DATUM(0));
    5851             :     else
    5852         138 :         PG_RETURN_DATUM(PG_GETARG_DATUM(1));
    5853             : }
    5854             : 
    5855             : Datum
    5856         258 : array_smaller(PG_FUNCTION_ARGS)
    5857             : {
    5858         258 :     if (array_cmp(fcinfo) < 0)
    5859         174 :         PG_RETURN_DATUM(PG_GETARG_DATUM(0));
    5860             :     else
    5861          84 :         PG_RETURN_DATUM(PG_GETARG_DATUM(1));
    5862             : }
    5863             : 
    5864             : 
    5865             : typedef struct generate_subscripts_fctx
    5866             : {
    5867             :     int32       lower;
    5868             :     int32       upper;
    5869             :     bool        reverse;
    5870             : } generate_subscripts_fctx;
    5871             : 
    5872             : /*
    5873             :  * generate_subscripts(array anyarray, dim int [, reverse bool])
    5874             :  *      Returns all subscripts of the array for any dimension
    5875             :  */
    5876             : Datum
    5877        4870 : generate_subscripts(PG_FUNCTION_ARGS)
    5878             : {
    5879             :     FuncCallContext *funcctx;
    5880             :     MemoryContext oldcontext;
    5881             :     generate_subscripts_fctx *fctx;
    5882             : 
    5883             :     /* stuff done only on the first call of the function */
    5884        4870 :     if (SRF_IS_FIRSTCALL())
    5885             :     {
    5886        2276 :         AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
    5887        2276 :         int         reqdim = PG_GETARG_INT32(1);
    5888             :         int        *lb,
    5889             :                    *dimv;
    5890             : 
    5891             :         /* create a function context for cross-call persistence */
    5892        2276 :         funcctx = SRF_FIRSTCALL_INIT();
    5893             : 
    5894             :         /* Sanity check: does it look like an array at all? */
    5895        2276 :         if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
    5896           6 :             SRF_RETURN_DONE(funcctx);
    5897             : 
    5898             :         /* Sanity check: was the requested dim valid */
    5899        2270 :         if (reqdim <= 0 || reqdim > AARR_NDIM(v))
    5900           0 :             SRF_RETURN_DONE(funcctx);
    5901             : 
    5902             :         /*
    5903             :          * switch to memory context appropriate for multiple function calls
    5904             :          */
    5905        2270 :         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
    5906        2270 :         fctx = (generate_subscripts_fctx *) palloc(sizeof(generate_subscripts_fctx));
    5907             : 
    5908        2270 :         lb = AARR_LBOUND(v);
    5909        2270 :         dimv = AARR_DIMS(v);
    5910             : 
    5911        2270 :         fctx->lower = lb[reqdim - 1];
    5912        2270 :         fctx->upper = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
    5913        2270 :         fctx->reverse = (PG_NARGS() < 3) ? false : PG_GETARG_BOOL(2);
    5914             : 
    5915        2270 :         funcctx->user_fctx = fctx;
    5916             : 
    5917        2270 :         MemoryContextSwitchTo(oldcontext);
    5918             :     }
    5919             : 
    5920        4864 :     funcctx = SRF_PERCALL_SETUP();
    5921             : 
    5922        4864 :     fctx = funcctx->user_fctx;
    5923             : 
    5924        4864 :     if (fctx->lower <= fctx->upper)
    5925             :     {
    5926        2594 :         if (!fctx->reverse)
    5927        2594 :             SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->lower++));
    5928             :         else
    5929           0 :             SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->upper--));
    5930             :     }
    5931             :     else
    5932             :         /* done when there are no more elements left */
    5933        2270 :         SRF_RETURN_DONE(funcctx);
    5934             : }
    5935             : 
    5936             : /*
    5937             :  * generate_subscripts_nodir
    5938             :  *      Implements the 2-argument version of generate_subscripts
    5939             :  */
    5940             : Datum
    5941        4870 : generate_subscripts_nodir(PG_FUNCTION_ARGS)
    5942             : {
    5943             :     /* just call the other one -- it can handle both cases */
    5944        4870 :     return generate_subscripts(fcinfo);
    5945             : }
    5946             : 
    5947             : /*
    5948             :  * array_fill_with_lower_bounds
    5949             :  *      Create and fill array with defined lower bounds.
    5950             :  */
    5951             : Datum
    5952          66 : array_fill_with_lower_bounds(PG_FUNCTION_ARGS)
    5953             : {
    5954             :     ArrayType  *dims;
    5955             :     ArrayType  *lbs;
    5956             :     ArrayType  *result;
    5957             :     Oid         elmtype;
    5958             :     Datum       value;
    5959             :     bool        isnull;
    5960             : 
    5961          66 :     if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
    5962          12 :         ereport(ERROR,
    5963             :                 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
    5964             :                  errmsg("dimension array or low bound array cannot be null")));
    5965             : 
    5966          54 :     dims = PG_GETARG_ARRAYTYPE_P(1);
    5967          54 :     lbs = PG_GETARG_ARRAYTYPE_P(2);
    5968             : 
    5969          54 :     if (!PG_ARGISNULL(0))
    5970             :     {
    5971          42 :         value = PG_GETARG_DATUM(0);
    5972          42 :         isnull = false;
    5973             :     }
    5974             :     else
    5975             :     {
    5976          12 :         value = 0;
    5977          12 :         isnull = true;
    5978             :     }
    5979             : 
    5980          54 :     elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
    5981          54 :     if (!OidIsValid(elmtype))
    5982           0 :         elog(ERROR, "could not determine data type of input");
    5983             : 
    5984          54 :     result = array_fill_internal(dims, lbs, value, isnull, elmtype, fcinfo);
    5985          42 :     PG_RETURN_ARRAYTYPE_P(result);
    5986             : }
    5987             : 
    5988             : /*
    5989             :  * array_fill
    5990             :  *      Create and fill array with default lower bounds.
    5991             :  */
    5992             : Datum
    5993          84 : array_fill(PG_FUNCTION_ARGS)
    5994             : {
    5995             :     ArrayType  *dims;
    5996             :     ArrayType  *result;
    5997             :     Oid         elmtype;
    5998             :     Datum       value;
    5999             :     bool        isnull;
    6000             : 
    6001          84 :     if (PG_ARGISNULL(1))
    6002           0 :         ereport(ERROR,
    6003             :                 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
    6004             :                  errmsg("dimension array or low bound array cannot be null")));
    6005             : 
    6006          84 :     dims = PG_GETARG_ARRAYTYPE_P(1);
    6007             : 
    6008          84 :     if (!PG_ARGISNULL(0))
    6009             :     {
    6010          72 :         value = PG_GETARG_DATUM(0);
    6011          72 :         isnull = false;
    6012             :     }
    6013             :     else
    6014             :     {
    6015          12 :         value = 0;
    6016          12 :         isnull = true;
    6017             :     }
    6018             : 
    6019          84 :     elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
    6020          84 :     if (!OidIsValid(elmtype))
    6021           0 :         elog(ERROR, "could not determine data type of input");
    6022             : 
    6023          84 :     result = array_fill_internal(dims, NULL, value, isnull, elmtype, fcinfo);
    6024          72 :     PG_RETURN_ARRAYTYPE_P(result);
    6025             : }
    6026             : 
    6027             : static ArrayType *
    6028          60 : create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
    6029             :                       Oid elmtype, int dataoffset)
    6030             : {
    6031             :     ArrayType  *result;
    6032             : 
    6033          60 :     result = (ArrayType *) palloc0(nbytes);
    6034          60 :     SET_VARSIZE(result, nbytes);
    6035          60 :     result->ndim = ndims;
    6036          60 :     result->dataoffset = dataoffset;
    6037          60 :     result->elemtype = elmtype;
    6038          60 :     memcpy(ARR_DIMS(result), dimv, ndims * sizeof(int));
    6039          60 :     memcpy(ARR_LBOUND(result), lbsv, ndims * sizeof(int));
    6040             : 
    6041          60 :     return result;
    6042             : }
    6043             : 
    6044             : static ArrayType *
    6045         138 : array_fill_internal(ArrayType *dims, ArrayType *lbs,
    6046             :                     Datum value, bool isnull, Oid elmtype,
    6047             :                     FunctionCallInfo fcinfo)
    6048             : {
    6049             :     ArrayType  *result;
    6050             :     int        *dimv;
    6051             :     int        *lbsv;
    6052             :     int         ndims;
    6053             :     int         nitems;
    6054             :     int         deflbs[MAXDIM];
    6055             :     int16       elmlen;
    6056             :     bool        elmbyval;
    6057             :     char        elmalign;
    6058             :     ArrayMetaState *my_extra;
    6059             : 
    6060             :     /*
    6061             :      * Params checks
    6062             :      */
    6063         138 :     if (ARR_NDIM(dims) > 1)
    6064           6 :         ereport(ERROR,
    6065             :                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    6066             :                  errmsg("wrong number of array subscripts"),
    6067             :                  errdetail("Dimension array must be one dimensional.")));
    6068             : 
    6069         132 :     if (array_contains_nulls(dims))
    6070           6 :         ereport(ERROR,
    6071             :                 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
    6072             :                  errmsg("dimension values cannot be null")));
    6073             : 
    6074         126 :     dimv = (int *) ARR_DATA_PTR(dims);
    6075         126 :     ndims = (ARR_NDIM(dims) > 0) ? ARR_DIMS(dims)[0] : 0;
    6076             : 
    6077         126 :     if (ndims < 0)               /* we do allow zero-dimension arrays */
    6078           0 :         ereport(ERROR,
    6079             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    6080             :                  errmsg("invalid number of dimensions: %d", ndims)));
    6081         126 :     if (ndims > MAXDIM)
    6082           0 :         ereport(ERROR,
    6083             :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    6084             :                  errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
    6085             :                         ndims, MAXDIM)));
    6086             : 
    6087         126 :     if (lbs != NULL)
    6088             :     {
    6089          54 :         if (ARR_NDIM(lbs) > 1)
    6090           0 :             ereport(ERROR,
    6091             :                     (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    6092             :                      errmsg("wrong number of array subscripts"),
    6093             :                      errdetail("Dimension array must be one dimensional.")));
    6094             : 
    6095          54 :         if (array_contains_nulls(lbs))
    6096           0 :             ereport(ERROR,
    6097             :                     (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
    6098             :                      errmsg("dimension values cannot be null")));
    6099             : 
    6100          54 :         if (ndims != ((ARR_NDIM(lbs) > 0) ? ARR_DIMS(lbs)[0] : 0))
    6101          12 :             ereport(ERROR,
    6102             :                     (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    6103             :                      errmsg("wrong number of array subscripts"),
    6104             :                      errdetail("Low bound array has different size than dimensions array.")));
    6105             : 
    6106          42 :         lbsv = (int *) ARR_DATA_PTR(lbs);
    6107             :     }
    6108             :     else
    6109             :     {
    6110             :         int         i;
    6111             : 
    6112         504 :         for (i = 0; i < MAXDIM; i++)
    6113         432 :             deflbs[i] = 1;
    6114             : 
    6115          72 :         lbsv = deflbs;
    6116             :     }
    6117             : 
    6118             :     /* This checks for overflow of the array dimensions */
    6119         114 :     nitems = ArrayGetNItems(ndims, dimv);
    6120         114 :     ArrayCheckBounds(ndims, dimv, lbsv);
    6121             : 
    6122             :     /* fast track for empty array */
    6123         114 :     if (nitems <= 0)
    6124          54 :         return construct_empty_array(elmtype);
    6125             : 
    6126             :     /*
    6127             :      * We arrange to look up info about element type only once per series of
    6128             :      * calls, assuming the element type doesn't change underneath us.
    6129             :      */
    6130          60 :     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
    6131          60 :     if (my_extra == NULL)
    6132             :     {
    6133          60 :         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
    6134             :                                                       sizeof(ArrayMetaState));
    6135          60 :         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
    6136          60 :         my_extra->element_type = InvalidOid;
    6137             :     }
    6138             : 
    6139          60 :     if (my_extra->element_type != elmtype)
    6140             :     {
    6141             :         /* Get info about element type */
    6142          60 :         get_typlenbyvalalign(elmtype,
    6143             :                              &my_extra->typlen,
    6144             :                              &my_extra->typbyval,
    6145             :                              &my_extra->typalign);
    6146          60 :         my_extra->element_type = elmtype;
    6147             :     }
    6148             : 
    6149          60 :     elmlen = my_extra->typlen;
    6150          60 :     elmbyval = my_extra->typbyval;
    6151          60 :     elmalign = my_extra->typalign;
    6152             : 
    6153             :     /* compute required space */
    6154          60 :     if (!isnull)
    6155             :     {
    6156             :         int         i;
    6157             :         char       *p;
    6158             :         int         nbytes;
    6159             :         int         totbytes;
    6160             : 
    6161             :         /* make sure data is not toasted */
    6162          36 :         if (elmlen == -1)
    6163          12 :             value = PointerGetDatum(PG_DETOAST_DATUM(value));
    6164             : 
    6165          36 :         nbytes = att_addlength_datum(0, elmlen, value);
    6166          36 :         nbytes = att_align_nominal(nbytes, elmalign);
    6167             :         Assert(nbytes > 0);
    6168             : 
    6169          36 :         totbytes = nbytes * nitems;
    6170             : 
    6171             :         /* check for overflow of multiplication or total request */
    6172          36 :         if (totbytes / nbytes != nitems ||
    6173          36 :             !AllocSizeIsValid(totbytes))
    6174           0 :             ereport(ERROR,
    6175             :                     (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    6176             :                      errmsg("array size exceeds the maximum allowed (%d)",
    6177             :                             (int) MaxAllocSize)));
    6178             : 
    6179             :         /*
    6180             :          * This addition can't overflow, but it might cause us to go past
    6181             :          * MaxAllocSize.  We leave it to palloc to complain in that case.
    6182             :          */
    6183          36 :         totbytes += ARR_OVERHEAD_NONULLS(ndims);
    6184             : 
    6185          36 :         result = create_array_envelope(ndims, dimv, lbsv, totbytes,
    6186             :                                        elmtype, 0);
    6187             : 
    6188          36 :         p = ARR_DATA_PTR(result);
    6189         300 :         for (i = 0; i < nitems; i++)
    6190         264 :             p += ArrayCastAndSet(value, elmlen, elmbyval, elmalign, p);
    6191             :     }
    6192             :     else
    6193             :     {
    6194             :         int         nbytes;
    6195             :         int         dataoffset;
    6196             : 
    6197          24 :         dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
    6198          24 :         nbytes = dataoffset;
    6199             : 
    6200          24 :         result = create_array_envelope(ndims, dimv, lbsv, nbytes,
    6201             :                                        elmtype, dataoffset);
    6202             : 
    6203             :         /* create_array_envelope already zeroed the bitmap, so we're done */
    6204             :     }
    6205             : 
    6206          60 :     return result;
    6207             : }
    6208             : 
    6209             : 
    6210             : /*
    6211             :  * UNNEST
    6212             :  */
    6213             : Datum
    6214      549852 : array_unnest(PG_FUNCTION_ARGS)
    6215             : {
    6216             :     typedef struct
    6217             :     {
    6218             :         array_iter  iter;
    6219             :         int         nextelem;
    6220             :         int         numelems;
    6221             :         int16       elmlen;
    6222             :         bool        elmbyval;
    6223             :         char        elmalign;
    6224             :     } array_unnest_fctx;
    6225             : 
    6226             :     FuncCallContext *funcctx;
    6227             :     array_unnest_fctx *fctx;
    6228             :     MemoryContext oldcontext;
    6229             : 
    6230             :     /* stuff done only on the first call of the function */
    6231      549852 :     if (SRF_IS_FIRSTCALL())
    6232             :     {
    6233             :         AnyArrayType *arr;
    6234             : 
    6235             :         /* create a function context for cross-call persistence */
    6236      130358 :         funcctx = SRF_FIRSTCALL_INIT();
    6237             : 
    6238             :         /*
    6239             :          * switch to memory context appropriate for multiple function calls
    6240             :          */
    6241      130358 :         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
    6242             : 
    6243             :         /*
    6244             :          * Get the array value and detoast if needed.  We can't do this
    6245             :          * earlier because if we have to detoast, we want the detoasted copy
    6246             :          * to be in multi_call_memory_ctx, so it will go away when we're done
    6247             :          * and not before.  (If no detoast happens, we assume the originally
    6248             :          * passed array will stick around till then.)
    6249             :          */
    6250      130358 :         arr = PG_GETARG_ANY_ARRAY_P(0);
    6251             : 
    6252             :         /* allocate memory for user context */
    6253      130358 :         fctx = (array_unnest_fctx *) palloc(sizeof(array_unnest_fctx));
    6254             : 
    6255             :         /* initialize state */
    6256      130358 :         array_iter_setup(&fctx->iter, arr);
    6257      130358 :         fctx->nextelem = 0;
    6258      130358 :         fctx->numelems = ArrayGetNItems(AARR_NDIM(arr), AARR_DIMS(arr));
    6259             : 
    6260      130358 :         if (VARATT_IS_EXPANDED_HEADER(arr))
    6261             :         {
    6262             :             /* we can just grab the type data from expanded array */
    6263           0 :             fctx->elmlen = arr->xpn.typlen;
    6264           0 :             fctx->elmbyval = arr->xpn.typbyval;
    6265           0 :             fctx->elmalign = arr->xpn.typalign;
    6266             :         }
    6267             :         else
    6268      130358 :             get_typlenbyvalalign(AARR_ELEMTYPE(arr),
    6269             :                                  &fctx->elmlen,
    6270             :                                  &fctx->elmbyval,
    6271             :                                  &fctx->elmalign);
    6272             : 
    6273      130358 :         funcctx->user_fctx = fctx;
    6274      130358 :         MemoryContextSwitchTo(oldcontext);
    6275             :     }
    6276             : 
    6277             :     /* stuff done on every call of the function */
    6278      549852 :     funcctx = SRF_PERCALL_SETUP();
    6279      549852 :     fctx = funcctx->user_fctx;
    6280             : 
    6281      549852 :     if (fctx->nextelem < fctx->numelems)
    6282             :     {
    6283      419494 :         int         offset = fctx->nextelem++;
    6284             :         Datum       elem;
    6285             : 
    6286      419494 :         elem = array_iter_next(&fctx->iter, &fcinfo->isnull, offset,
    6287      419494 :                                fctx->elmlen, fctx->elmbyval, fctx->elmalign);
    6288             : 
    6289      419494 :         SRF_RETURN_NEXT(funcctx, elem);
    6290             :     }
    6291             :     else
    6292             :     {
    6293             :         /* do when there is no more left */
    6294      130358 :         SRF_RETURN_DONE(funcctx);
    6295             :     }
    6296             : }
    6297             : 
    6298             : /*
    6299             :  * Planner support function for array_unnest(anyarray)
    6300             :  */
    6301             : Datum
    6302       13944 : array_unnest_support(PG_FUNCTION_ARGS)
    6303             : {
    6304       13944 :     Node       *rawreq = (Node *) PG_GETARG_POINTER(0);
    6305       13944 :     Node       *ret = NULL;
    6306             : 
    6307       13944 :     if (IsA(rawreq, SupportRequestRows))
    6308             :     {
    6309             :         /* Try to estimate the number of rows returned */
    6310        4612 :         SupportRequestRows *req = (SupportRequestRows *) rawreq;
    6311             : 
    6312        4612 :         if (is_funcclause(req->node))    /* be paranoid */
    6313             :         {
    6314        4606 :             List       *args = ((FuncExpr *) req->node)->args;
    6315             :             Node       *arg1;
    6316             : 
    6317             :             /* We can use estimated argument values here */
    6318        4606 :             arg1 = estimate_expression_value(req->root, linitial(args));
    6319             : 
    6320        4606 :             req->rows = estimate_array_length(arg1);
    6321        4606 :             ret = (Node *) req;
    6322             :         }
    6323             :     }
    6324             : 
    6325       13944 :     PG_RETURN_POINTER(ret);
    6326             : }
    6327             : 
    6328             : 
    6329             : /*
    6330             :  * array_replace/array_remove support
    6331             :  *
    6332             :  * Find all array entries matching (not distinct from) search/search_isnull,
    6333             :  * and delete them if remove is true, else replace them with
    6334             :  * replace/replace_isnull.  Comparisons are done using the specified
    6335             :  * collation.  fcinfo is passed only for caching purposes.
    6336             :  */
    6337             : static ArrayType *
    6338        2334 : array_replace_internal(ArrayType *array,
    6339             :                        Datum search, bool search_isnull,
    6340             :                        Datum replace, bool replace_isnull,
    6341             :                        bool remove, Oid collation,
    6342             :                        FunctionCallInfo fcinfo)
    6343             : {
    6344        2334 :     LOCAL_FCINFO(locfcinfo, 2);
    6345             :     ArrayType  *result;
    6346             :     Oid         element_type;
    6347             :     Datum      *values;
    6348             :     bool       *nulls;
    6349             :     int        *dim;
    6350             :     int         ndim;
    6351             :     int         nitems,
    6352             :                 nresult;
    6353             :     int         i;
    6354        2334 :     int32       nbytes = 0;
    6355             :     int32       dataoffset;
    6356             :     bool        hasnulls;
    6357             :     int         typlen;
    6358             :     bool        typbyval;
    6359             :     char        typalign;
    6360             :     char       *arraydataptr;
    6361             :     bits8      *bitmap;
    6362             :     int         bitmask;
    6363        2334 :     bool        changed = false;
    6364             :     TypeCacheEntry *typentry;
    6365             : 
    6366        2334 :     element_type = ARR_ELEMTYPE(array);
    6367        2334 :     ndim = ARR_NDIM(array);
    6368        2334 :     dim = ARR_DIMS(array);
    6369        2334 :     nitems = ArrayGetNItems(ndim, dim);
    6370             : 
    6371             :     /* Return input array unmodified if it is empty */
    6372        2334 :     if (nitems <= 0)
    6373           0 :         return array;
    6374             : 
    6375             :     /*
    6376             :      * We can't remove elements from multi-dimensional arrays, since the
    6377             :      * result might not be rectangular.
    6378             :      */
    6379        2334 :     if (remove && ndim > 1)
    6380           6 :         ereport(ERROR,
    6381             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    6382             :                  errmsg("removing elements from multidimensional arrays is not supported")));
    6383             : 
    6384             :     /*
    6385             :      * We arrange to look up the equality function only once per series of
    6386             :      * calls, assuming the element type doesn't change underneath us.
    6387             :      */
    6388        2328 :     typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
    6389        2328 :     if (typentry == NULL ||
    6390        1792 :         typentry->type_id != element_type)
    6391             :     {
    6392         536 :         typentry = lookup_type_cache(element_type,
    6393             :                                      TYPECACHE_EQ_OPR_FINFO);
    6394         536 :         if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
    6395           0 :             ereport(ERROR,
    6396             :                     (errcode(ERRCODE_UNDEFINED_FUNCTION),
    6397             :                      errmsg("could not identify an equality operator for type %s",
    6398             :                             format_type_be(element_type))));
    6399         536 :         fcinfo->flinfo->fn_extra = (void *) typentry;
    6400             :     }
    6401        2328 :     typlen = typentry->typlen;
    6402        2328 :     typbyval = typentry->typbyval;
    6403        2328 :     typalign = typentry->typalign;
    6404             : 
    6405             :     /*
    6406             :      * Detoast values if they are toasted.  The replacement value must be
    6407             :      * detoasted for insertion into the result array, while detoasting the
    6408             :      * search value only once saves cycles.
    6409             :      */
    6410        2328 :     if (typlen == -1)
    6411             :     {
    6412        2286 :         if (!search_isnull)
    6413        2280 :             search = PointerGetDatum(PG_DETOAST_DATUM(search));
    6414        2286 :         if (!replace_isnull)
    6415          12 :             replace = PointerGetDatum(PG_DETOAST_DATUM(replace));
    6416             :     }
    6417             : 
    6418             :     /* Prepare to apply the comparison operator */
    6419        2328 :     InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
    6420             :                              collation, NULL, NULL);
    6421             : 
    6422             :     /* Allocate temporary arrays for new values */
    6423        2328 :     values = (Datum *) palloc(nitems * sizeof(Datum));
    6424        2328 :     nulls = (bool *) palloc(nitems * sizeof(bool));
    6425             : 
    6426             :     /* Loop over source data */
    6427        2328 :     arraydataptr = ARR_DATA_PTR(array);
    6428        2328 :     bitmap = ARR_NULLBITMAP(array);
    6429        2328 :     bitmask = 1;
    6430        2328 :     hasnulls = false;
    6431        2328 :     nresult = 0;
    6432             : 
    6433        5218 :     for (i = 0; i < nitems; i++)
    6434             :     {
    6435             :         Datum       elt;
    6436             :         bool        isNull;
    6437             :         bool        oprresult;
    6438        2890 :         bool        skip = false;
    6439             : 
    6440             :         /* Get source element, checking for NULL */
    6441        2890 :         if (bitmap && (*bitmap & bitmask) == 0)
    6442             :         {
    6443          36 :             isNull = true;
    6444             :             /* If searching for NULL, we have a match */
    6445          36 :             if (search_isnull)
    6446             :             {
    6447          36 :                 if (remove)
    6448             :                 {
    6449          12 :                     skip = true;
    6450          12 :                     changed = true;
    6451             :                 }
    6452          24 :                 else if (!replace_isnull)
    6453             :                 {
    6454          18 :                     values[nresult] = replace;
    6455          18 :                     isNull = false;
    6456          18 :                     changed = true;
    6457             :                 }
    6458             :             }
    6459             :         }
    6460             :         else
    6461             :         {
    6462        2854 :             isNull = false;
    6463        2854 :             elt = fetch_att(arraydataptr, typbyval, typlen);
    6464        2854 :             arraydataptr = att_addlength_datum(arraydataptr, typlen, elt);
    6465        2854 :             arraydataptr = (char *) att_align_nominal(arraydataptr, typalign);
    6466             : 
    6467        2854 :             if (search_isnull)
    6468             :             {
    6469             :                 /* no match possible, keep element */
    6470          54 :                 values[nresult] = elt;
    6471             :             }
    6472             :             else
    6473             :             {
    6474             :                 /*
    6475             :                  * Apply the operator to the element pair; treat NULL as false
    6476             :                  */
    6477        2800 :                 locfcinfo->args[0].value = elt;
    6478        2800 :                 locfcinfo->args[0].isnull = false;
    6479        2800 :                 locfcinfo->args[1].value = search;
    6480        2800 :                 locfcinfo->args[1].isnull = false;
    6481        2800 :                 locfcinfo->isnull = false;
    6482        2800 :                 oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
    6483        2800 :                 if (locfcinfo->isnull || !oprresult)
    6484             :                 {
    6485             :                     /* no match, keep element */
    6486        2648 :                     values[nresult] = elt;
    6487             :                 }
    6488             :                 else
    6489             :                 {
    6490             :                     /* match, so replace or delete */
    6491         152 :                     changed = true;
    6492         152 :                     if (remove)
    6493         128 :                         skip = true;
    6494             :                     else
    6495             :                     {
    6496          24 :                         values[nresult] = replace;
    6497          24 :                         isNull = replace_isnull;
    6498             :                     }
    6499             :                 }
    6500             :             }
    6501             :         }
    6502             : 
    6503        2890 :         if (!skip)
    6504             :         {
    6505        2750 :             nulls[nresult] = isNull;
    6506        2750 :             if (isNull)
    6507          12 :                 hasnulls = true;
    6508             :             else
    6509             :             {
    6510             :                 /* Update total result size */
    6511        2738 :                 nbytes = att_addlength_datum(nbytes, typlen, values[nresult]);
    6512        2738 :                 nbytes = att_align_nominal(nbytes, typalign);
    6513             :                 /* check for overflow of total request */
    6514        2738 :                 if (!AllocSizeIsValid(nbytes))
    6515           0 :                     ereport(ERROR,
    6516             :                             (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    6517             :                              errmsg("array size exceeds the maximum allowed (%d)",
    6518             :                                     (int) MaxAllocSize)));
    6519             :             }
    6520        2750 :             nresult++;
    6521             :         }
    6522             : 
    6523             :         /* advance bitmap pointer if any */
    6524        2890 :         if (bitmap)
    6525             :         {
    6526          90 :             bitmask <<= 1;
    6527          90 :             if (bitmask == 0x100)
    6528             :             {
    6529           0 :                 bitmap++;
    6530           0 :                 bitmask = 1;
    6531             :             }
    6532             :         }
    6533             :     }
    6534             : 
    6535             :     /*
    6536             :      * If not changed just return the original array
    6537             :      */
    6538        2328 :     if (!changed)
    6539             :     {
    6540        2182 :         pfree(values);
    6541        2182 :         pfree(nulls);
    6542        2182 :         return array;
    6543             :     }
    6544             : 
    6545             :     /* If all elements were removed return an empty array */
    6546         146 :     if (nresult == 0)
    6547             :     {
    6548           6 :         pfree(values);
    6549           6 :         pfree(nulls);
    6550           6 :         return construct_empty_array(element_type);
    6551             :     }
    6552             : 
    6553             :     /* Allocate and initialize the result array */
    6554         140 :     if (hasnulls)
    6555             :     {
    6556           6 :         dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nresult);
    6557           6 :         nbytes += dataoffset;
    6558             :     }
    6559             :     else
    6560             :     {
    6561         134 :         dataoffset = 0;         /* marker for no null bitmap */
    6562         134 :         nbytes += ARR_OVERHEAD_NONULLS(ndim);
    6563             :     }
    6564         140 :     result = (ArrayType *) palloc0(nbytes);
    6565         140 :     SET_VARSIZE(result, nbytes);
    6566         140 :     result->ndim = ndim;
    6567         140 :     result->dataoffset = dataoffset;
    6568         140 :     result->elemtype = element_type;
    6569         140 :     memcpy(ARR_DIMS(result), ARR_DIMS(array), ndim * sizeof(int));
    6570         140 :     memcpy(ARR_LBOUND(result), ARR_LBOUND(array), ndim * sizeof(int));
    6571             : 
    6572         140 :     if (remove)
    6573             :     {
    6574             :         /* Adjust the result length */
    6575         110 :         ARR_DIMS(result)[0] = nresult;
    6576             :     }
    6577             : 
    6578             :     /* Insert data into result array */
    6579         140 :     CopyArrayEls(result,
    6580             :                  values, nulls, nresult,
    6581             :                  typlen, typbyval, typalign,
    6582             :                  false);
    6583             : 
    6584         140 :     pfree(values);
    6585         140 :     pfree(nulls);
    6586             : 
    6587         140 :     return result;
    6588             : }
    6589             : 
    6590             : /*
    6591             :  * Remove any occurrences of an element from an array
    6592             :  *
    6593             :  * If used on a multi-dimensional array this will raise an error.
    6594             :  */
    6595             : Datum
    6596      119694 : array_remove(PG_FUNCTION_ARGS)
    6597             : {
    6598             :     ArrayType  *array;
    6599      119694 :     Datum       search = PG_GETARG_DATUM(1);
    6600      119694 :     bool        search_isnull = PG_ARGISNULL(1);
    6601             : 
    6602      119694 :     if (PG_ARGISNULL(0))
    6603      117396 :         PG_RETURN_NULL();
    6604        2298 :     array = PG_GETARG_ARRAYTYPE_P(0);
    6605             : 
    6606        2298 :     array = array_replace_internal(array,
    6607             :                                    search, search_isnull,
    6608             :                                    (Datum) 0, true,
    6609             :                                    true, PG_GET_COLLATION(),
    6610             :                                    fcinfo);
    6611        2292 :     PG_RETURN_ARRAYTYPE_P(array);
    6612             : }
    6613             : 
    6614             : /*
    6615             :  * Replace any occurrences of an element in an array
    6616             :  */
    6617             : Datum
    6618          36 : array_replace(PG_FUNCTION_ARGS)
    6619             : {
    6620             :     ArrayType  *array;
    6621          36 :     Datum       search = PG_GETARG_DATUM(1);
    6622          36 :     bool        search_isnull = PG_ARGISNULL(1);
    6623          36 :     Datum       replace = PG_GETARG_DATUM(2);
    6624          36 :     bool        replace_isnull = PG_ARGISNULL(2);
    6625             : 
    6626          36 :     if (PG_ARGISNULL(0))
    6627           0 :         PG_RETURN_NULL();
    6628          36 :     array = PG_GETARG_ARRAYTYPE_P(0);
    6629             : 
    6630          36 :     array = array_replace_internal(array,
    6631             :                                    search, search_isnull,
    6632             :                                    replace, replace_isnull,
    6633             :                                    false, PG_GET_COLLATION(),
    6634             :                                    fcinfo);
    6635          36 :     PG_RETURN_ARRAYTYPE_P(array);
    6636             : }
    6637             : 
    6638             : /*
    6639             :  * Implements width_bucket(anyelement, anyarray).
    6640             :  *
    6641             :  * 'thresholds' is an array containing lower bound values for each bucket;
    6642             :  * these must be sorted from smallest to largest, or bogus results will be
    6643             :  * produced.  If N thresholds are supplied, the output is from 0 to N:
    6644             :  * 0 is for inputs < first threshold, N is for inputs >= last threshold.
    6645             :  */
    6646             : Datum
    6647         810 : width_bucket_array(PG_FUNCTION_ARGS)
    6648             : {
    6649         810 :     Datum       operand = PG_GETARG_DATUM(0);
    6650         810 :     ArrayType  *thresholds = PG_GETARG_ARRAYTYPE_P(1);
    6651         810 :     Oid         collation = PG_GET_COLLATION();
    6652         810 :     Oid         element_type = ARR_ELEMTYPE(thresholds);
    6653             :     int         result;
    6654             : 
    6655             :     /* Check input */
    6656         810 :     if (ARR_NDIM(thresholds) > 1)
    6657           6 :         ereport(ERROR,
    6658             :                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    6659             :                  errmsg("thresholds must be one-dimensional array")));
    6660             : 
    6661         804 :     if (array_contains_nulls(thresholds))
    6662           6 :         ereport(ERROR,
    6663             :                 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
    6664             :                  errmsg("thresholds array must not contain NULLs")));
    6665             : 
    6666             :     /* We have a dedicated implementation for float8 data */
    6667         798 :     if (element_type == FLOAT8OID)
    6668         366 :         result = width_bucket_array_float8(operand, thresholds);
    6669             :     else
    6670             :     {
    6671             :         TypeCacheEntry *typentry;
    6672             : 
    6673             :         /* Cache information about the input type */
    6674         432 :         typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
    6675         432 :         if (typentry == NULL ||
    6676         390 :             typentry->type_id != element_type)
    6677             :         {
    6678          42 :             typentry = lookup_type_cache(element_type,
    6679             :                                          TYPECACHE_CMP_PROC_FINFO);
    6680          42 :             if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
    6681           0 :                 ereport(ERROR,
    6682             :                         (errcode(ERRCODE_UNDEFINED_FUNCTION),
    6683             :                          errmsg("could not identify a comparison function for type %s",
    6684             :                                 format_type_be(element_type))));
    6685          42 :             fcinfo->flinfo->fn_extra = (void *) typentry;
    6686             :         }
    6687             : 
    6688             :         /*
    6689             :          * We have separate implementation paths for fixed- and variable-width
    6690             :          * types, since indexing the array is a lot cheaper in the first case.
    6691             :          */
    6692         432 :         if (typentry->typlen > 0)
    6693          90 :             result = width_bucket_array_fixed(operand, thresholds,
    6694             :                                               collation, typentry);
    6695             :         else
    6696         342 :             result = width_bucket_array_variable(operand, thresholds,
    6697             :                                                  collation, typentry);
    6698             :     }
    6699             : 
    6700             :     /* Avoid leaking memory when handed toasted input. */
    6701         798 :     PG_FREE_IF_COPY(thresholds, 1);
    6702             : 
    6703         798 :     PG_RETURN_INT32(result);
    6704             : }
    6705             : 
    6706             : /*
    6707             :  * width_bucket_array for float8 data.
    6708             :  */
    6709             : static int
    6710         366 : width_bucket_array_float8(Datum operand, ArrayType *thresholds)
    6711             : {
    6712         366 :     float8      op = DatumGetFloat8(operand);
    6713             :     float8     *thresholds_data;
    6714             :     int         left;
    6715             :     int         right;
    6716             : 
    6717             :     /*
    6718             :      * Since we know the array contains no NULLs, we can just index it
    6719             :      * directly.
    6720             :      */
    6721         366 :     thresholds_data = (float8 *) ARR_DATA_PTR(thresholds);
    6722             : 
    6723         366 :     left = 0;
    6724         366 :     right = ArrayGetNItems(ARR_NDIM(thresholds), ARR_DIMS(thresholds));
    6725             : 
    6726             :     /*
    6727             :      * If the probe value is a NaN, it's greater than or equal to all possible
    6728             :      * threshold values (including other NaNs), so we need not search.  Note
    6729             :      * that this would give the same result as searching even if the array
    6730             :      * contains multiple NaNs (as long as they're correctly sorted), since the
    6731             :      * loop logic will find the rightmost of multiple equal threshold values.
    6732             :      */
    6733         366 :     if (isnan(op))
    6734           6 :         return right;
    6735             : 
    6736             :     /* Find the bucket */
    6737        1134 :     while (left < right)
    6738             :     {
    6739         774 :         int         mid = (left + right) / 2;
    6740             : 
    6741         774 :         if (isnan(thresholds_data[mid]) || op < thresholds_data[mid])
    6742         336 :             right = mid;
    6743             :         else
    6744         438 :             left = mid + 1;
    6745             :     }
    6746             : 
    6747         360 :     return left;
    6748             : }
    6749             : 
    6750             : /*
    6751             :  * width_bucket_array for generic fixed-width data types.
    6752             :  */
    6753             : static int
    6754          90 : width_bucket_array_fixed(Datum operand,
    6755             :                          ArrayType *thresholds,
    6756             :                          Oid collation,
    6757             :                          TypeCacheEntry *typentry)
    6758             : {
    6759          90 :     LOCAL_FCINFO(locfcinfo, 2);
    6760             :     char       *thresholds_data;
    6761          90 :     int         typlen = typentry->typlen;
    6762          90 :     bool        typbyval = typentry->typbyval;
    6763             :     int         left;
    6764             :     int         right;
    6765             : 
    6766             :     /*
    6767             :      * Since we know the array contains no NULLs, we can just index it
    6768             :      * directly.
    6769             :      */
    6770          90 :     thresholds_data = (char *) ARR_DATA_PTR(thresholds);
    6771             : 
    6772          90 :     InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
    6773             :                              collation, NULL, NULL);
    6774             : 
    6775             :     /* Find the bucket */
    6776          90 :     left = 0;
    6777          90 :     right = ArrayGetNItems(ARR_NDIM(thresholds), ARR_DIMS(thresholds));
    6778         270 :     while (left < right)
    6779             :     {
    6780         180 :         int         mid = (left + right) / 2;
    6781             :         char       *ptr;
    6782             :         int32       cmpresult;
    6783             : 
    6784         180 :         ptr = thresholds_data + mid * typlen;
    6785             : 
    6786         180 :         locfcinfo->args[0].value = operand;
    6787         180 :         locfcinfo->args[0].isnull = false;
    6788         180 :         locfcinfo->args[1].value = fetch_att(ptr, typbyval, typlen);
    6789         180 :         locfcinfo->args[1].isnull = false;
    6790             : 
    6791         180 :         cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
    6792             : 
    6793             :         /* We don't expect comparison support functions to return null */
    6794             :         Assert(!locfcinfo->isnull);
    6795             : 
    6796         180 :         if (cmpresult < 0)
    6797          90 :             right = mid;
    6798             :         else
    6799          90 :             left = mid + 1;
    6800             :     }
    6801             : 
    6802          90 :     return left;
    6803             : }
    6804             : 
    6805             : /*
    6806             :  * width_bucket_array for generic variable-width data types.
    6807             :  */
    6808             : static int
    6809         342 : width_bucket_array_variable(Datum operand,
    6810             :                             ArrayType *thresholds,
    6811             :                             Oid collation,
    6812             :                             TypeCacheEntry *typentry)
    6813             : {
    6814         342 :     LOCAL_FCINFO(locfcinfo, 2);
    6815             :     char       *thresholds_data;
    6816         342 :     int         typlen = typentry->typlen;
    6817         342 :     bool        typbyval = typentry->typbyval;
    6818         342 :     char        typalign = typentry->typalign;
    6819             :     int         left;
    6820             :     int         right;
    6821             : 
    6822         342 :     thresholds_data = (char *) ARR_DATA_PTR(thresholds);
    6823             : 
    6824         342 :     InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
    6825             :                              collation, NULL, NULL);
    6826             : 
    6827             :     /* Find the bucket */
    6828         342 :     left = 0;
    6829         342 :     right = ArrayGetNItems(ARR_NDIM(thresholds), ARR_DIMS(thresholds));
    6830        1068 :     while (left < right)
    6831             :     {
    6832         726 :         int         mid = (left + right) / 2;
    6833             :         char       *ptr;
    6834             :         int         i;
    6835             :         int32       cmpresult;
    6836             : 
    6837             :         /* Locate mid'th array element by advancing from left element */
    6838         726 :         ptr = thresholds_data;
    6839        1242 :         for (i = left; i < mid; i++)
    6840             :         {
    6841         516 :             ptr = att_addlength_pointer(ptr, typlen, ptr);
    6842         516 :             ptr = (char *) att_align_nominal(ptr, typalign);
    6843             :         }
    6844             : 
    6845         726 :         locfcinfo->args[0].value = operand;
    6846         726 :         locfcinfo->args[0].isnull = false;
    6847         726 :         locfcinfo->args[1].value = fetch_att(ptr, typbyval, typlen);
    6848         726 :         locfcinfo->args[1].isnull = false;
    6849             : 
    6850         726 :         cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
    6851             : 
    6852             :         /* We don't expect comparison support functions to return null */
    6853             :         Assert(!locfcinfo->isnull);
    6854             : 
    6855         726 :         if (cmpresult < 0)
    6856         300 :             right = mid;
    6857             :         else
    6858             :         {
    6859         426 :             left = mid + 1;
    6860             : 
    6861             :             /*
    6862             :              * Move the thresholds pointer to match new "left" index, so we
    6863             :              * don't have to seek over those elements again.  This trick
    6864             :              * ensures we do only O(N) array indexing work, not O(N^2).
    6865             :              */
    6866         426 :             ptr = att_addlength_pointer(ptr, typlen, ptr);
    6867         426 :             thresholds_data = (char *) att_align_nominal(ptr, typalign);
    6868             :         }
    6869             :     }
    6870             : 
    6871         342 :     return left;
    6872             : }
    6873             : 
    6874             : /*
    6875             :  * Trim the last N elements from an array by building an appropriate slice.
    6876             :  * Only the first dimension is trimmed.
    6877             :  */
    6878             : Datum
    6879          48 : trim_array(PG_FUNCTION_ARGS)
    6880             : {
    6881          48 :     ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
    6882          48 :     int         n = PG_GETARG_INT32(1);
    6883          48 :     int         array_length = (ARR_NDIM(v) > 0) ? ARR_DIMS(v)[0] : 0;
    6884             :     int16       elmlen;
    6885             :     bool        elmbyval;
    6886             :     char        elmalign;
    6887             :     int         lower[MAXDIM];
    6888             :     int         upper[MAXDIM];
    6889             :     bool        lowerProvided[MAXDIM];
    6890             :     bool        upperProvided[MAXDIM];
    6891             :     Datum       result;
    6892             : 
    6893             :     /* Per spec, throw an error if out of bounds */
    6894          48 :     if (n < 0 || n > array_length)
    6895          18 :         ereport(ERROR,
    6896             :                 (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
    6897             :                  errmsg("number of elements to trim must be between 0 and %d",
    6898             :                         array_length)));
    6899             : 
    6900             :     /* Set all the bounds as unprovided except the first upper bound */
    6901          30 :     memset(lowerProvided, false, sizeof(lowerProvided));
    6902          30 :     memset(upperProvided, false, sizeof(upperProvided));
    6903          30 :     if (ARR_NDIM(v) > 0)
    6904             :     {
    6905          30 :         upper[0] = ARR_LBOUND(v)[0] + array_length - n - 1;
    6906          30 :         upperProvided[0] = true;
    6907             :     }
    6908             : 
    6909             :     /* Fetch the needed information about the element type */
    6910          30 :     get_typlenbyvalalign(ARR_ELEMTYPE(v), &elmlen, &elmbyval, &elmalign);
    6911             : 
    6912             :     /* Get the slice */
    6913          30 :     result = array_get_slice(PointerGetDatum(v), 1,
    6914             :                              upper, lower, upperProvided, lowerProvided,
    6915             :                              -1, elmlen, elmbyval, elmalign);
    6916             : 
    6917          30 :     PG_RETURN_DATUM(result);
    6918             : }

Generated by: LCOV version 1.14