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

Generated by: LCOV version 1.16