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

Generated by: LCOV version 1.14