LCOV - code coverage report
Current view: top level - src/backend/utils/adt - arrayfuncs.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 94.1 % 2365 2225
Test Date: 2026-03-01 15:14:58 Functions: 100.0 % 90 90
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       127701 : array_in(PG_FUNCTION_ARGS)
     182              : {
     183       127701 :     char       *string = PG_GETARG_CSTRING(0);  /* external form */
     184       127701 :     Oid         element_type = PG_GETARG_OID(1);    /* type of an array
     185              :                                                      * element */
     186       127701 :     int32       typmod = PG_GETARG_INT32(2);    /* typmod for array elements */
     187       127701 :     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       127701 :     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
     213       127701 :     if (my_extra == NULL)
     214              :     {
     215        42311 :         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
     216              :                                                       sizeof(ArrayMetaState));
     217        42311 :         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
     218        42311 :         my_extra->element_type = ~element_type;
     219              :     }
     220              : 
     221       127701 :     if (my_extra->element_type != element_type)
     222              :     {
     223              :         /*
     224              :          * Get info about element type, including its input conversion proc
     225              :          */
     226        42331 :         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        42331 :         fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
     231        42331 :                       fcinfo->flinfo->fn_mcxt);
     232        42331 :         my_extra->element_type = element_type;
     233              :     }
     234       127701 :     typlen = my_extra->typlen;
     235       127701 :     typbyval = my_extra->typbyval;
     236       127701 :     typalign = my_extra->typalign;
     237       127701 :     typalignby = typalign_to_alignby(typalign);
     238       127701 :     typdelim = my_extra->typdelim;
     239       127701 :     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       893907 :     for (int i = 0; i < MAXDIM; i++)
     247              :     {
     248       766206 :         dim[i] = -1;            /* indicates "not yet known" */
     249       766206 :         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       127701 :     p = string;
     259       127701 :     if (!ReadArrayDimensions(&p, &ndim, dim, lBound, string, escontext))
     260            0 :         return (Datum) 0;
     261              : 
     262       127680 :     if (ndim == 0)
     263              :     {
     264              :         /* No array dimensions, so next character should be a left brace */
     265       127606 :         if (*p != '{')
     266            7 :             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           74 :         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           74 :         p += strlen(ASSGN);
     281              :         /* Allow whitespace after it */
     282           74 :         while (scanner_isspace(*p))
     283            0 :             p++;
     284              : 
     285           74 :         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       127673 :     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           21 :         return (Datum) 0;
     304              : 
     305              :     /* only whitespace is allowed after the closing brace */
     306       127577 :     while (*p)
     307              :     {
     308            6 :         if (!scanner_isspace(*p++))
     309            6 :             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       127571 :     if (nitems == 0)
     317         2465 :         PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
     318              : 
     319              :     /*
     320              :      * Check for nulls, compute total data space needed
     321              :      */
     322       125106 :     hasnulls = false;
     323       125106 :     nbytes = 0;
     324       745138 :     for (int i = 0; i < nitems; i++)
     325              :     {
     326       620032 :         if (nulls[i])
     327          409 :             hasnulls = true;
     328              :         else
     329              :         {
     330              :             /* let's just make sure data is not toasted */
     331       619623 :             if (typlen == -1)
     332       356895 :                 values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
     333       619623 :             nbytes = att_addlength_datum(nbytes, typlen, values[i]);
     334       619623 :             nbytes = att_nominal_alignby(nbytes, typalignby);
     335              :             /* check for overflow of total request */
     336       619623 :             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       125106 :     if (hasnulls)
     344              :     {
     345          369 :         dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
     346          369 :         nbytes += dataoffset;
     347              :     }
     348              :     else
     349              :     {
     350       124737 :         dataoffset = 0;         /* marker for no null bitmap */
     351       124737 :         nbytes += ARR_OVERHEAD_NONULLS(ndim);
     352              :     }
     353              : 
     354              :     /*
     355              :      * Construct the final array datum
     356              :      */
     357       125106 :     retval = (ArrayType *) palloc0(nbytes);
     358       125106 :     SET_VARSIZE(retval, nbytes);
     359       125106 :     retval->ndim = ndim;
     360       125106 :     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       125106 :     retval->elemtype = element_type;
     368       125106 :     memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
     369       125106 :     memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
     370              : 
     371       125106 :     CopyArrayEls(retval,
     372              :                  values, nulls, nitems,
     373              :                  typlen, typbyval, typalign,
     374              :                  true);
     375              : 
     376       125106 :     pfree(values);
     377       125106 :     pfree(nulls);
     378              : 
     379       125106 :     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       127701 : ReadArrayDimensions(char **srcptr, int *ndim_p, int *dim, int *lBound,
     407              :                     const char *origStr, Node *escontext)
     408              : {
     409       127701 :     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       127701 :     ndim = 0;
     417              :     for (;;)
     418          102 :     {
     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       127809 :         while (scanner_isspace(*p))
     428            6 :             p++;
     429       127803 :         if (*p != '[')
     430       127680 :             break;              /* no more dimension items */
     431          123 :         p++;
     432          123 :         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          123 :         q = p;
     439          123 :         if (!ReadDimensionInt(&p, &i, origStr, escontext))
     440            0 :             return false;
     441          117 :         if (p == q)             /* no digits? */
     442            3 :             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          114 :         if (*p == ':')
     448              :         {
     449              :             /* [m:n] format */
     450          108 :             lBound[ndim] = i;
     451          108 :             p++;
     452          108 :             q = p;
     453          108 :             if (!ReadDimensionInt(&p, &ub, origStr, escontext))
     454            0 :                 return false;
     455          108 :             if (p == q)         /* no digits? */
     456            3 :                 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            6 :             lBound[ndim] = 1;
     465            6 :             ub = i;
     466              :         }
     467          111 :         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          111 :         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          111 :         if (ub < lBound[ndim])
     483            6 :             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          105 :         if (ub == INT_MAX)
     489            3 :             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          204 :         if (pg_sub_s32_overflow(ub, lBound[ndim], &ub) ||
     495          102 :             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          102 :         dim[ndim] = ub;
     502          102 :         ndim++;
     503              :     }
     504              : 
     505       127680 :     *srcptr = p;
     506       127680 :     *ndim_p = ndim;
     507       127680 :     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          231 : ReadDimensionInt(char **srcptr, int *result,
     524              :                  const char *origStr, Node *escontext)
     525              : {
     526          231 :     char       *p = *srcptr;
     527              :     long        l;
     528              : 
     529              :     /* don't accept leading whitespace */
     530          231 :     if (!isdigit((unsigned char) *p) && *p != '-' && *p != '+')
     531              :     {
     532            6 :         *result = 0;
     533            6 :         return true;
     534              :     }
     535              : 
     536          225 :     errno = 0;
     537          225 :     l = strtol(p, srcptr, 10);
     538              : 
     539          225 :     if (errno == ERANGE || l > PG_INT32_MAX || l < PG_INT32_MIN)
     540            6 :         ereturn(escontext, false,
     541              :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     542              :                  errmsg("array bound is out of integer range")));
     543              : 
     544          219 :     *result = (int) l;
     545          219 :     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       127673 : 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       127673 :     int         ndim = *ndim_p;
     600       127673 :     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       127673 :     maxitems = 16;
     613       127673 :     values = palloc_array(Datum, maxitems);
     614       127673 :     nulls = palloc_array(bool, maxitems);
     615              : 
     616              :     /* Allocate workspace to hold (string representation of) one element */
     617       127673 :     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       127673 :     nest_level = 0;
     624       127673 :     nitems = 0;
     625       127673 :     ndim_frozen = dimensions_specified;
     626       127673 :     expect_delim = false;
     627              :     do
     628              :     {
     629              :         ArrayToken  tok;
     630              : 
     631      1372185 :         tok = ReadArrayToken(srcptr, &elembuf, typdelim, origStr, escontext);
     632              : 
     633      1372170 :         switch (tok)
     634              :         {
     635       128580 :             case ATOK_LEVEL_START:
     636              :                 /* Can't write left brace where delim is expected */
     637       128580 :                 if (expect_delim)
     638            3 :                     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       128577 :                 if (nest_level >= MAXDIM)
     645            1 :                     ereturn(escontext, false,
     646              :                             (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     647              :                              errmsg("number of array dimensions exceeds the maximum allowed (%d)",
     648              :                                     MAXDIM)));
     649              : 
     650       128576 :                 nelems[nest_level] = 0;
     651       128576 :                 nest_level++;
     652       128576 :                 if (nest_level > ndim)
     653              :                 {
     654              :                     /* Can't increase ndim once it's frozen */
     655       127897 :                     if (ndim_frozen)
     656            6 :                         goto dimension_error;
     657       127891 :                     ndim = nest_level;
     658              :                 }
     659       128570 :                 break;
     660              : 
     661       128462 :             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       128462 :                 if (nelems[nest_level - 1] > 0 && !expect_delim)
     670           12 :                     ereturn(escontext, false,
     671              :                             (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     672              :                              errmsg("malformed array literal: \"%s\"", origStr),
     673              :                              errdetail("Unexpected \"%c\" character.",
     674              :                                        '}')));
     675       128450 :                 nest_level--;
     676              :                 /* Nested sub-arrays count as elements of outer level */
     677       128450 :                 if (nest_level > 0)
     678          870 :                     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       128450 :                 if (dim[nest_level] < 0)
     686              :                 {
     687              :                     /* Save length of first sub-array of this level */
     688       127777 :                     dim[nest_level] = nelems[nest_level];
     689              :                 }
     690          673 :                 else if (nelems[nest_level] != dim[nest_level])
     691              :                 {
     692              :                     /* Subsequent sub-arrays must have same length */
     693           16 :                     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       128434 :                 expect_delim = true;
     701       128434 :                 break;
     702              : 
     703       494992 :             case ATOK_DELIM:
     704       494992 :                 if (!expect_delim)
     705            3 :                     ereturn(escontext, false,
     706              :                             (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     707              :                              errmsg("malformed array literal: \"%s\"", origStr),
     708              :                              errdetail("Unexpected \"%c\" character.",
     709              :                                        typdelim)));
     710       494989 :                 expect_delim = false;
     711       494989 :                 break;
     712              : 
     713       620133 :             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       620133 :                 if (expect_delim)
     720            3 :                     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       620130 :                 if (nitems >= maxitems)
     727              :                 {
     728         3169 :                     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         3169 :                     maxitems = Min(maxitems * 2, MaxArraySize);
     734         3169 :                     values = repalloc_array(values, Datum, maxitems);
     735         3169 :                     nulls = repalloc_array(nulls, bool, maxitems);
     736              :                 }
     737              : 
     738              :                 /* Read the element's value, or check that NULL is allowed */
     739       620130 :                 if (!InputFunctionCallSafe(inputproc,
     740              :                                            (tok == ATOK_ELEM_NULL) ? NULL : elembuf.data,
     741              :                                            typioparam, typmod,
     742              :                                            escontext,
     743       620130 :                                            &values[nitems]))
     744           18 :                     return false;
     745       620102 :                 nulls[nitems] = (tok == ATOK_ELEM_NULL);
     746       620102 :                 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       620102 :                 ndim_frozen = true;
     754       620102 :                 if (nest_level != ndim)
     755            6 :                     goto dimension_error;
     756              :                 /* Count the new element */
     757       620096 :                 nelems[nest_level - 1]++;
     758              : 
     759              :                 /* Must have a delim or a right brace following */
     760       620096 :                 expect_delim = true;
     761       620096 :                 break;
     762              : 
     763            3 :             case ATOK_ERROR:
     764            3 :                 return false;
     765              :         }
     766      1372089 :     } while (nest_level > 0);
     767              : 
     768              :     /* Clean up and return results */
     769       127577 :     pfree(elembuf.data);
     770              : 
     771       127577 :     *ndim_p = ndim;
     772       127577 :     *nitems_p = nitems;
     773       127577 :     *values_p = values;
     774       127577 :     *nulls_p = nulls;
     775       127577 :     return true;
     776              : 
     777           28 : dimension_error:
     778           28 :     if (dimensions_specified)
     779            3 :         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           25 :         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      1372185 : ReadArrayToken(char **srcptr, StringInfo elembuf, char typdelim,
     801              :                const char *origStr, Node *escontext)
     802              : {
     803      1372185 :     char       *p = *srcptr;
     804              :     int         dstlen;
     805              :     bool        has_escapes;
     806              : 
     807      1372185 :     resetStringInfo(elembuf);
     808              : 
     809              :     /* Identify token type.  Loop advances over leading whitespace. */
     810              :     for (;;)
     811              :     {
     812      1386294 :         switch (*p)
     813              :         {
     814            0 :             case '\0':
     815            0 :                 goto ending_error;
     816       128580 :             case '{':
     817       128580 :                 *srcptr = p + 1;
     818       128580 :                 return ATOK_LEVEL_START;
     819       128462 :             case '}':
     820       128462 :                 *srcptr = p + 1;
     821       128462 :                 return ATOK_LEVEL_END;
     822       281823 :             case '"':
     823       281823 :                 p++;
     824       281823 :                 goto quoted_element;
     825       847429 :             default:
     826       847429 :                 if (*p == typdelim)
     827              :                 {
     828       494992 :                     *srcptr = p + 1;
     829       494992 :                     return ATOK_DELIM;
     830              :                 }
     831       352437 :                 if (scanner_isspace(*p))
     832              :                 {
     833        14109 :                     p++;
     834        14109 :                     continue;
     835              :                 }
     836       338328 :                 goto unquoted_element;
     837              :         }
     838              :     }
     839              : 
     840       281823 : quoted_element:
     841              :     for (;;)
     842              :     {
     843      2160022 :         switch (*p)
     844              :         {
     845            0 :             case '\0':
     846            0 :                 goto ending_error;
     847         2223 :             case '\\':
     848              :                 /* Skip backslash, copy next character as-is. */
     849         2223 :                 p++;
     850         2223 :                 if (*p == '\0')
     851            0 :                     goto ending_error;
     852         2223 :                 appendStringInfoChar(elembuf, *p++);
     853         2223 :                 break;
     854       281823 :             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       281841 :                 while (*(++p) != '\0')
     865              :                 {
     866       281841 :                     if (*p == typdelim || *p == '}' || *p == '{')
     867              :                     {
     868       281814 :                         *srcptr = p;
     869       281814 :                         return ATOK_ELEM;
     870              :                     }
     871           27 :                     if (!scanner_isspace(*p))
     872            9 :                         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      1875976 :             default:
     879      1875976 :                 appendStringInfoChar(elembuf, *p++);
     880      1875976 :                 break;
     881              :         }
     882              :     }
     883              : 
     884       338328 : 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       338328 :     dstlen = 0;
     891       338328 :     has_escapes = false;
     892              :     for (;;)
     893              :     {
     894      1952645 :         switch (*p)
     895              :         {
     896            3 :             case '\0':
     897            3 :                 goto ending_error;
     898            3 :             case '{':
     899            3 :                 ereturn(escontext, ATOK_ERROR,
     900              :                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     901              :                          errmsg("malformed array literal: \"%s\"", origStr),
     902              :                          errdetail("Unexpected \"%c\" character.",
     903              :                                    '{')));
     904            3 :             case '"':
     905              :                 /* Must double-quote all or none of an element. */
     906            3 :                 ereturn(escontext, ATOK_ERROR,
     907              :                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     908              :                          errmsg("malformed array literal: \"%s\"", origStr),
     909              :                          errdetail("Incorrectly quoted array element.")));
     910            9 :             case '\\':
     911              :                 /* Skip backslash, copy next character as-is. */
     912            9 :                 p++;
     913            9 :                 if (*p == '\0')
     914            0 :                     goto ending_error;
     915            9 :                 appendStringInfoChar(elembuf, *p++);
     916            9 :                 dstlen = elembuf->len;   /* treat it as non-whitespace */
     917            9 :                 has_escapes = true;
     918            9 :                 break;
     919      1952627 :             default:
     920              :                 /* End of elem? */
     921      1952627 :                 if (*p == typdelim || *p == '}')
     922              :                 {
     923              :                     /* hack: truncate the output string to dstlen */
     924       338319 :                     elembuf->data[dstlen] = '\0';
     925       338319 :                     elembuf->len = dstlen;
     926       338319 :                     *srcptr = p;
     927              :                     /* Check if it's unquoted "NULL" */
     928       676629 :                     if (Array_nulls && !has_escapes &&
     929       338310 :                         pg_strcasecmp(elembuf->data, "NULL") == 0)
     930          409 :                         return ATOK_ELEM_NULL;
     931              :                     else
     932       337910 :                         return ATOK_ELEM;
     933              :                 }
     934      1614308 :                 appendStringInfoChar(elembuf, *p);
     935      1614308 :                 if (!scanner_isspace(*p))
     936      1613812 :                     dstlen = elembuf->len;
     937      1614308 :                 p++;
     938      1614308 :                 break;
     939              :         }
     940              :     }
     941              : 
     942            3 : ending_error:
     943            3 :     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       916753 : 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       916753 :     char       *p = ARR_DATA_PTR(array);
     975       916753 :     bits8      *bitmap = ARR_NULLBITMAP(array);
     976       916753 :     int         bitval = 0;
     977       916753 :     int         bitmask = 1;
     978       916753 :     uint8       typalignby = typalign_to_alignby(typalign);
     979              :     int         i;
     980              : 
     981       916753 :     if (typbyval)
     982       616370 :         freedata = false;
     983              : 
     984      6653049 :     for (i = 0; i < nitems; i++)
     985              :     {
     986      5736296 :         if (nulls && nulls[i])
     987              :         {
     988        16836 :             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      5719460 :             bitval |= bitmask;
     995      5719460 :             p += ArrayCastAndSet(values[i], typlen, typbyval, typalignby, p);
     996      5719460 :             if (freedata)
     997       369064 :                 pfree(DatumGetPointer(values[i]));
     998              :         }
     999      5736296 :         if (bitmap)
    1000              :         {
    1001       393555 :             bitmask <<= 1;
    1002       393555 :             if (bitmask == 0x100)
    1003              :             {
    1004        47598 :                 *bitmap++ = bitval;
    1005        47598 :                 bitval = 0;
    1006        47598 :                 bitmask = 1;
    1007              :             }
    1008              :         }
    1009              :     }
    1010              : 
    1011       916753 :     if (bitmap && bitmask != 1)
    1012         9088 :         *bitmap = bitval;
    1013       916753 : }
    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       481725 : array_out(PG_FUNCTION_ARGS)
    1022              : {
    1023       481725 :     AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
    1024       481725 :     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       481725 :                 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       481725 :     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
    1060       481725 :     if (my_extra == NULL)
    1061              :     {
    1062        14135 :         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
    1063              :                                                       sizeof(ArrayMetaState));
    1064        14135 :         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
    1065        14135 :         my_extra->element_type = ~element_type;
    1066              :     }
    1067              : 
    1068       481725 :     if (my_extra->element_type != element_type)
    1069              :     {
    1070              :         /*
    1071              :          * Get info about element type, including its output conversion proc
    1072              :          */
    1073        16475 :         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        16475 :         fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
    1078        16475 :                       fcinfo->flinfo->fn_mcxt);
    1079        16475 :         my_extra->element_type = element_type;
    1080              :     }
    1081       481725 :     typlen = my_extra->typlen;
    1082       481725 :     typbyval = my_extra->typbyval;
    1083       481725 :     typalign = my_extra->typalign;
    1084       481725 :     typdelim = my_extra->typdelim;
    1085              : 
    1086       481725 :     ndim = AARR_NDIM(v);
    1087       481725 :     dims = AARR_DIMS(v);
    1088       481725 :     lb = AARR_LBOUND(v);
    1089       481725 :     nitems = ArrayGetNItems(ndim, dims);
    1090              : 
    1091       481725 :     if (nitems == 0)
    1092              :     {
    1093         1744 :         retval = pstrdup("{}");
    1094         1744 :         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       960336 :     for (i = 0; i < ndim; i++)
    1102              :     {
    1103       480509 :         if (lb[i] != 1)
    1104              :         {
    1105          154 :             needdims = true;
    1106          154 :             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       479981 :     values = (char **) palloc(nitems * sizeof(char *));
    1116       479981 :     needquotes = (bool *) palloc(nitems * sizeof(bool));
    1117       479981 :     overall_length = 0;
    1118              : 
    1119       479981 :     array_iter_setup(&iter, v, typlen, typbyval, typalign);
    1120              : 
    1121      1712518 :     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      1232537 :         itemvalue = array_iter_next(&iter, &isnull, i);
    1129              : 
    1130      1232537 :         if (isnull)
    1131              :         {
    1132         1190 :             values[i] = pstrdup("NULL");
    1133         1190 :             overall_length += 4;
    1134         1190 :             needquote = false;
    1135              :         }
    1136              :         else
    1137              :         {
    1138      1231347 :             values[i] = OutputFunctionCall(&my_extra->proc, itemvalue);
    1139              : 
    1140              :             /* count data plus backslashes; detect chars needing quotes */
    1141      1231347 :             if (values[i][0] == '\0')
    1142          270 :                 needquote = true;   /* force quotes for empty string */
    1143      1231077 :             else if (pg_strcasecmp(values[i], "NULL") == 0)
    1144           10 :                 needquote = true;   /* force quotes for literal NULL */
    1145              :             else
    1146      1231067 :                 needquote = false;
    1147              : 
    1148     18717961 :             for (tmp = values[i]; *tmp != '\0'; tmp++)
    1149              :             {
    1150     17486614 :                 char        ch = *tmp;
    1151              : 
    1152     17486614 :                 overall_length += 1;
    1153     17486614 :                 if (ch == '"' || ch == '\\')
    1154              :                 {
    1155         3916 :                     needquote = true;
    1156         3916 :                     overall_length += 1;
    1157              :                 }
    1158     34936637 :                 else if (ch == '{' || ch == '}' || ch == typdelim ||
    1159     17453939 :                          scanner_isspace(ch))
    1160       131269 :                     needquote = true;
    1161              :             }
    1162              :         }
    1163              : 
    1164      1232537 :         needquotes[i] = needquote;
    1165              : 
    1166              :         /* Count the pair of double quotes, if needed */
    1167      1232537 :         if (needquote)
    1168        15730 :             overall_length += 2;
    1169              :         /* and the comma (or other typdelim delimiter) */
    1170      1232537 :         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       960523 :     for (i = j = 0, k = 1; i < ndim; i++)
    1180              :     {
    1181       480542 :         j += k, k *= dims[i];
    1182              :     }
    1183       479981 :     overall_length += 2 * j;
    1184              : 
    1185              :     /* Format explicit dimensions if required */
    1186       479981 :     dims_str[0] = '\0';
    1187       479981 :     if (needdims)
    1188              :     {
    1189          154 :         char       *ptr = dims_str;
    1190              : 
    1191          342 :         for (i = 0; i < ndim; i++)
    1192              :         {
    1193          188 :             sprintf(ptr, "[%d:%d]", lb[i], lb[i] + dims[i] - 1);
    1194          188 :             ptr += strlen(ptr);
    1195              :         }
    1196          154 :         *ptr++ = *ASSGN;
    1197          154 :         *ptr = '\0';
    1198          154 :         overall_length += ptr - dims_str;
    1199              :     }
    1200              : 
    1201              :     /* Now construct the output string */
    1202       479981 :     retval = (char *) palloc(overall_length);
    1203       479981 :     p = retval;
    1204              : 
    1205              : #define APPENDSTR(str)  (strcpy(p, (str)), p += strlen(p))
    1206              : #define APPENDCHAR(ch)  (*p++ = (ch), *p = '\0')
    1207              : 
    1208       479981 :     if (needdims)
    1209          154 :         APPENDSTR(dims_str);
    1210       479981 :     APPENDCHAR('{');
    1211       960523 :     for (i = 0; i < ndim; i++)
    1212       480542 :         indx[i] = 0;
    1213       479981 :     j = 0;
    1214       479981 :     k = 0;
    1215              :     do
    1216              :     {
    1217      1237129 :         for (i = j; i < ndim - 1; i++)
    1218         4592 :             APPENDCHAR('{');
    1219              : 
    1220      1232537 :         if (needquotes[k])
    1221              :         {
    1222        15730 :             APPENDCHAR('"');
    1223       680440 :             for (tmp = values[k]; *tmp; tmp++)
    1224              :             {
    1225       664710 :                 char        ch = *tmp;
    1226              : 
    1227       664710 :                 if (ch == '"' || ch == '\\')
    1228         3916 :                     *p++ = '\\';
    1229       664710 :                 *p++ = ch;
    1230              :             }
    1231        15730 :             *p = '\0';
    1232        15730 :             APPENDCHAR('"');
    1233              :         }
    1234              :         else
    1235      1216807 :             APPENDSTR(values[k]);
    1236      1232537 :         pfree(values[k++]);
    1237              : 
    1238      1717110 :         for (i = ndim - 1; i >= 0; i--)
    1239              :         {
    1240      1237129 :             if (++(indx[i]) < dims[i])
    1241              :             {
    1242       752556 :                 APPENDCHAR(typdelim);
    1243       752556 :                 break;
    1244              :             }
    1245              :             else
    1246              :             {
    1247       484573 :                 indx[i] = 0;
    1248       484573 :                 APPENDCHAR('}');
    1249              :             }
    1250              :         }
    1251      1232537 :         j = i;
    1252      1232537 :     } 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       479981 :     pfree(values);
    1261       479981 :     pfree(needquotes);
    1262              : 
    1263       479981 :     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           31 : array_recv(PG_FUNCTION_ARGS)
    1276              : {
    1277           31 :     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
    1278           31 :     Oid         spec_element_type = PG_GETARG_OID(1);   /* type of an array
    1279              :                                                          * element */
    1280           31 :     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           31 :     ndim = pq_getmsgint(buf, 4);
    1302           31 :     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           31 :     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           31 :     flags = pq_getmsgint(buf, 4);
    1313           31 :     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           31 :     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           31 :     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           62 :     for (i = 0; i < ndim; i++)
    1349              :     {
    1350           31 :         dim[i] = pq_getmsgint(buf, 4);
    1351           31 :         lBound[i] = pq_getmsgint(buf, 4);
    1352              :     }
    1353              : 
    1354              :     /* This checks for overflow of array dimensions */
    1355           31 :     nitems = ArrayGetNItems(ndim, dim);
    1356           31 :     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           31 :     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
    1364           31 :     if (my_extra == NULL)
    1365              :     {
    1366           28 :         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
    1367              :                                                       sizeof(ArrayMetaState));
    1368           28 :         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
    1369           28 :         my_extra->element_type = ~element_type;
    1370              :     }
    1371              : 
    1372           31 :     if (my_extra->element_type != element_type)
    1373              :     {
    1374              :         /* Get info about element type, including its receive proc */
    1375           28 :         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           28 :         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           28 :         fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
    1385           28 :                       fcinfo->flinfo->fn_mcxt);
    1386           28 :         my_extra->element_type = element_type;
    1387              :     }
    1388              : 
    1389           31 :     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           31 :     typlen = my_extra->typlen;
    1396           31 :     typbyval = my_extra->typbyval;
    1397           31 :     typalign = my_extra->typalign;
    1398           31 :     typioparam = my_extra->typioparam;
    1399              : 
    1400           31 :     dataPtr = (Datum *) palloc(nitems * sizeof(Datum));
    1401           31 :     nullsPtr = (bool *) palloc(nitems * sizeof(bool));
    1402           31 :     ReadArrayBinary(buf, nitems,
    1403              :                     &my_extra->proc, typioparam, typmod,
    1404              :                     typlen, typbyval, typalign,
    1405              :                     dataPtr, nullsPtr,
    1406              :                     &hasnulls, &nbytes);
    1407           31 :     if (hasnulls)
    1408              :     {
    1409            0 :         dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
    1410            0 :         nbytes += dataoffset;
    1411              :     }
    1412              :     else
    1413              :     {
    1414           31 :         dataoffset = 0;         /* marker for no null bitmap */
    1415           31 :         nbytes += ARR_OVERHEAD_NONULLS(ndim);
    1416              :     }
    1417           31 :     retval = (ArrayType *) palloc0(nbytes);
    1418           31 :     SET_VARSIZE(retval, nbytes);
    1419           31 :     retval->ndim = ndim;
    1420           31 :     retval->dataoffset = dataoffset;
    1421           31 :     retval->elemtype = element_type;
    1422           31 :     memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
    1423           31 :     memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
    1424              : 
    1425           31 :     CopyArrayEls(retval,
    1426              :                  dataPtr, nullsPtr, nitems,
    1427              :                  typlen, typbyval, typalign,
    1428              :                  true);
    1429              : 
    1430           31 :     pfree(dataPtr);
    1431           31 :     pfree(nullsPtr);
    1432              : 
    1433           31 :     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           31 : 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           31 :     uint8       typalignby = typalign_to_alignby(typalign);
    1475              : 
    1476          124 :     for (i = 0; i < nitems; i++)
    1477              :     {
    1478              :         int         itemlen;
    1479              :         StringInfoData elem_buf;
    1480              : 
    1481              :         /* Get and check the item length */
    1482           93 :         itemlen = pq_getmsgint(buf, 4);
    1483           93 :         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           93 :         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           93 :         initReadOnlyStringInfo(&elem_buf, &buf->data[buf->cursor], itemlen);
    1502              : 
    1503           93 :         buf->cursor += itemlen;
    1504              : 
    1505              :         /* Now call the element's receiveproc */
    1506           93 :         values[i] = ReceiveFunctionCall(receiveproc, &elem_buf,
    1507              :                                         typioparam, typmod);
    1508           93 :         nulls[i] = false;
    1509              : 
    1510              :         /* Trouble if it didn't eat the whole buffer */
    1511           93 :         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           31 :     hasnull = false;
    1522           31 :     totbytes = 0;
    1523          124 :     for (i = 0; i < nitems; i++)
    1524              :     {
    1525           93 :         if (nulls[i])
    1526            0 :             hasnull = true;
    1527              :         else
    1528              :         {
    1529              :             /* let's just make sure data is not toasted */
    1530           93 :             if (typlen == -1)
    1531           54 :                 values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
    1532           93 :             totbytes = att_addlength_datum(totbytes, typlen, values[i]);
    1533           93 :             totbytes = att_nominal_alignby(totbytes, typalignby);
    1534              :             /* check for overflow of total request */
    1535           93 :             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           31 :     *hasnulls = hasnull;
    1543           31 :     *nbytes = totbytes;
    1544           31 : }
    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           23 : array_send(PG_FUNCTION_ARGS)
    1554              : {
    1555           23 :     AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
    1556           23 :     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           23 :     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
    1575           23 :     if (my_extra == NULL)
    1576              :     {
    1577           20 :         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
    1578              :                                                       sizeof(ArrayMetaState));
    1579           20 :         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
    1580           20 :         my_extra->element_type = ~element_type;
    1581              :     }
    1582              : 
    1583           23 :     if (my_extra->element_type != element_type)
    1584              :     {
    1585              :         /* Get info about element type, including its send proc */
    1586           20 :         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           20 :         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           20 :         fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
    1596           20 :                       fcinfo->flinfo->fn_mcxt);
    1597           20 :         my_extra->element_type = element_type;
    1598              :     }
    1599           23 :     typlen = my_extra->typlen;
    1600           23 :     typbyval = my_extra->typbyval;
    1601           23 :     typalign = my_extra->typalign;
    1602              : 
    1603           23 :     ndim = AARR_NDIM(v);
    1604           23 :     dim = AARR_DIMS(v);
    1605           23 :     lb = AARR_LBOUND(v);
    1606           23 :     nitems = ArrayGetNItems(ndim, dim);
    1607              : 
    1608           23 :     pq_begintypsend(&buf);
    1609              : 
    1610              :     /* Send the array header information */
    1611           23 :     pq_sendint32(&buf, ndim);
    1612           23 :     pq_sendint32(&buf, AARR_HASNULL(v) ? 1 : 0);
    1613           23 :     pq_sendint32(&buf, element_type);
    1614           46 :     for (i = 0; i < ndim; i++)
    1615              :     {
    1616           23 :         pq_sendint32(&buf, dim[i]);
    1617           23 :         pq_sendint32(&buf, lb[i]);
    1618              :     }
    1619              : 
    1620              :     /* Send the array elements using the element's own sendproc */
    1621           23 :     array_iter_setup(&iter, v, typlen, typbyval, typalign);
    1622              : 
    1623           92 :     for (i = 0; i < nitems; i++)
    1624              :     {
    1625              :         Datum       itemvalue;
    1626              :         bool        isnull;
    1627              : 
    1628              :         /* Get source element, checking for NULL */
    1629           69 :         itemvalue = array_iter_next(&iter, &isnull, i);
    1630              : 
    1631           69 :         if (isnull)
    1632              :         {
    1633              :             /* -1 length means a NULL */
    1634            0 :             pq_sendint32(&buf, -1);
    1635              :         }
    1636              :         else
    1637              :         {
    1638              :             bytea      *outputbytes;
    1639              : 
    1640           69 :             outputbytes = SendFunctionCall(&my_extra->proc, itemvalue);
    1641           69 :             pq_sendint32(&buf, VARSIZE(outputbytes) - VARHDRSZ);
    1642           69 :             pq_sendbytes(&buf, VARDATA(outputbytes),
    1643           69 :                          VARSIZE(outputbytes) - VARHDRSZ);
    1644           69 :             pfree(outputbytes);
    1645              :         }
    1646              :     }
    1647              : 
    1648           23 :     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          984 : array_ndims(PG_FUNCTION_ARGS)
    1657              : {
    1658          984 :     AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
    1659              : 
    1660              :     /* Sanity check: does it look like an array at all? */
    1661          984 :     if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
    1662            6 :         PG_RETURN_NULL();
    1663              : 
    1664          978 :     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         4750 : array_dims(PG_FUNCTION_ARGS)
    1673              : {
    1674         4750 :     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         4750 :     if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
    1689           34 :         PG_RETURN_NULL();
    1690              : 
    1691         4716 :     dimv = AARR_DIMS(v);
    1692         4716 :     lb = AARR_LBOUND(v);
    1693              : 
    1694         4716 :     p = buf;
    1695         9486 :     for (i = 0; i < AARR_NDIM(v); i++)
    1696              :     {
    1697         4770 :         sprintf(p, "[%d:%d]", lb[i], dimv[i] + lb[i] - 1);
    1698         4770 :         p += strlen(p);
    1699              :     }
    1700              : 
    1701         4716 :     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        13008 : array_lower(PG_FUNCTION_ARGS)
    1711              : {
    1712        13008 :     AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
    1713        13008 :     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        13008 :     if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
    1719            0 :         PG_RETURN_NULL();
    1720              : 
    1721              :     /* Sanity check: was the requested dim valid */
    1722        13008 :     if (reqdim <= 0 || reqdim > AARR_NDIM(v))
    1723            0 :         PG_RETURN_NULL();
    1724              : 
    1725        13008 :     lb = AARR_LBOUND(v);
    1726        13008 :     result = lb[reqdim - 1];
    1727              : 
    1728        13008 :     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        13353 : array_upper(PG_FUNCTION_ARGS)
    1738              : {
    1739        13353 :     AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
    1740        13353 :     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        13353 :     if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
    1747           15 :         PG_RETURN_NULL();
    1748              : 
    1749              :     /* Sanity check: was the requested dim valid */
    1750        13338 :     if (reqdim <= 0 || reqdim > AARR_NDIM(v))
    1751            0 :         PG_RETURN_NULL();
    1752              : 
    1753        13338 :     lb = AARR_LBOUND(v);
    1754        13338 :     dimv = AARR_DIMS(v);
    1755              : 
    1756        13338 :     result = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
    1757              : 
    1758        13338 :     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        57045 : array_length(PG_FUNCTION_ARGS)
    1768              : {
    1769        57045 :     AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
    1770        57045 :     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        57045 :     if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
    1776            0 :         PG_RETURN_NULL();
    1777              : 
    1778              :     /* Sanity check: was the requested dim valid */
    1779        57045 :     if (reqdim <= 0 || reqdim > AARR_NDIM(v))
    1780            6 :         PG_RETURN_NULL();
    1781              : 
    1782        57039 :     dimv = AARR_DIMS(v);
    1783              : 
    1784        57039 :     result = dimv[reqdim - 1];
    1785              : 
    1786        57039 :     PG_RETURN_INT32(result);
    1787              : }
    1788              : 
    1789              : /*
    1790              :  * array_cardinality:
    1791              :  *      returns the total number of elements in an array
    1792              :  */
    1793              : Datum
    1794         1266 : array_cardinality(PG_FUNCTION_ARGS)
    1795              : {
    1796         1266 :     AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
    1797              : 
    1798         1266 :     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       471478 : 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       471478 :     if (arraytyplen > 0)
    1845              :     {
    1846              :         /*
    1847              :          * fixed-length arrays -- these are assumed to be 1-d, 0-based
    1848              :          */
    1849       212927 :         ndim = 1;
    1850       212927 :         fixedDim[0] = arraytyplen / elmlen;
    1851       212927 :         fixedLb[0] = 0;
    1852       212927 :         dim = fixedDim;
    1853       212927 :         lb = fixedLb;
    1854       212927 :         arraydataptr = (char *) DatumGetPointer(arraydatum);
    1855       212927 :         arraynullsptr = NULL;
    1856              :     }
    1857       258551 :     else if (VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(arraydatum)))
    1858              :     {
    1859              :         /* expanded array: let's do this in a separate function */
    1860         2507 :         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       256044 :         ArrayType  *array = DatumGetArrayTypeP(arraydatum);
    1873              : 
    1874       256044 :         ndim = ARR_NDIM(array);
    1875       256044 :         dim = ARR_DIMS(array);
    1876       256044 :         lb = ARR_LBOUND(array);
    1877       256044 :         arraydataptr = ARR_DATA_PTR(array);
    1878       256044 :         arraynullsptr = ARR_NULLBITMAP(array);
    1879              :     }
    1880              : 
    1881              :     /*
    1882              :      * Return NULL for invalid subscript
    1883              :      */
    1884       468971 :     if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
    1885              :     {
    1886           48 :         *isNull = true;
    1887           48 :         return (Datum) 0;
    1888              :     }
    1889       911278 :     for (i = 0; i < ndim; i++)
    1890              :     {
    1891       468971 :         if (indx[i] < lb[i] || indx[i] >= (dim[i] + lb[i]))
    1892              :         {
    1893        26616 :             *isNull = true;
    1894        26616 :             return (Datum) 0;
    1895              :         }
    1896              :     }
    1897              : 
    1898              :     /*
    1899              :      * Calculate the element number
    1900              :      */
    1901       442307 :     offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
    1902              : 
    1903              :     /*
    1904              :      * Check for NULL array element
    1905              :      */
    1906       442307 :     if (array_get_isnull(arraynullsptr, offset))
    1907              :     {
    1908           36 :         *isNull = true;
    1909           36 :         return (Datum) 0;
    1910              :     }
    1911              : 
    1912              :     /*
    1913              :      * OK, get the element
    1914              :      */
    1915       442271 :     *isNull = false;
    1916       442271 :     retptr = array_seek(arraydataptr, 0, arraynullsptr, offset,
    1917              :                         elmlen, elmbyval, elmalign);
    1918       442271 :     return ArrayCast(retptr, elmbyval, elmlen);
    1919              : }
    1920              : 
    1921              : /*
    1922              :  * Implementation of array_get_element() for an expanded array
    1923              :  */
    1924              : static Datum
    1925         2507 : 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         2507 :     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         2507 :     ndim = eah->ndims;
    1950         2507 :     dim = eah->dims;
    1951         2507 :     lb = eah->lbound;
    1952              : 
    1953              :     /*
    1954              :      * Return NULL for invalid subscript
    1955              :      */
    1956         2507 :     if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
    1957              :     {
    1958            3 :         *isNull = true;
    1959            3 :         return (Datum) 0;
    1960              :     }
    1961         5008 :     for (i = 0; i < ndim; i++)
    1962              :     {
    1963         2504 :         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         2504 :     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         2504 :     deconstruct_expanded_array(eah);
    1980              : 
    1981         2504 :     dvalues = eah->dvalues;
    1982         2504 :     dnulls = eah->dnulls;
    1983              : 
    1984              :     /*
    1985              :      * Check for NULL array element
    1986              :      */
    1987         2504 :     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         2504 :     *isNull = false;
    2000         2504 :     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          204 : 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          204 :     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           12 :         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          192 :         array = DatumGetArrayTypeP(arraydatum);
    2091              : 
    2092          192 :         ndim = ARR_NDIM(array);
    2093          192 :         dim = ARR_DIMS(array);
    2094          192 :         lb = ARR_LBOUND(array);
    2095          192 :         elemtype = ARR_ELEMTYPE(array);
    2096          192 :         arraydataptr = ARR_DATA_PTR(array);
    2097          192 :         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          192 :     if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
    2106           48 :         return PointerGetDatum(construct_empty_array(elemtype));
    2107              : 
    2108          330 :     for (i = 0; i < nSubscripts; i++)
    2109              :     {
    2110          192 :         if (!lowerProvided[i] || lowerIndx[i] < lb[i])
    2111           63 :             lowerIndx[i] = lb[i];
    2112          192 :         if (!upperProvided[i] || upperIndx[i] >= (dim[i] + lb[i]))
    2113           36 :             upperIndx[i] = dim[i] + lb[i] - 1;
    2114          192 :         if (lowerIndx[i] > upperIndx[i])
    2115            6 :             return PointerGetDatum(construct_empty_array(elemtype));
    2116              :     }
    2117              :     /* fill any missing subscript positions with full array range */
    2118          162 :     for (; i < ndim; i++)
    2119              :     {
    2120           24 :         lowerIndx[i] = lb[i];
    2121           24 :         upperIndx[i] = dim[i] + lb[i] - 1;
    2122           24 :         if (lowerIndx[i] > upperIndx[i])
    2123            0 :             return PointerGetDatum(construct_empty_array(elemtype));
    2124              :     }
    2125              : 
    2126          138 :     mda_get_range(ndim, span, lowerIndx, upperIndx);
    2127              : 
    2128          138 :     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          138 :     if (arraynullsptr)
    2138              :     {
    2139           18 :         dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, ArrayGetNItems(ndim, span));
    2140           18 :         bytes += dataoffset;
    2141              :     }
    2142              :     else
    2143              :     {
    2144          120 :         dataoffset = 0;         /* marker for no null bitmap */
    2145          120 :         bytes += ARR_OVERHEAD_NONULLS(ndim);
    2146              :     }
    2147              : 
    2148          138 :     newarray = (ArrayType *) palloc0(bytes);
    2149          138 :     SET_VARSIZE(newarray, bytes);
    2150          138 :     newarray->ndim = ndim;
    2151          138 :     newarray->dataoffset = dataoffset;
    2152          138 :     newarray->elemtype = elemtype;
    2153          138 :     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          138 :     newlb = ARR_LBOUND(newarray);
    2160          348 :     for (i = 0; i < ndim; i++)
    2161          210 :         newlb[i] = 1;
    2162              : 
    2163          138 :     array_extract_slice(newarray,
    2164              :                         ndim, dim, lb,
    2165              :                         arraydataptr, arraynullsptr,
    2166              :                         lowerIndx, upperIndx,
    2167              :                         elmlen, elmbyval, elmalign);
    2168              : 
    2169          138 :     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         2089 : 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         2089 :     uint8       elmalignby = typalign_to_alignby(elmalign);
    2238              : 
    2239         2089 :     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            9 :         if (nSubscripts != 1)
    2248            0 :             ereport(ERROR,
    2249              :                     (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    2250              :                      errmsg("wrong number of array subscripts")));
    2251              : 
    2252            9 :         if (indx[0] < 0 || indx[0] >= arraytyplen / elmlen)
    2253            3 :             ereport(ERROR,
    2254              :                     (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    2255              :                      errmsg("array subscript out of range")));
    2256              : 
    2257            6 :         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            6 :         resultarray = (char *) palloc(arraytyplen);
    2263            6 :         memcpy(resultarray, DatumGetPointer(arraydatum), arraytyplen);
    2264            6 :         elt_ptr = resultarray + indx[0] * elmlen;
    2265            6 :         ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalignby, elt_ptr);
    2266            6 :         return PointerGetDatum(resultarray);
    2267              :     }
    2268              : 
    2269         2080 :     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         2080 :     if (elmlen == -1 && !isNull)
    2276         1313 :         dataValue = PointerGetDatum(PG_DETOAST_DATUM(dataValue));
    2277              : 
    2278         2080 :     if (VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(arraydatum)))
    2279              :     {
    2280              :         /* expanded array: let's do this in a separate function */
    2281         1008 :         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         1072 :     array = DatumGetArrayTypeP(arraydatum);
    2294              : 
    2295         1072 :     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         1072 :     if (ndim == 0)
    2303              :     {
    2304          174 :         Oid         elmtype = ARR_ELEMTYPE(array);
    2305              : 
    2306          349 :         for (i = 0; i < nSubscripts; i++)
    2307              :         {
    2308          175 :             dim[i] = 1;
    2309          175 :             lb[i] = indx[i];
    2310              :         }
    2311              : 
    2312          174 :         return PointerGetDatum(construct_md_array(&dataValue, &isNull,
    2313              :                                                   nSubscripts, dim, lb,
    2314              :                                                   elmtype,
    2315              :                                                   elmlen, elmbyval, elmalign));
    2316              :     }
    2317              : 
    2318          898 :     if (ndim != nSubscripts)
    2319            3 :         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          895 :     memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
    2325          895 :     memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
    2326              : 
    2327          895 :     newhasnulls = (ARR_HASNULL(array) || isNull);
    2328          895 :     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          895 :     if (ndim == 1)
    2337              :     {
    2338          892 :         if (indx[0] < lb[0])
    2339              :         {
    2340              :             /* addedbefore = lb[0] - indx[0]; */
    2341              :             /* dim[0] += addedbefore; */
    2342           24 :             if (pg_sub_s32_overflow(lb[0], indx[0], &addedbefore) ||
    2343           12 :                 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           12 :             lb[0] = indx[0];
    2349           12 :             if (addedbefore > 1)
    2350            6 :                 newhasnulls = true; /* will insert nulls */
    2351              :         }
    2352          892 :         if (indx[0] >= (dim[0] + lb[0]))
    2353              :         {
    2354              :             /* addedafter = indx[0] - (dim[0] + lb[0]) + 1; */
    2355              :             /* dim[0] += addedafter; */
    2356         1413 :             if (pg_sub_s32_overflow(indx[0], dim[0] + lb[0], &addedafter) ||
    2357         1410 :                 pg_add_s32_overflow(addedafter, 1, &addedafter) ||
    2358          705 :                 pg_add_s32_overflow(dim[0], addedafter, &dim[0]))
    2359            3 :                 ereport(ERROR,
    2360              :                         (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    2361              :                          errmsg("array size exceeds the maximum allowed (%zu)",
    2362              :                                 MaxArraySize)));
    2363          705 :             if (addedafter > 1)
    2364           18 :                 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            9 :         for (i = 0; i < ndim; i++)
    2374              :         {
    2375            6 :             if (indx[i] < lb[i] ||
    2376            6 :                 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          892 :     newnitems = ArrayGetNItems(ndim, dim);
    2385          892 :     ArrayCheckBounds(ndim, dim, lb);
    2386              : 
    2387              :     /*
    2388              :      * Compute sizes of items and areas to copy
    2389              :      */
    2390          892 :     if (newhasnulls)
    2391           67 :         overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, newnitems);
    2392              :     else
    2393          825 :         overheadlen = ARR_OVERHEAD_NONULLS(ndim);
    2394          892 :     oldnitems = ArrayGetNItems(ndim, ARR_DIMS(array));
    2395          892 :     oldnullbitmap = ARR_NULLBITMAP(array);
    2396          892 :     oldoverheadlen = ARR_DATA_OFFSET(array);
    2397          892 :     olddatasize = ARR_SIZE(array) - oldoverheadlen;
    2398          892 :     if (addedbefore)
    2399              :     {
    2400           12 :         offset = 0;
    2401           12 :         lenbefore = 0;
    2402           12 :         olditemlen = 0;
    2403           12 :         lenafter = olddatasize;
    2404              :     }
    2405          880 :     else if (addedafter)
    2406              :     {
    2407          705 :         offset = oldnitems;
    2408          705 :         lenbefore = olddatasize;
    2409          705 :         olditemlen = 0;
    2410          705 :         lenafter = 0;
    2411              :     }
    2412              :     else
    2413              :     {
    2414          175 :         offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
    2415          175 :         elt_ptr = array_seek(ARR_DATA_PTR(array), 0, oldnullbitmap, offset,
    2416              :                              elmlen, elmbyval, elmalign);
    2417          175 :         lenbefore = (int) (elt_ptr - ARR_DATA_PTR(array));
    2418          175 :         if (array_get_isnull(oldnullbitmap, offset))
    2419           12 :             olditemlen = 0;
    2420              :         else
    2421              :         {
    2422          163 :             olditemlen = att_addlength_pointer(0, elmlen, elt_ptr);
    2423          163 :             olditemlen = att_nominal_alignby(olditemlen, elmalignby);
    2424              :         }
    2425          175 :         lenafter = olddatasize - lenbefore - olditemlen;
    2426              :     }
    2427              : 
    2428          892 :     if (isNull)
    2429           10 :         newitemlen = 0;
    2430              :     else
    2431              :     {
    2432          882 :         newitemlen = att_addlength_datum(0, elmlen, dataValue);
    2433          882 :         newitemlen = att_nominal_alignby(newitemlen, elmalignby);
    2434              :     }
    2435              : 
    2436          892 :     newsize = overheadlen + lenbefore + newitemlen + lenafter;
    2437              : 
    2438              :     /*
    2439              :      * OK, create the new array and fill in header/dimensions
    2440              :      */
    2441          892 :     newarray = (ArrayType *) palloc0(newsize);
    2442          892 :     SET_VARSIZE(newarray, newsize);
    2443          892 :     newarray->ndim = ndim;
    2444          892 :     newarray->dataoffset = newhasnulls ? overheadlen : 0;
    2445          892 :     newarray->elemtype = ARR_ELEMTYPE(array);
    2446          892 :     memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
    2447          892 :     memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
    2448              : 
    2449              :     /*
    2450              :      * Fill in data
    2451              :      */
    2452          892 :     memcpy((char *) newarray + overheadlen,
    2453              :            (char *) array + oldoverheadlen,
    2454              :            lenbefore);
    2455          892 :     if (!isNull)
    2456          882 :         ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalignby,
    2457          882 :                         (char *) newarray + overheadlen + lenbefore);
    2458          892 :     memcpy((char *) newarray + overheadlen + lenbefore + newitemlen,
    2459          892 :            (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          892 :     if (newhasnulls)
    2469              :     {
    2470           67 :         bits8      *newnullbitmap = ARR_NULLBITMAP(newarray);
    2471              : 
    2472              :         /* palloc0 above already marked any inserted positions as nulls */
    2473              :         /* Fix the inserted value */
    2474           67 :         if (addedafter)
    2475           28 :             array_set_isnull(newnullbitmap, newnitems - 1, isNull);
    2476              :         else
    2477           39 :             array_set_isnull(newnullbitmap, offset, isNull);
    2478              :         /* Fix the copied range(s) */
    2479           67 :         if (addedbefore)
    2480           12 :             array_bitmap_copy(newnullbitmap, addedbefore,
    2481              :                               oldnullbitmap, 0,
    2482              :                               oldnitems);
    2483              :         else
    2484              :         {
    2485           55 :             array_bitmap_copy(newnullbitmap, 0,
    2486              :                               oldnullbitmap, 0,
    2487              :                               offset);
    2488           55 :             if (addedafter == 0)
    2489           27 :                 array_bitmap_copy(newnullbitmap, offset + 1,
    2490              :                                   oldnullbitmap, offset + 1,
    2491           27 :                                   oldnitems - offset - 1);
    2492              :         }
    2493              :     }
    2494              : 
    2495          892 :     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         1008 : 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         1008 :     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         1008 :     ndim = eah->ndims;
    2541              :     Assert(ndim >= 0 && ndim <= MAXDIM);
    2542         1008 :     memcpy(dim, eah->dims, ndim * sizeof(int));
    2543         1008 :     memcpy(lb, eah->lbound, ndim * sizeof(int));
    2544         1008 :     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         1008 :     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          208 :         eah->dims = (int *) MemoryContextAllocZero(eah->hdr.eoh_context,
    2559              :                                                    nSubscripts * sizeof(int));
    2560          208 :         eah->lbound = (int *) MemoryContextAllocZero(eah->hdr.eoh_context,
    2561              :                                                      nSubscripts * sizeof(int));
    2562              : 
    2563              :         /* Update local copies of dimension info */
    2564          208 :         ndim = nSubscripts;
    2565          416 :         for (i = 0; i < nSubscripts; i++)
    2566              :         {
    2567          208 :             dim[i] = 0;
    2568          208 :             lb[i] = indx[i];
    2569              :         }
    2570          208 :         dimschanged = true;
    2571              :     }
    2572          800 :     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         1008 :     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         1008 :     if (!eah->typbyval && !isNull)
    2594              :     {
    2595          449 :         MemoryContext oldcxt = MemoryContextSwitchTo(eah->hdr.eoh_context);
    2596              : 
    2597          449 :         dataValue = datumCopy(dataValue, false, eah->typlen);
    2598          449 :         MemoryContextSwitchTo(oldcxt);
    2599              :     }
    2600              : 
    2601         1008 :     dvalues = eah->dvalues;
    2602         1008 :     dnulls = eah->dnulls;
    2603              : 
    2604         1008 :     newhasnulls = ((dnulls != NULL) || isNull);
    2605         1008 :     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         1008 :     if (ndim == 1)
    2614              :     {
    2615         1008 :         if (indx[0] < lb[0])
    2616              :         {
    2617              :             /* addedbefore = lb[0] - indx[0]; */
    2618              :             /* dim[0] += addedbefore; */
    2619           80 :             if (pg_sub_s32_overflow(lb[0], indx[0], &addedbefore) ||
    2620           40 :                 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           40 :             lb[0] = indx[0];
    2626           40 :             dimschanged = true;
    2627           40 :             if (addedbefore > 1)
    2628            0 :                 newhasnulls = true; /* will insert nulls */
    2629              :         }
    2630         1008 :         if (indx[0] >= (dim[0] + lb[0]))
    2631              :         {
    2632              :             /* addedafter = indx[0] - (dim[0] + lb[0]) + 1; */
    2633              :             /* dim[0] += addedafter; */
    2634         1883 :             if (pg_sub_s32_overflow(indx[0], dim[0] + lb[0], &addedafter) ||
    2635         1880 :                 pg_add_s32_overflow(addedafter, 1, &addedafter) ||
    2636          940 :                 pg_add_s32_overflow(dim[0], addedafter, &dim[0]))
    2637            3 :                 ereport(ERROR,
    2638              :                         (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    2639              :                          errmsg("array size exceeds the maximum allowed (%zu)",
    2640              :                                 MaxArraySize)));
    2641          940 :             dimschanged = true;
    2642          940 :             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         1005 :     if (dimschanged)
    2664              :     {
    2665          980 :         (void) ArrayGetNItems(ndim, dim);
    2666          980 :         ArrayCheckBounds(ndim, dim, lb);
    2667              :     }
    2668              : 
    2669              :     /* Now we can calculate linear offset of target item in array */
    2670         1005 :     offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
    2671              : 
    2672              :     /* Physically enlarge existing dvalues/dnulls arrays if needed */
    2673         1005 :     if (dim[0] > eah->dvalueslen)
    2674              :     {
    2675              :         /* We want some extra space if we're enlarging */
    2676          974 :         int         newlen = dim[0] + dim[0] / 8;
    2677              : 
    2678          974 :         newlen = Max(newlen, dim[0]);   /* integer overflow guard */
    2679          974 :         eah->dvalues = dvalues = (Datum *)
    2680          974 :             repalloc(dvalues, newlen * sizeof(Datum));
    2681          974 :         if (dnulls)
    2682            0 :             eah->dnulls = dnulls = (bool *)
    2683            0 :                 repalloc(dnulls, newlen * sizeof(bool));
    2684          974 :         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         1005 :     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         1005 :     eah->fvalue = NULL;
    2703              :     /* And we don't know the flattened size either */
    2704         1005 :     eah->flat_size = 0;
    2705              : 
    2706              :     /* Update dimensionality info if needed */
    2707         1005 :     if (dimschanged)
    2708              :     {
    2709          980 :         eah->ndims = ndim;
    2710          980 :         memcpy(eah->dims, dim, ndim * sizeof(int));
    2711          980 :         memcpy(eah->lbound, lb, ndim * sizeof(int));
    2712              :     }
    2713              : 
    2714              :     /* Reposition items if needed, and fill addedbefore items with nulls */
    2715         1005 :     if (addedbefore > 0)
    2716              :     {
    2717           40 :         memmove(dvalues + addedbefore, dvalues, eah->nelems * sizeof(Datum));
    2718           80 :         for (i = 0; i < addedbefore; i++)
    2719           40 :             dvalues[i] = (Datum) 0;
    2720           40 :         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           40 :         eah->nelems += addedbefore;
    2727              :     }
    2728              : 
    2729              :     /* fill addedafter items with nulls */
    2730         1005 :     if (addedafter > 0)
    2731              :     {
    2732         1880 :         for (i = 0; i < addedafter; i++)
    2733          940 :             dvalues[eah->nelems + i] = (Datum) 0;
    2734          940 :         if (dnulls)
    2735              :         {
    2736            0 :             for (i = 0; i < addedafter; i++)
    2737            0 :                 dnulls[eah->nelems + i] = true;
    2738              :         }
    2739          940 :         eah->nelems += addedafter;
    2740              :     }
    2741              : 
    2742              :     /* Grab old element value for pfree'ing, if needed. */
    2743         1005 :     if (!eah->typbyval && (dnulls == NULL || !dnulls[offset]))
    2744          449 :         oldValue = (char *) DatumGetPointer(dvalues[offset]);
    2745              :     else
    2746          556 :         oldValue = NULL;
    2747              : 
    2748              :     /* And finally we can insert the new element. */
    2749         1005 :     dvalues[offset] = dataValue;
    2750         1005 :     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         1005 :     if (oldValue)
    2759              :     {
    2760              :         /* Don't try to pfree a part of the original flat array */
    2761            1 :         if (oldValue < eah->fstartptr || oldValue >= eah->fendptr)
    2762            0 :             pfree(oldValue);
    2763              :     }
    2764              : 
    2765              :     /* Done, return standard TOAST pointer for object */
    2766         1005 :     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          131 : 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          131 :     if (isNull)
    2851            0 :         return arraydatum;
    2852              : 
    2853          131 :     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          131 :     array = DatumGetArrayTypeP(arraydatum);
    2865          131 :     srcArray = DatumGetArrayTypeP(srcArrayDatum);
    2866              : 
    2867              :     /* note: we assume srcArray contains no toasted elements */
    2868              : 
    2869          131 :     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          131 :     if (ndim == 0)
    2877              :     {
    2878              :         Datum      *dvalues;
    2879              :         bool       *dnulls;
    2880              :         int         nelems;
    2881           28 :         Oid         elmtype = ARR_ELEMTYPE(array);
    2882              : 
    2883           28 :         deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
    2884              :                           &dvalues, &dnulls, &nelems);
    2885              : 
    2886           56 :         for (i = 0; i < nSubscripts; i++)
    2887              :         {
    2888           37 :             if (!upperProvided[i] || !lowerProvided[i])
    2889            3 :                 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           65 :             if (pg_sub_s32_overflow(upperIndx[i], lowerIndx[i], &dim[i]) ||
    2897           31 :                 pg_add_s32_overflow(dim[i], 1, &dim[i]))
    2898            6 :                 ereport(ERROR,
    2899              :                         (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    2900              :                          errmsg("array size exceeds the maximum allowed (%zu)",
    2901              :                                 MaxArraySize)));
    2902              : 
    2903           28 :             lb[i] = lowerIndx[i];
    2904              :         }
    2905              : 
    2906              :         /* complain if too few source items; we ignore extras, however */
    2907           19 :         if (nelems < ArrayGetNItems(nSubscripts, dim))
    2908            0 :             ereport(ERROR,
    2909              :                     (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    2910              :                      errmsg("source array too small")));
    2911              : 
    2912           19 :         return PointerGetDatum(construct_md_array(dvalues, dnulls, nSubscripts,
    2913              :                                                   dim, lb, elmtype,
    2914              :                                                   elmlen, elmbyval, elmalign));
    2915              :     }
    2916              : 
    2917          103 :     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          103 :     memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
    2924          103 :     memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
    2925              : 
    2926          103 :     newhasnulls = (ARR_HASNULL(array) || ARR_HASNULL(srcArray));
    2927          103 :     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          103 :     if (ndim == 1)
    2936              :     {
    2937              :         Assert(nSubscripts == 1);
    2938           88 :         if (!lowerProvided[0])
    2939           18 :             lowerIndx[0] = lb[0];
    2940           88 :         if (!upperProvided[0])
    2941           21 :             upperIndx[0] = dim[0] + lb[0] - 1;
    2942           88 :         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           88 :         if (lowerIndx[0] < lb[0])
    2947              :         {
    2948              :             /* addedbefore = lb[0] - lowerIndx[0]; */
    2949              :             /* dim[0] += addedbefore; */
    2950           48 :             if (pg_sub_s32_overflow(lb[0], lowerIndx[0], &addedbefore) ||
    2951           24 :                 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           24 :             lb[0] = lowerIndx[0];
    2957           24 :             if (addedbefore > 1)
    2958           18 :                 newhasnulls = true; /* will insert nulls */
    2959              :         }
    2960           88 :         if (upperIndx[0] >= (dim[0] + lb[0]))
    2961              :         {
    2962              :             /* addedafter = upperIndx[0] - (dim[0] + lb[0]) + 1; */
    2963              :             /* dim[0] += addedafter; */
    2964           59 :             if (pg_sub_s32_overflow(upperIndx[0], dim[0] + lb[0], &addedafter) ||
    2965           56 :                 pg_add_s32_overflow(addedafter, 1, &addedafter) ||
    2966           28 :                 pg_add_s32_overflow(dim[0], addedafter, &dim[0]))
    2967            3 :                 ereport(ERROR,
    2968              :                         (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    2969              :                          errmsg("array size exceeds the maximum allowed (%zu)",
    2970              :                                 MaxArraySize)));
    2971           28 :             if (addedafter > 1)
    2972           18 :                 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           51 :         for (i = 0; i < nSubscripts; i++)
    2982              :         {
    2983           36 :             if (!lowerProvided[i])
    2984            6 :                 lowerIndx[i] = lb[i];
    2985           36 :             if (!upperProvided[i])
    2986           12 :                 upperIndx[i] = dim[i] + lb[i] - 1;
    2987           36 :             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           36 :             if (lowerIndx[i] < lb[i] ||
    2992           36 :                 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           15 :         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          100 :     nitems = ArrayGetNItems(ndim, dim);
    3011          100 :     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          100 :     mda_get_range(ndim, span, lowerIndx, upperIndx);
    3018          100 :     nsrcitems = ArrayGetNItems(ndim, span);
    3019          100 :     if (nsrcitems > ArrayGetNItems(ARR_NDIM(srcArray), ARR_DIMS(srcArray)))
    3020            3 :         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           97 :     if (newhasnulls)
    3029           48 :         overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
    3030              :     else
    3031           49 :         overheadlen = ARR_OVERHEAD_NONULLS(ndim);
    3032           97 :     newitemsize = array_nelems_size(ARR_DATA_PTR(srcArray), 0,
    3033           97 :                                     ARR_NULLBITMAP(srcArray), nsrcitems,
    3034              :                                     elmlen, elmbyval, elmalign);
    3035           97 :     oldoverheadlen = ARR_DATA_OFFSET(array);
    3036           97 :     olddatasize = ARR_SIZE(array) - oldoverheadlen;
    3037           97 :     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           15 :         olditemsize = array_slice_size(ARR_DATA_PTR(array),
    3044           15 :                                        ARR_NULLBITMAP(array),
    3045              :                                        ndim, dim, lb,
    3046              :                                        lowerIndx, upperIndx,
    3047              :                                        elmlen, elmbyval, elmalign);
    3048           15 :         lenbefore = lenafter = 0;   /* keep compiler quiet */
    3049           15 :         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           82 :         int         oldlb = ARR_LBOUND(array)[0];
    3058           82 :         int         oldub = oldlb + ARR_DIMS(array)[0] - 1;
    3059           82 :         int         slicelb = Max(oldlb, lowerIndx[0]);
    3060           82 :         int         sliceub = Min(oldub, upperIndx[0]);
    3061           82 :         char       *oldarraydata = ARR_DATA_PTR(array);
    3062           82 :         bits8      *oldarraybitmap = ARR_NULLBITMAP(array);
    3063              : 
    3064              :         /* count/size of old array entries that will go before the slice */
    3065           82 :         itemsbefore = Min(slicelb, oldub + 1) - oldlb;
    3066           82 :         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           82 :         if (slicelb > sliceub)
    3071              :         {
    3072           27 :             nolditems = 0;
    3073           27 :             olditemsize = 0;
    3074              :         }
    3075              :         else
    3076              :         {
    3077           55 :             nolditems = sliceub - slicelb + 1;
    3078           55 :             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           82 :         itemsafter = oldub + 1 - Max(sliceub + 1, oldlb);
    3085           82 :         lenafter = olddatasize - lenbefore - olditemsize;
    3086              :     }
    3087              : 
    3088           97 :     newsize = overheadlen + olddatasize - olditemsize + newitemsize;
    3089              : 
    3090           97 :     newarray = (ArrayType *) palloc0(newsize);
    3091           97 :     SET_VARSIZE(newarray, newsize);
    3092           97 :     newarray->ndim = ndim;
    3093           97 :     newarray->dataoffset = newhasnulls ? overheadlen : 0;
    3094           97 :     newarray->elemtype = ARR_ELEMTYPE(array);
    3095           97 :     memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
    3096           97 :     memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
    3097              : 
    3098           97 :     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           15 :         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           82 :         memcpy((char *) newarray + overheadlen,
    3113              :                (char *) array + oldoverheadlen,
    3114              :                lenbefore);
    3115          164 :         memcpy((char *) newarray + overheadlen + lenbefore,
    3116           82 :                ARR_DATA_PTR(srcArray),
    3117              :                newitemsize);
    3118           82 :         memcpy((char *) newarray + overheadlen + lenbefore + newitemsize,
    3119           82 :                (char *) array + oldoverheadlen + lenbefore + olditemsize,
    3120              :                lenafter);
    3121              :         /* fill in nulls bitmap if needed */
    3122           82 :         if (newhasnulls)
    3123              :         {
    3124           48 :             bits8      *newnullbitmap = ARR_NULLBITMAP(newarray);
    3125           48 :             bits8      *oldnullbitmap = ARR_NULLBITMAP(array);
    3126              : 
    3127              :             /* palloc0 above already marked any inserted positions as nulls */
    3128           48 :             array_bitmap_copy(newnullbitmap, addedbefore,
    3129              :                               oldnullbitmap, 0,
    3130              :                               itemsbefore);
    3131           48 :             array_bitmap_copy(newnullbitmap, lowerIndx[0] - lb[0],
    3132           48 :                               ARR_NULLBITMAP(srcArray), 0,
    3133              :                               nsrcitems);
    3134           48 :             array_bitmap_copy(newnullbitmap, addedbefore + itemsbefore + nolditems,
    3135              :                               oldnullbitmap, itemsbefore + nolditems,
    3136              :                               itemsafter);
    3137              :         }
    3138              :     }
    3139              : 
    3140           97 :     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        23408 : array_ref(ArrayType *array, int nSubscripts, int *indx,
    3152              :           int arraytyplen, int elmlen, bool elmbyval, char elmalign,
    3153              :           bool *isNull)
    3154              : {
    3155        23408 :     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          573 : array_set(ArrayType *array, int nSubscripts, int *indx,
    3169              :           Datum dataValue, bool isNull,
    3170              :           int arraytyplen, int elmlen, bool elmbyval, char elmalign)
    3171              : {
    3172          573 :     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        20523 : array_map(Datum arrayd,
    3207              :           ExprState *exprstate, ExprContext *econtext,
    3208              :           Oid retType, ArrayMapState *amstate)
    3209              : {
    3210        20523 :     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        20523 :     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        20523 :     Datum      *transform_source = exprstate->innermost_caseval;
    3233        20523 :     bool       *transform_source_isnull = exprstate->innermost_casenull;
    3234              : 
    3235        20523 :     inpType = AARR_ELEMTYPE(v);
    3236        20523 :     ndim = AARR_NDIM(v);
    3237        20523 :     dim = AARR_DIMS(v);
    3238        20523 :     nitems = ArrayGetNItems(ndim, dim);
    3239              : 
    3240              :     /* Check for empty array */
    3241        20523 :     if (nitems <= 0)
    3242              :     {
    3243              :         /* Return empty array */
    3244            6 :         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        20517 :     inp_extra = &amstate->inp_extra;
    3253        20517 :     ret_extra = &amstate->ret_extra;
    3254              : 
    3255        20517 :     if (inp_extra->element_type != inpType)
    3256              :     {
    3257          217 :         get_typlenbyvalalign(inpType,
    3258              :                              &inp_extra->typlen,
    3259              :                              &inp_extra->typbyval,
    3260              :                              &inp_extra->typalign);
    3261          217 :         inp_extra->element_type = inpType;
    3262              :     }
    3263        20517 :     inp_typlen = inp_extra->typlen;
    3264        20517 :     inp_typbyval = inp_extra->typbyval;
    3265        20517 :     inp_typalign = inp_extra->typalign;
    3266              : 
    3267        20517 :     if (ret_extra->element_type != retType)
    3268              :     {
    3269          217 :         get_typlenbyvalalign(retType,
    3270              :                              &ret_extra->typlen,
    3271              :                              &ret_extra->typbyval,
    3272              :                              &ret_extra->typalign);
    3273          217 :         ret_extra->element_type = retType;
    3274              :     }
    3275        20517 :     typlen = ret_extra->typlen;
    3276        20517 :     typbyval = ret_extra->typbyval;
    3277        20517 :     typalign = ret_extra->typalign;
    3278        20517 :     typalignby = typalign_to_alignby(typalign);
    3279              : 
    3280              :     /* Allocate temporary arrays for new values */
    3281        20517 :     values = (Datum *) palloc(nitems * sizeof(Datum));
    3282        20517 :     nulls = (bool *) palloc(nitems * sizeof(bool));
    3283              : 
    3284              :     /* Loop over source data */
    3285        20517 :     array_iter_setup(&iter, v, inp_typlen, inp_typbyval, inp_typalign);
    3286        20517 :     hasnulls = false;
    3287              : 
    3288       231377 :     for (i = 0; i < nitems; i++)
    3289              :     {
    3290              :         /* Get source element, checking for NULL */
    3291       210876 :         *transform_source =
    3292       210876 :             array_iter_next(&iter, transform_source_isnull, i);
    3293              : 
    3294              :         /* Apply the given expression to source element */
    3295       210876 :         values[i] = ExecEvalExpr(exprstate, econtext, &nulls[i]);
    3296              : 
    3297       210860 :         if (nulls[i])
    3298            6 :             hasnulls = true;
    3299              :         else
    3300              :         {
    3301              :             /* Ensure data is not toasted */
    3302       210854 :             if (typlen == -1)
    3303          214 :                 values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
    3304              :             /* Update total result size */
    3305       210854 :             nbytes = att_addlength_datum(nbytes, typlen, values[i]);
    3306       210854 :             nbytes = att_nominal_alignby(nbytes, typalignby);
    3307              :             /* check for overflow of total request */
    3308       210854 :             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        20501 :     if (hasnulls)
    3318              :     {
    3319            3 :         dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
    3320            3 :         nbytes += dataoffset;
    3321              :     }
    3322              :     else
    3323              :     {
    3324        20498 :         dataoffset = 0;         /* marker for no null bitmap */
    3325        20498 :         nbytes += ARR_OVERHEAD_NONULLS(ndim);
    3326              :     }
    3327        20501 :     result = (ArrayType *) palloc0(nbytes);
    3328        20501 :     SET_VARSIZE(result, nbytes);
    3329        20501 :     result->ndim = ndim;
    3330        20501 :     result->dataoffset = dataoffset;
    3331        20501 :     result->elemtype = retType;
    3332        20501 :     memcpy(ARR_DIMS(result), AARR_DIMS(v), ndim * sizeof(int));
    3333        20501 :     memcpy(ARR_LBOUND(result), AARR_LBOUND(v), ndim * sizeof(int));
    3334              : 
    3335        20501 :     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        20501 :     pfree(values);
    3344        20501 :     pfree(nulls);
    3345              : 
    3346        20501 :     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       176296 : 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       176296 :     dims[0] = nelems;
    3375       176296 :     lbs[0] = 1;
    3376              : 
    3377       176296 :     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       125467 : construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
    3388              : {
    3389              :     int         elmlen;
    3390              :     bool        elmbyval;
    3391              :     char        elmalign;
    3392              : 
    3393       125467 :     switch (elmtype)
    3394              :     {
    3395         1489 :         case CHAROID:
    3396         1489 :             elmlen = 1;
    3397         1489 :             elmbyval = true;
    3398         1489 :             elmalign = TYPALIGN_CHAR;
    3399         1489 :             break;
    3400              : 
    3401         4197 :         case CSTRINGOID:
    3402         4197 :             elmlen = -2;
    3403         4197 :             elmbyval = false;
    3404         4197 :             elmalign = TYPALIGN_CHAR;
    3405         4197 :             break;
    3406              : 
    3407        72172 :         case FLOAT4OID:
    3408        72172 :             elmlen = sizeof(float4);
    3409        72172 :             elmbyval = true;
    3410        72172 :             elmalign = TYPALIGN_INT;
    3411        72172 :             break;
    3412              : 
    3413           24 :         case FLOAT8OID:
    3414           24 :             elmlen = sizeof(float8);
    3415           24 :             elmbyval = true;
    3416           24 :             elmalign = TYPALIGN_DOUBLE;
    3417           24 :             break;
    3418              : 
    3419        29589 :         case INT2OID:
    3420        29589 :             elmlen = sizeof(int16);
    3421        29589 :             elmbyval = true;
    3422        29589 :             elmalign = TYPALIGN_SHORT;
    3423        29589 :             break;
    3424              : 
    3425         3574 :         case INT4OID:
    3426         3574 :             elmlen = sizeof(int32);
    3427         3574 :             elmbyval = true;
    3428         3574 :             elmalign = TYPALIGN_INT;
    3429         3574 :             break;
    3430              : 
    3431            2 :         case INT8OID:
    3432            2 :             elmlen = sizeof(int64);
    3433            2 :             elmbyval = true;
    3434            2 :             elmalign = TYPALIGN_DOUBLE;
    3435            2 :             break;
    3436              : 
    3437          441 :         case NAMEOID:
    3438          441 :             elmlen = NAMEDATALEN;
    3439          441 :             elmbyval = false;
    3440          441 :             elmalign = TYPALIGN_CHAR;
    3441          441 :             break;
    3442              : 
    3443         8658 :         case OIDOID:
    3444              :         case REGTYPEOID:
    3445         8658 :             elmlen = sizeof(Oid);
    3446         8658 :             elmbyval = true;
    3447         8658 :             elmalign = TYPALIGN_INT;
    3448         8658 :             break;
    3449              : 
    3450         5298 :         case TEXTOID:
    3451         5298 :             elmlen = -1;
    3452         5298 :             elmbyval = false;
    3453         5298 :             elmalign = TYPALIGN_INT;
    3454         5298 :             break;
    3455              : 
    3456           21 :         case TIDOID:
    3457           21 :             elmlen = sizeof(ItemPointerData);
    3458           21 :             elmbyval = false;
    3459           21 :             elmalign = TYPALIGN_SHORT;
    3460           21 :             break;
    3461              : 
    3462            2 :         case XIDOID:
    3463            2 :             elmlen = sizeof(TransactionId);
    3464            2 :             elmbyval = true;
    3465            2 :             elmalign = TYPALIGN_INT;
    3466            2 :             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       125467 :     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       798698 : 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       798698 :     uint8       elmalignby = typalign_to_alignby(elmalign);
    3514              : 
    3515       798698 :     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       798698 :     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       798698 :     nelems = ArrayGetNItems(ndims, dims);
    3527       798698 :     ArrayCheckBounds(ndims, dims, lbs);
    3528              : 
    3529              :     /* if ndims <= 0 or any dims[i] == 0, return empty array */
    3530       798698 :     if (nelems <= 0)
    3531        29238 :         return construct_empty_array(elmtype);
    3532              : 
    3533              :     /* compute required space */
    3534       769460 :     nbytes = 0;
    3535       769460 :     hasnulls = false;
    3536      5670261 :     for (i = 0; i < nelems; i++)
    3537              :     {
    3538      4900801 :         if (nulls && nulls[i])
    3539              :         {
    3540        16418 :             hasnulls = true;
    3541        16418 :             continue;
    3542              :         }
    3543              :         /* make sure data is not toasted */
    3544      4884383 :         if (elmlen == -1)
    3545      1330976 :             elems[i] = PointerGetDatum(PG_DETOAST_DATUM(elems[i]));
    3546      4884383 :         nbytes = att_addlength_datum(nbytes, elmlen, elems[i]);
    3547      4884383 :         nbytes = att_nominal_alignby(nbytes, elmalignby);
    3548              :         /* check for overflow of total request */
    3549      4884383 :         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       769460 :     if (hasnulls)
    3558              :     {
    3559         8790 :         dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nelems);
    3560         8790 :         nbytes += dataoffset;
    3561              :     }
    3562              :     else
    3563              :     {
    3564       760670 :         dataoffset = 0;         /* marker for no null bitmap */
    3565       760670 :         nbytes += ARR_OVERHEAD_NONULLS(ndims);
    3566              :     }
    3567       769460 :     result = (ArrayType *) palloc0(nbytes);
    3568       769460 :     SET_VARSIZE(result, nbytes);
    3569       769460 :     result->ndim = ndims;
    3570       769460 :     result->dataoffset = dataoffset;
    3571       769460 :     result->elemtype = elmtype;
    3572       769460 :     memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
    3573       769460 :     memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
    3574              : 
    3575       769460 :     CopyArrayEls(result,
    3576              :                  elems, nulls, nelems,
    3577              :                  elmlen, elmbyval, elmalign,
    3578              :                  false);
    3579              : 
    3580       769460 :     return result;
    3581              : }
    3582              : 
    3583              : /*
    3584              :  * construct_empty_array    --- make a zero-dimensional array of given type
    3585              :  */
    3586              : ArrayType *
    3587      1457368 : construct_empty_array(Oid elmtype)
    3588              : {
    3589              :     ArrayType  *result;
    3590              : 
    3591      1457368 :     result = palloc0_object(ArrayType);
    3592      1457368 :     SET_VARSIZE(result, sizeof(ArrayType));
    3593      1457368 :     result->ndim = 0;
    3594      1457368 :     result->dataoffset = 0;
    3595      1457368 :     result->elemtype = elmtype;
    3596      1457368 :     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           12 : construct_empty_expanded_array(Oid element_type,
    3605              :                                MemoryContext parentcontext,
    3606              :                                ArrayMetaState *metacache)
    3607              : {
    3608           12 :     ArrayType  *array = construct_empty_array(element_type);
    3609              :     Datum       d;
    3610              : 
    3611           12 :     d = expand_array(PointerGetDatum(array), parentcontext, metacache);
    3612           12 :     pfree(array);
    3613           12 :     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      1702695 : 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      1702695 :     uint8       elmalignby = typalign_to_alignby(elmalign);
    3651              : 
    3652              :     Assert(ARR_ELEMTYPE(array) == elmtype);
    3653              : 
    3654      1702695 :     nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
    3655      1702695 :     *elemsp = elems = palloc_array(Datum, nelems);
    3656      1702695 :     if (nullsp)
    3657       943280 :         *nullsp = nulls = palloc0_array(bool, nelems);
    3658              :     else
    3659       759415 :         nulls = NULL;
    3660      1702695 :     *nelemsp = nelems;
    3661              : 
    3662      1702695 :     p = ARR_DATA_PTR(array);
    3663      1702695 :     bitmap = ARR_NULLBITMAP(array);
    3664      1702695 :     bitmask = 1;
    3665              : 
    3666     30148454 :     for (i = 0; i < nelems; i++)
    3667              :     {
    3668              :         /* Get source element, checking for NULL */
    3669     28445759 :         if (bitmap && (*bitmap & bitmask) == 0)
    3670              :         {
    3671         1744 :             elems[i] = (Datum) 0;
    3672         1744 :             if (nulls)
    3673         1744 :                 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     28444015 :             elems[i] = fetch_att(p, elmbyval, elmlen);
    3682     28444015 :             p = att_addlength_pointer(p, elmlen, p);
    3683     28444015 :             p = (char *) att_nominal_alignby(p, elmalignby);
    3684              :         }
    3685              : 
    3686              :         /* advance bitmap pointer if any */
    3687     28445759 :         if (bitmap)
    3688              :         {
    3689         2744 :             bitmask <<= 1;
    3690         2744 :             if (bitmask == 0x100)
    3691              :             {
    3692           45 :                 bitmap++;
    3693           45 :                 bitmask = 1;
    3694              :             }
    3695              :         }
    3696              :     }
    3697      1702695 : }
    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       291526 : 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       291526 :     switch (elmtype)
    3714              :     {
    3715            9 :         case CHAROID:
    3716            9 :             elmlen = 1;
    3717            9 :             elmbyval = true;
    3718            9 :             elmalign = TYPALIGN_CHAR;
    3719            9 :             break;
    3720              : 
    3721         6084 :         case CSTRINGOID:
    3722         6084 :             elmlen = -2;
    3723         6084 :             elmbyval = false;
    3724         6084 :             elmalign = TYPALIGN_CHAR;
    3725         6084 :             break;
    3726              : 
    3727           15 :         case FLOAT8OID:
    3728           15 :             elmlen = sizeof(float8);
    3729           15 :             elmbyval = true;
    3730           15 :             elmalign = TYPALIGN_DOUBLE;
    3731           15 :             break;
    3732              : 
    3733         2807 :         case INT2OID:
    3734         2807 :             elmlen = sizeof(int16);
    3735         2807 :             elmbyval = true;
    3736         2807 :             elmalign = TYPALIGN_SHORT;
    3737         2807 :             break;
    3738              : 
    3739          168 :         case INT4OID:
    3740          168 :             elmlen = sizeof(int32);
    3741          168 :             elmbyval = true;
    3742          168 :             elmalign = TYPALIGN_INT;
    3743          168 :             break;
    3744              : 
    3745          154 :         case OIDOID:
    3746          154 :             elmlen = sizeof(Oid);
    3747          154 :             elmbyval = true;
    3748          154 :             elmalign = TYPALIGN_INT;
    3749          154 :             break;
    3750              : 
    3751       282267 :         case TEXTOID:
    3752       282267 :             elmlen = -1;
    3753       282267 :             elmbyval = false;
    3754       282267 :             elmalign = TYPALIGN_INT;
    3755       282267 :             break;
    3756              : 
    3757           22 :         case TIDOID:
    3758           22 :             elmlen = sizeof(ItemPointerData);
    3759           22 :             elmbyval = false;
    3760           22 :             elmalign = TYPALIGN_SHORT;
    3761           22 :             break;
    3762              : 
    3763            0 :         default:
    3764            0 :             elog(ERROR, "type %u not supported by deconstruct_array_builtin()", elmtype);
    3765              :             /* keep compiler quiet */
    3766              :             elmlen = 0;
    3767              :             elmbyval = false;
    3768              :             elmalign = 0;
    3769              :     }
    3770              : 
    3771       291526 :     deconstruct_array(array, elmtype, elmlen, elmbyval, elmalign, elemsp, nullsp, nelemsp);
    3772       291526 : }
    3773              : 
    3774              : /*
    3775              :  * array_contains_nulls --- detect whether an array has any null elements
    3776              :  *
    3777              :  * This gives an accurate answer, whereas testing ARR_HASNULL only tells
    3778              :  * if the array *might* contain a null.
    3779              :  */
    3780              : bool
    3781        36930 : array_contains_nulls(const ArrayType *array)
    3782              : {
    3783              :     int         nelems;
    3784              :     bits8      *bitmap;
    3785              :     int         bitmask;
    3786              : 
    3787              :     /* Easy answer if there's no null bitmap */
    3788        36930 :     if (!ARR_HASNULL(array))
    3789        36899 :         return false;
    3790              : 
    3791           31 :     nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
    3792              : 
    3793           31 :     bitmap = ARR_NULLBITMAP(array);
    3794              : 
    3795              :     /* check whole bytes of the bitmap byte-at-a-time */
    3796           31 :     while (nelems >= 8)
    3797              :     {
    3798            9 :         if (*bitmap != 0xFF)
    3799            9 :             return true;
    3800            0 :         bitmap++;
    3801            0 :         nelems -= 8;
    3802              :     }
    3803              : 
    3804              :     /* check last partial byte */
    3805           22 :     bitmask = 1;
    3806           49 :     while (nelems > 0)
    3807              :     {
    3808           49 :         if ((*bitmap & bitmask) == 0)
    3809           22 :             return true;
    3810           27 :         bitmask <<= 1;
    3811           27 :         nelems--;
    3812              :     }
    3813              : 
    3814            0 :     return false;
    3815              : }
    3816              : 
    3817              : 
    3818              : /*
    3819              :  * array_eq :
    3820              :  *        compares two arrays for equality
    3821              :  * result :
    3822              :  *        returns true if the arrays are equal, false otherwise.
    3823              :  *
    3824              :  * Note: we do not use array_cmp here, since equality may be meaningful in
    3825              :  * datatypes that don't have a total ordering (and hence no btree support).
    3826              :  */
    3827              : Datum
    3828       135481 : array_eq(PG_FUNCTION_ARGS)
    3829              : {
    3830       135481 :     LOCAL_FCINFO(locfcinfo, 2);
    3831       135481 :     AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
    3832       135481 :     AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
    3833       135481 :     Oid         collation = PG_GET_COLLATION();
    3834       135481 :     int         ndims1 = AARR_NDIM(array1);
    3835       135481 :     int         ndims2 = AARR_NDIM(array2);
    3836       135481 :     int        *dims1 = AARR_DIMS(array1);
    3837       135481 :     int        *dims2 = AARR_DIMS(array2);
    3838       135481 :     int        *lbs1 = AARR_LBOUND(array1);
    3839       135481 :     int        *lbs2 = AARR_LBOUND(array2);
    3840       135481 :     Oid         element_type = AARR_ELEMTYPE(array1);
    3841       135481 :     bool        result = true;
    3842              :     int         nitems;
    3843              :     TypeCacheEntry *typentry;
    3844              :     int         typlen;
    3845              :     bool        typbyval;
    3846              :     char        typalign;
    3847              :     array_iter  it1;
    3848              :     array_iter  it2;
    3849              :     int         i;
    3850              : 
    3851       135481 :     if (element_type != AARR_ELEMTYPE(array2))
    3852            0 :         ereport(ERROR,
    3853              :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    3854              :                  errmsg("cannot compare arrays of different element types")));
    3855              : 
    3856              :     /* fast path if the arrays do not have the same dimensionality */
    3857       135481 :     if (ndims1 != ndims2 ||
    3858       128091 :         memcmp(dims1, dims2, ndims1 * sizeof(int)) != 0 ||
    3859        95828 :         memcmp(lbs1, lbs2, ndims1 * sizeof(int)) != 0)
    3860        39653 :         result = false;
    3861              :     else
    3862              :     {
    3863              :         /*
    3864              :          * We arrange to look up the equality function only once per series of
    3865              :          * calls, assuming the element type doesn't change underneath us.  The
    3866              :          * typcache is used so that we have no memory leakage when being used
    3867              :          * as an index support function.
    3868              :          */
    3869        95828 :         typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
    3870        95828 :         if (typentry == NULL ||
    3871        94220 :             typentry->type_id != element_type)
    3872              :         {
    3873         1608 :             typentry = lookup_type_cache(element_type,
    3874              :                                          TYPECACHE_EQ_OPR_FINFO);
    3875         1608 :             if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
    3876            0 :                 ereport(ERROR,
    3877              :                         (errcode(ERRCODE_UNDEFINED_FUNCTION),
    3878              :                          errmsg("could not identify an equality operator for type %s",
    3879              :                                 format_type_be(element_type))));
    3880         1608 :             fcinfo->flinfo->fn_extra = typentry;
    3881              :         }
    3882        95828 :         typlen = typentry->typlen;
    3883        95828 :         typbyval = typentry->typbyval;
    3884        95828 :         typalign = typentry->typalign;
    3885              : 
    3886              :         /*
    3887              :          * apply the operator to each pair of array elements.
    3888              :          */
    3889        95828 :         InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
    3890              :                                  collation, NULL, NULL);
    3891              : 
    3892              :         /* Loop over source data */
    3893        95828 :         nitems = ArrayGetNItems(ndims1, dims1);
    3894        95828 :         array_iter_setup(&it1, array1, typlen, typbyval, typalign);
    3895        95828 :         array_iter_setup(&it2, array2, typlen, typbyval, typalign);
    3896              : 
    3897       224072 :         for (i = 0; i < nitems; i++)
    3898              :         {
    3899              :             Datum       elt1;
    3900              :             Datum       elt2;
    3901              :             bool        isnull1;
    3902              :             bool        isnull2;
    3903              :             bool        oprresult;
    3904              : 
    3905              :             /* Get elements, checking for NULL */
    3906       145769 :             elt1 = array_iter_next(&it1, &isnull1, i);
    3907       145769 :             elt2 = array_iter_next(&it2, &isnull2, i);
    3908              : 
    3909              :             /*
    3910              :              * We consider two NULLs equal; NULL and not-NULL are unequal.
    3911              :              */
    3912       145769 :             if (isnull1 && isnull2)
    3913           11 :                 continue;
    3914       145758 :             if (isnull1 || isnull2)
    3915              :             {
    3916           54 :                 result = false;
    3917        17525 :                 break;
    3918              :             }
    3919              : 
    3920              :             /*
    3921              :              * Apply the operator to the element pair; treat NULL as false
    3922              :              */
    3923       145704 :             locfcinfo->args[0].value = elt1;
    3924       145704 :             locfcinfo->args[0].isnull = false;
    3925       145704 :             locfcinfo->args[1].value = elt2;
    3926       145704 :             locfcinfo->args[1].isnull = false;
    3927       145704 :             locfcinfo->isnull = false;
    3928       145704 :             oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
    3929       145704 :             if (locfcinfo->isnull || !oprresult)
    3930              :             {
    3931        17471 :                 result = false;
    3932        17471 :                 break;
    3933              :             }
    3934              :         }
    3935              :     }
    3936              : 
    3937              :     /* Avoid leaking memory when handed toasted input. */
    3938       135481 :     AARR_FREE_IF_COPY(array1, 0);
    3939       135481 :     AARR_FREE_IF_COPY(array2, 1);
    3940              : 
    3941       135481 :     PG_RETURN_BOOL(result);
    3942              : }
    3943              : 
    3944              : 
    3945              : /*-----------------------------------------------------------------------------
    3946              :  * array-array bool operators:
    3947              :  *      Given two arrays, iterate comparison operators
    3948              :  *      over the array. Uses logic similar to text comparison
    3949              :  *      functions, except element-by-element instead of
    3950              :  *      character-by-character.
    3951              :  *----------------------------------------------------------------------------
    3952              :  */
    3953              : 
    3954              : Datum
    3955          495 : array_ne(PG_FUNCTION_ARGS)
    3956              : {
    3957          495 :     PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo)));
    3958              : }
    3959              : 
    3960              : Datum
    3961         5081 : array_lt(PG_FUNCTION_ARGS)
    3962              : {
    3963         5081 :     PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
    3964              : }
    3965              : 
    3966              : Datum
    3967            9 : array_gt(PG_FUNCTION_ARGS)
    3968              : {
    3969            9 :     PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
    3970              : }
    3971              : 
    3972              : Datum
    3973            9 : array_le(PG_FUNCTION_ARGS)
    3974              : {
    3975            9 :     PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
    3976              : }
    3977              : 
    3978              : Datum
    3979            9 : array_ge(PG_FUNCTION_ARGS)
    3980              : {
    3981            9 :     PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
    3982              : }
    3983              : 
    3984              : Datum
    3985      4237696 : btarraycmp(PG_FUNCTION_ARGS)
    3986              : {
    3987      4237696 :     PG_RETURN_INT32(array_cmp(fcinfo));
    3988              : }
    3989              : 
    3990              : /*
    3991              :  * array_cmp()
    3992              :  * Internal comparison function for arrays.
    3993              :  *
    3994              :  * Returns -1, 0 or 1
    3995              :  */
    3996              : static int
    3997      4243077 : array_cmp(FunctionCallInfo fcinfo)
    3998              : {
    3999      4243077 :     LOCAL_FCINFO(locfcinfo, 2);
    4000      4243077 :     AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
    4001      4243077 :     AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
    4002      4243077 :     Oid         collation = PG_GET_COLLATION();
    4003      4243077 :     int         ndims1 = AARR_NDIM(array1);
    4004      4243077 :     int         ndims2 = AARR_NDIM(array2);
    4005      4243077 :     int        *dims1 = AARR_DIMS(array1);
    4006      4243077 :     int        *dims2 = AARR_DIMS(array2);
    4007      4243077 :     int         nitems1 = ArrayGetNItems(ndims1, dims1);
    4008      4243077 :     int         nitems2 = ArrayGetNItems(ndims2, dims2);
    4009      4243077 :     Oid         element_type = AARR_ELEMTYPE(array1);
    4010      4243077 :     int         result = 0;
    4011              :     TypeCacheEntry *typentry;
    4012              :     int         typlen;
    4013              :     bool        typbyval;
    4014              :     char        typalign;
    4015              :     int         min_nitems;
    4016              :     array_iter  it1;
    4017              :     array_iter  it2;
    4018              :     int         i;
    4019              : 
    4020      4243077 :     if (element_type != AARR_ELEMTYPE(array2))
    4021            3 :         ereport(ERROR,
    4022              :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    4023              :                  errmsg("cannot compare arrays of different element types")));
    4024              : 
    4025              :     /*
    4026              :      * We arrange to look up the comparison function only once per series of
    4027              :      * calls, assuming the element type doesn't change underneath us. The
    4028              :      * typcache is used so that we have no memory leakage when being used as
    4029              :      * an index support function.
    4030              :      */
    4031      4243074 :     typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
    4032      4243074 :     if (typentry == NULL ||
    4033      4241624 :         typentry->type_id != element_type)
    4034              :     {
    4035         1450 :         typentry = lookup_type_cache(element_type,
    4036              :                                      TYPECACHE_CMP_PROC_FINFO);
    4037         1450 :         if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
    4038            3 :             ereport(ERROR,
    4039              :                     (errcode(ERRCODE_UNDEFINED_FUNCTION),
    4040              :                      errmsg("could not identify a comparison function for type %s",
    4041              :                             format_type_be(element_type))));
    4042         1447 :         fcinfo->flinfo->fn_extra = typentry;
    4043              :     }
    4044      4243071 :     typlen = typentry->typlen;
    4045      4243071 :     typbyval = typentry->typbyval;
    4046      4243071 :     typalign = typentry->typalign;
    4047              : 
    4048              :     /*
    4049              :      * apply the operator to each pair of array elements.
    4050              :      */
    4051      4243071 :     InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
    4052              :                              collation, NULL, NULL);
    4053              : 
    4054              :     /* Loop over source data */
    4055      4243071 :     min_nitems = Min(nitems1, nitems2);
    4056      4243071 :     array_iter_setup(&it1, array1, typlen, typbyval, typalign);
    4057      4243071 :     array_iter_setup(&it2, array2, typlen, typbyval, typalign);
    4058              : 
    4059      8793646 :     for (i = 0; i < min_nitems; i++)
    4060              :     {
    4061              :         Datum       elt1;
    4062              :         Datum       elt2;
    4063              :         bool        isnull1;
    4064              :         bool        isnull2;
    4065              :         int32       cmpresult;
    4066              : 
    4067              :         /* Get elements, checking for NULL */
    4068      7530622 :         elt1 = array_iter_next(&it1, &isnull1, i);
    4069      7530622 :         elt2 = array_iter_next(&it2, &isnull2, i);
    4070              : 
    4071              :         /*
    4072              :          * We consider two NULLs equal; NULL > not-NULL.
    4073              :          */
    4074      7530622 :         if (isnull1 && isnull2)
    4075      4550575 :             continue;
    4076      7530605 :         if (isnull1)
    4077              :         {
    4078              :             /* arg1 is greater than arg2 */
    4079           96 :             result = 1;
    4080      2980047 :             break;
    4081              :         }
    4082      7530509 :         if (isnull2)
    4083              :         {
    4084              :             /* arg1 is less than arg2 */
    4085          180 :             result = -1;
    4086          180 :             break;
    4087              :         }
    4088              : 
    4089              :         /* Compare the pair of elements */
    4090      7530329 :         locfcinfo->args[0].value = elt1;
    4091      7530329 :         locfcinfo->args[0].isnull = false;
    4092      7530329 :         locfcinfo->args[1].value = elt2;
    4093      7530329 :         locfcinfo->args[1].isnull = false;
    4094      7530329 :         cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
    4095              : 
    4096              :         /* We don't expect comparison support functions to return null */
    4097              :         Assert(!locfcinfo->isnull);
    4098              : 
    4099      7530329 :         if (cmpresult == 0)
    4100      4550558 :             continue;           /* equal */
    4101              : 
    4102      2979771 :         if (cmpresult < 0)
    4103              :         {
    4104              :             /* arg1 is less than arg2 */
    4105      1692922 :             result = -1;
    4106      1692922 :             break;
    4107              :         }
    4108              :         else
    4109              :         {
    4110              :             /* arg1 is greater than arg2 */
    4111      1286849 :             result = 1;
    4112      1286849 :             break;
    4113              :         }
    4114              :     }
    4115              : 
    4116              :     /*
    4117              :      * If arrays contain same data (up to end of shorter one), apply
    4118              :      * additional rules to sort by dimensionality.  The relative significance
    4119              :      * of the different bits of information is historical; mainly we just care
    4120              :      * that we don't say "equal" for arrays of different dimensionality.
    4121              :      */
    4122      4243071 :     if (result == 0)
    4123              :     {
    4124      1263024 :         if (nitems1 != nitems2)
    4125       113309 :             result = (nitems1 < nitems2) ? -1 : 1;
    4126      1149715 :         else if (ndims1 != ndims2)
    4127            0 :             result = (ndims1 < ndims2) ? -1 : 1;
    4128              :         else
    4129              :         {
    4130      2299410 :             for (i = 0; i < ndims1; i++)
    4131              :             {
    4132      1149695 :                 if (dims1[i] != dims2[i])
    4133              :                 {
    4134            0 :                     result = (dims1[i] < dims2[i]) ? -1 : 1;
    4135            0 :                     break;
    4136              :                 }
    4137              :             }
    4138      1149715 :             if (result == 0)
    4139              :             {
    4140      1149715 :                 int        *lbound1 = AARR_LBOUND(array1);
    4141      1149715 :                 int        *lbound2 = AARR_LBOUND(array2);
    4142              : 
    4143      2299410 :                 for (i = 0; i < ndims1; i++)
    4144              :                 {
    4145      1149695 :                     if (lbound1[i] != lbound2[i])
    4146              :                     {
    4147            0 :                         result = (lbound1[i] < lbound2[i]) ? -1 : 1;
    4148            0 :                         break;
    4149              :                     }
    4150              :                 }
    4151              :             }
    4152              :         }
    4153              :     }
    4154              : 
    4155              :     /* Avoid leaking memory when handed toasted input. */
    4156      4243071 :     AARR_FREE_IF_COPY(array1, 0);
    4157      4243071 :     AARR_FREE_IF_COPY(array2, 1);
    4158              : 
    4159      4243071 :     return result;
    4160              : }
    4161              : 
    4162              : 
    4163              : /*-----------------------------------------------------------------------------
    4164              :  * array hashing
    4165              :  *      Hash the elements and combine the results.
    4166              :  *----------------------------------------------------------------------------
    4167              :  */
    4168              : 
    4169              : Datum
    4170        28788 : hash_array(PG_FUNCTION_ARGS)
    4171              : {
    4172        28788 :     LOCAL_FCINFO(locfcinfo, 1);
    4173        28788 :     AnyArrayType *array = PG_GETARG_ANY_ARRAY_P(0);
    4174        28788 :     int         ndims = AARR_NDIM(array);
    4175        28788 :     int        *dims = AARR_DIMS(array);
    4176        28788 :     Oid         element_type = AARR_ELEMTYPE(array);
    4177        28788 :     uint32      result = 1;
    4178              :     int         nitems;
    4179              :     TypeCacheEntry *typentry;
    4180              :     int         typlen;
    4181              :     bool        typbyval;
    4182              :     char        typalign;
    4183              :     int         i;
    4184              :     array_iter  iter;
    4185              : 
    4186              :     /*
    4187              :      * We arrange to look up the hash function only once per series of calls,
    4188              :      * assuming the element type doesn't change underneath us.  The typcache
    4189              :      * is used so that we have no memory leakage when being used as an index
    4190              :      * support function.
    4191              :      */
    4192        28788 :     typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
    4193        28788 :     if (typentry == NULL ||
    4194        28501 :         typentry->type_id != element_type)
    4195              :     {
    4196          287 :         typentry = lookup_type_cache(element_type,
    4197              :                                      TYPECACHE_HASH_PROC_FINFO);
    4198          287 :         if (!OidIsValid(typentry->hash_proc_finfo.fn_oid) && element_type != RECORDOID)
    4199            3 :             ereport(ERROR,
    4200              :                     (errcode(ERRCODE_UNDEFINED_FUNCTION),
    4201              :                      errmsg("could not identify a hash function for type %s",
    4202              :                             format_type_be(element_type))));
    4203              : 
    4204              :         /*
    4205              :          * The type cache doesn't believe that record is hashable (see
    4206              :          * cache_record_field_properties()), but since we're here, we're
    4207              :          * committed to hashing, so we can assume it does.  Worst case, if any
    4208              :          * components of the record don't support hashing, we will fail at
    4209              :          * execution.
    4210              :          */
    4211          284 :         if (element_type == RECORDOID)
    4212              :         {
    4213              :             MemoryContext oldcontext;
    4214              :             TypeCacheEntry *record_typentry;
    4215              : 
    4216            9 :             oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
    4217              : 
    4218              :             /*
    4219              :              * Make fake type cache entry structure.  Note that we can't just
    4220              :              * modify typentry, since that points directly into the type
    4221              :              * cache.
    4222              :              */
    4223            9 :             record_typentry = palloc0_object(TypeCacheEntry);
    4224            9 :             record_typentry->type_id = element_type;
    4225              : 
    4226              :             /* fill in what we need below */
    4227            9 :             record_typentry->typlen = typentry->typlen;
    4228            9 :             record_typentry->typbyval = typentry->typbyval;
    4229            9 :             record_typentry->typalign = typentry->typalign;
    4230            9 :             fmgr_info(F_HASH_RECORD, &record_typentry->hash_proc_finfo);
    4231              : 
    4232            9 :             MemoryContextSwitchTo(oldcontext);
    4233              : 
    4234            9 :             typentry = record_typentry;
    4235              :         }
    4236              : 
    4237          284 :         fcinfo->flinfo->fn_extra = typentry;
    4238              :     }
    4239              : 
    4240        28785 :     typlen = typentry->typlen;
    4241        28785 :     typbyval = typentry->typbyval;
    4242        28785 :     typalign = typentry->typalign;
    4243              : 
    4244              :     /*
    4245              :      * apply the hash function to each array element.
    4246              :      */
    4247        28785 :     InitFunctionCallInfoData(*locfcinfo, &typentry->hash_proc_finfo, 1,
    4248              :                              PG_GET_COLLATION(), NULL, NULL);
    4249              : 
    4250              :     /* Loop over source data */
    4251        28785 :     nitems = ArrayGetNItems(ndims, dims);
    4252        28785 :     array_iter_setup(&iter, array, typlen, typbyval, typalign);
    4253              : 
    4254        75534 :     for (i = 0; i < nitems; i++)
    4255              :     {
    4256              :         Datum       elt;
    4257              :         bool        isnull;
    4258              :         uint32      elthash;
    4259              : 
    4260              :         /* Get element, checking for NULL */
    4261        46749 :         elt = array_iter_next(&iter, &isnull, i);
    4262              : 
    4263        46749 :         if (isnull)
    4264              :         {
    4265              :             /* Treat nulls as having hashvalue 0 */
    4266            0 :             elthash = 0;
    4267              :         }
    4268              :         else
    4269              :         {
    4270              :             /* Apply the hash function */
    4271        46749 :             locfcinfo->args[0].value = elt;
    4272        46749 :             locfcinfo->args[0].isnull = false;
    4273        46749 :             elthash = DatumGetUInt32(FunctionCallInvoke(locfcinfo));
    4274              :             /* We don't expect hash functions to return null */
    4275              :             Assert(!locfcinfo->isnull);
    4276              :         }
    4277              : 
    4278              :         /*
    4279              :          * Combine hash values of successive elements by multiplying the
    4280              :          * current value by 31 and adding on the new element's hash value.
    4281              :          *
    4282              :          * The result is a sum in which each element's hash value is
    4283              :          * multiplied by a different power of 31. This is modulo 2^32
    4284              :          * arithmetic, and the powers of 31 modulo 2^32 form a cyclic group of
    4285              :          * order 2^27. So for arrays of up to 2^27 elements, each element's
    4286              :          * hash value is multiplied by a different (odd) number, resulting in
    4287              :          * a good mixing of all the elements' hash values.
    4288              :          */
    4289        46749 :         result = (result << 5) - result + elthash;
    4290              :     }
    4291              : 
    4292              :     /* Avoid leaking memory when handed toasted input. */
    4293        28785 :     AARR_FREE_IF_COPY(array, 0);
    4294              : 
    4295        28785 :     PG_RETURN_UINT32(result);
    4296              : }
    4297              : 
    4298              : /*
    4299              :  * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
    4300              :  * Otherwise, similar to hash_array.
    4301              :  */
    4302              : Datum
    4303           72 : hash_array_extended(PG_FUNCTION_ARGS)
    4304              : {
    4305           72 :     LOCAL_FCINFO(locfcinfo, 2);
    4306           72 :     AnyArrayType *array = PG_GETARG_ANY_ARRAY_P(0);
    4307           72 :     uint64      seed = PG_GETARG_INT64(1);
    4308           72 :     int         ndims = AARR_NDIM(array);
    4309           72 :     int        *dims = AARR_DIMS(array);
    4310           72 :     Oid         element_type = AARR_ELEMTYPE(array);
    4311           72 :     uint64      result = 1;
    4312              :     int         nitems;
    4313              :     TypeCacheEntry *typentry;
    4314              :     int         typlen;
    4315              :     bool        typbyval;
    4316              :     char        typalign;
    4317              :     int         i;
    4318              :     array_iter  iter;
    4319              : 
    4320           72 :     typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
    4321           72 :     if (typentry == NULL ||
    4322           42 :         typentry->type_id != element_type)
    4323              :     {
    4324           30 :         typentry = lookup_type_cache(element_type,
    4325              :                                      TYPECACHE_HASH_EXTENDED_PROC_FINFO);
    4326           30 :         if (!OidIsValid(typentry->hash_extended_proc_finfo.fn_oid))
    4327            3 :             ereport(ERROR,
    4328              :                     (errcode(ERRCODE_UNDEFINED_FUNCTION),
    4329              :                      errmsg("could not identify an extended hash function for type %s",
    4330              :                             format_type_be(element_type))));
    4331           27 :         fcinfo->flinfo->fn_extra = typentry;
    4332              :     }
    4333           69 :     typlen = typentry->typlen;
    4334           69 :     typbyval = typentry->typbyval;
    4335           69 :     typalign = typentry->typalign;
    4336              : 
    4337           69 :     InitFunctionCallInfoData(*locfcinfo, &typentry->hash_extended_proc_finfo, 2,
    4338              :                              PG_GET_COLLATION(), NULL, NULL);
    4339              : 
    4340              :     /* Loop over source data */
    4341           69 :     nitems = ArrayGetNItems(ndims, dims);
    4342           69 :     array_iter_setup(&iter, array, typlen, typbyval, typalign);
    4343              : 
    4344          228 :     for (i = 0; i < nitems; i++)
    4345              :     {
    4346              :         Datum       elt;
    4347              :         bool        isnull;
    4348              :         uint64      elthash;
    4349              : 
    4350              :         /* Get element, checking for NULL */
    4351          159 :         elt = array_iter_next(&iter, &isnull, i);
    4352              : 
    4353          159 :         if (isnull)
    4354              :         {
    4355            0 :             elthash = 0;
    4356              :         }
    4357              :         else
    4358              :         {
    4359              :             /* Apply the hash function */
    4360          159 :             locfcinfo->args[0].value = elt;
    4361          159 :             locfcinfo->args[0].isnull = false;
    4362          159 :             locfcinfo->args[1].value = Int64GetDatum(seed);
    4363          159 :             locfcinfo->args[1].isnull = false;
    4364          159 :             elthash = DatumGetUInt64(FunctionCallInvoke(locfcinfo));
    4365              :             /* We don't expect hash functions to return null */
    4366              :             Assert(!locfcinfo->isnull);
    4367              :         }
    4368              : 
    4369          159 :         result = (result << 5) - result + elthash;
    4370              :     }
    4371              : 
    4372           69 :     AARR_FREE_IF_COPY(array, 0);
    4373              : 
    4374           69 :     PG_RETURN_UINT64(result);
    4375              : }
    4376              : 
    4377              : 
    4378              : /*-----------------------------------------------------------------------------
    4379              :  * array overlap/containment comparisons
    4380              :  *      These use the same methods of comparing array elements as array_eq.
    4381              :  *      We consider only the elements of the arrays, ignoring dimensionality.
    4382              :  *----------------------------------------------------------------------------
    4383              :  */
    4384              : 
    4385              : /*
    4386              :  * array_contain_compare :
    4387              :  *        compares two arrays for overlap/containment
    4388              :  *
    4389              :  * When matchall is true, return true if all members of array1 are in array2.
    4390              :  * When matchall is false, return true if any members of array1 are in array2.
    4391              :  */
    4392              : static bool
    4393        11229 : array_contain_compare(AnyArrayType *array1, AnyArrayType *array2, Oid collation,
    4394              :                       bool matchall, void **fn_extra)
    4395              : {
    4396        11229 :     LOCAL_FCINFO(locfcinfo, 2);
    4397        11229 :     bool        result = matchall;
    4398        11229 :     Oid         element_type = AARR_ELEMTYPE(array1);
    4399              :     TypeCacheEntry *typentry;
    4400              :     int         nelems1;
    4401              :     Datum      *values2;
    4402              :     bool       *nulls2;
    4403              :     int         nelems2;
    4404              :     int         typlen;
    4405              :     bool        typbyval;
    4406              :     char        typalign;
    4407              :     int         i;
    4408              :     int         j;
    4409              :     array_iter  it1;
    4410              : 
    4411        11229 :     if (element_type != AARR_ELEMTYPE(array2))
    4412            0 :         ereport(ERROR,
    4413              :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    4414              :                  errmsg("cannot compare arrays of different element types")));
    4415              : 
    4416              :     /*
    4417              :      * We arrange to look up the equality function only once per series of
    4418              :      * calls, assuming the element type doesn't change underneath us.  The
    4419              :      * typcache is used so that we have no memory leakage when being used as
    4420              :      * an index support function.
    4421              :      */
    4422        11229 :     typentry = (TypeCacheEntry *) *fn_extra;
    4423        11229 :     if (typentry == NULL ||
    4424        10995 :         typentry->type_id != element_type)
    4425              :     {
    4426          234 :         typentry = lookup_type_cache(element_type,
    4427              :                                      TYPECACHE_EQ_OPR_FINFO);
    4428          234 :         if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
    4429            0 :             ereport(ERROR,
    4430              :                     (errcode(ERRCODE_UNDEFINED_FUNCTION),
    4431              :                      errmsg("could not identify an equality operator for type %s",
    4432              :                             format_type_be(element_type))));
    4433          234 :         *fn_extra = typentry;
    4434              :     }
    4435        11229 :     typlen = typentry->typlen;
    4436        11229 :     typbyval = typentry->typbyval;
    4437        11229 :     typalign = typentry->typalign;
    4438              : 
    4439              :     /*
    4440              :      * Since we probably will need to scan array2 multiple times, it's
    4441              :      * worthwhile to use deconstruct_array on it.  We scan array1 the hard way
    4442              :      * however, since we very likely won't need to look at all of it.
    4443              :      */
    4444        11229 :     if (VARATT_IS_EXPANDED_HEADER(array2))
    4445              :     {
    4446              :         /* This should be safe even if input is read-only */
    4447         2556 :         deconstruct_expanded_array(&(array2->xpn));
    4448         2556 :         values2 = array2->xpn.dvalues;
    4449         2556 :         nulls2 = array2->xpn.dnulls;
    4450         2556 :         nelems2 = array2->xpn.nelems;
    4451              :     }
    4452              :     else
    4453         8673 :         deconstruct_array((ArrayType *) array2,
    4454              :                           element_type, typlen, typbyval, typalign,
    4455              :                           &values2, &nulls2, &nelems2);
    4456              : 
    4457              :     /*
    4458              :      * Apply the comparison operator to each pair of array elements.
    4459              :      */
    4460        11229 :     InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
    4461              :                              collation, NULL, NULL);
    4462              : 
    4463              :     /* Loop over source data */
    4464        11229 :     nelems1 = ArrayGetNItems(AARR_NDIM(array1), AARR_DIMS(array1));
    4465        11229 :     array_iter_setup(&it1, array1, typlen, typbyval, typalign);
    4466              : 
    4467       210167 :     for (i = 0; i < nelems1; i++)
    4468              :     {
    4469              :         Datum       elt1;
    4470              :         bool        isnull1;
    4471              : 
    4472              :         /* Get element, checking for NULL */
    4473       203661 :         elt1 = array_iter_next(&it1, &isnull1, i);
    4474              : 
    4475              :         /*
    4476              :          * We assume that the comparison operator is strict, so a NULL can't
    4477              :          * match anything.  XXX this diverges from the "NULL=NULL" behavior of
    4478              :          * array_eq, should we act like that?
    4479              :          */
    4480       203661 :         if (isnull1)
    4481              :         {
    4482          660 :             if (matchall)
    4483              :             {
    4484          630 :                 result = false;
    4485         4723 :                 break;
    4486              :             }
    4487           30 :             continue;
    4488              :         }
    4489              : 
    4490      9208184 :         for (j = 0; j < nelems2; j++)
    4491              :         {
    4492      9188482 :             Datum       elt2 = values2[j];
    4493      9188482 :             bool        isnull2 = nulls2 ? nulls2[j] : false;
    4494              :             bool        oprresult;
    4495              : 
    4496      9188482 :             if (isnull2)
    4497         3606 :                 continue;       /* can't match */
    4498              : 
    4499              :             /*
    4500              :              * Apply the operator to the element pair; treat NULL as false
    4501              :              */
    4502      9184876 :             locfcinfo->args[0].value = elt1;
    4503      9184876 :             locfcinfo->args[0].isnull = false;
    4504      9184876 :             locfcinfo->args[1].value = elt2;
    4505      9184876 :             locfcinfo->args[1].isnull = false;
    4506      9184876 :             locfcinfo->isnull = false;
    4507      9184876 :             oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
    4508      9184876 :             if (!locfcinfo->isnull && oprresult)
    4509       183299 :                 break;
    4510              :         }
    4511              : 
    4512       203001 :         if (j < nelems2)
    4513              :         {
    4514              :             /* found a match for elt1 */
    4515       183299 :             if (!matchall)
    4516              :             {
    4517          114 :                 result = true;
    4518          114 :                 break;
    4519              :             }
    4520              :         }
    4521              :         else
    4522              :         {
    4523              :             /* no match for elt1 */
    4524        19702 :             if (matchall)
    4525              :             {
    4526         3979 :                 result = false;
    4527         3979 :                 break;
    4528              :             }
    4529              :         }
    4530              :     }
    4531              : 
    4532        11229 :     return result;
    4533              : }
    4534              : 
    4535              : Datum
    4536         3060 : arrayoverlap(PG_FUNCTION_ARGS)
    4537              : {
    4538         3060 :     AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
    4539         3060 :     AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
    4540         3060 :     Oid         collation = PG_GET_COLLATION();
    4541              :     bool        result;
    4542              : 
    4543         3060 :     result = array_contain_compare(array1, array2, collation, false,
    4544         3060 :                                    &fcinfo->flinfo->fn_extra);
    4545              : 
    4546              :     /* Avoid leaking memory when handed toasted input. */
    4547         3060 :     AARR_FREE_IF_COPY(array1, 0);
    4548         3060 :     AARR_FREE_IF_COPY(array2, 1);
    4549              : 
    4550         3060 :     PG_RETURN_BOOL(result);
    4551              : }
    4552              : 
    4553              : Datum
    4554         4911 : arraycontains(PG_FUNCTION_ARGS)
    4555              : {
    4556         4911 :     AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
    4557         4911 :     AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
    4558         4911 :     Oid         collation = PG_GET_COLLATION();
    4559              :     bool        result;
    4560              : 
    4561         4911 :     result = array_contain_compare(array2, array1, collation, true,
    4562         4911 :                                    &fcinfo->flinfo->fn_extra);
    4563              : 
    4564              :     /* Avoid leaking memory when handed toasted input. */
    4565         4911 :     AARR_FREE_IF_COPY(array1, 0);
    4566         4911 :     AARR_FREE_IF_COPY(array2, 1);
    4567              : 
    4568         4911 :     PG_RETURN_BOOL(result);
    4569              : }
    4570              : 
    4571              : Datum
    4572         3258 : arraycontained(PG_FUNCTION_ARGS)
    4573              : {
    4574         3258 :     AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
    4575         3258 :     AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
    4576         3258 :     Oid         collation = PG_GET_COLLATION();
    4577              :     bool        result;
    4578              : 
    4579         3258 :     result = array_contain_compare(array1, array2, collation, true,
    4580         3258 :                                    &fcinfo->flinfo->fn_extra);
    4581              : 
    4582              :     /* Avoid leaking memory when handed toasted input. */
    4583         3258 :     AARR_FREE_IF_COPY(array1, 0);
    4584         3258 :     AARR_FREE_IF_COPY(array2, 1);
    4585              : 
    4586         3258 :     PG_RETURN_BOOL(result);
    4587              : }
    4588              : 
    4589              : 
    4590              : /*-----------------------------------------------------------------------------
    4591              :  * Array iteration functions
    4592              :  *      These functions are used to iterate efficiently through arrays
    4593              :  *-----------------------------------------------------------------------------
    4594              :  */
    4595              : 
    4596              : /*
    4597              :  * array_create_iterator --- set up to iterate through an array
    4598              :  *
    4599              :  * If slice_ndim is zero, we will iterate element-by-element; the returned
    4600              :  * datums are of the array's element type.
    4601              :  *
    4602              :  * If slice_ndim is 1..ARR_NDIM(arr), we will iterate by slices: the
    4603              :  * returned datums are of the same array type as 'arr', but of size
    4604              :  * equal to the rightmost N dimensions of 'arr'.
    4605              :  *
    4606              :  * The passed-in array must remain valid for the lifetime of the iterator.
    4607              :  */
    4608              : ArrayIterator
    4609          289 : array_create_iterator(ArrayType *arr, int slice_ndim, ArrayMetaState *mstate)
    4610              : {
    4611          289 :     ArrayIterator iterator = palloc0_object(ArrayIteratorData);
    4612              : 
    4613              :     /*
    4614              :      * Sanity-check inputs --- caller should have got this right already
    4615              :      */
    4616              :     Assert(arr);
    4617          289 :     if (slice_ndim < 0 || slice_ndim > ARR_NDIM(arr))
    4618            0 :         elog(ERROR, "invalid arguments to array_create_iterator");
    4619              : 
    4620              :     /*
    4621              :      * Remember basic info about the array and its element type
    4622              :      */
    4623          289 :     iterator->arr = arr;
    4624          289 :     iterator->nullbitmap = ARR_NULLBITMAP(arr);
    4625          289 :     iterator->nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
    4626              : 
    4627          289 :     if (mstate != NULL)
    4628              :     {
    4629              :         Assert(mstate->element_type == ARR_ELEMTYPE(arr));
    4630              : 
    4631          192 :         iterator->typlen = mstate->typlen;
    4632          192 :         iterator->typbyval = mstate->typbyval;
    4633          192 :         iterator->typalign = mstate->typalign;
    4634              :     }
    4635              :     else
    4636           97 :         get_typlenbyvalalign(ARR_ELEMTYPE(arr),
    4637              :                              &iterator->typlen,
    4638              :                              &iterator->typbyval,
    4639              :                              &iterator->typalign);
    4640          289 :     iterator->typalignby = typalign_to_alignby(iterator->typalign);
    4641              : 
    4642              :     /*
    4643              :      * Remember the slicing parameters.
    4644              :      */
    4645          289 :     iterator->slice_ndim = slice_ndim;
    4646              : 
    4647          289 :     if (slice_ndim > 0)
    4648              :     {
    4649              :         /*
    4650              :          * Get pointers into the array's dims and lbound arrays to represent
    4651              :          * the dims/lbound arrays of a slice.  These are the same as the
    4652              :          * rightmost N dimensions of the array.
    4653              :          */
    4654           39 :         iterator->slice_dims = ARR_DIMS(arr) + ARR_NDIM(arr) - slice_ndim;
    4655           39 :         iterator->slice_lbound = ARR_LBOUND(arr) + ARR_NDIM(arr) - slice_ndim;
    4656              : 
    4657              :         /*
    4658              :          * Compute number of elements in a slice.
    4659              :          */
    4660           78 :         iterator->slice_len = ArrayGetNItems(slice_ndim,
    4661           39 :                                              iterator->slice_dims);
    4662              : 
    4663              :         /*
    4664              :          * Create workspace for building sub-arrays.
    4665              :          */
    4666           39 :         iterator->slice_values = (Datum *)
    4667           39 :             palloc(iterator->slice_len * sizeof(Datum));
    4668           39 :         iterator->slice_nulls = (bool *)
    4669           39 :             palloc(iterator->slice_len * sizeof(bool));
    4670              :     }
    4671              : 
    4672              :     /*
    4673              :      * Initialize our data pointer and linear element number.  These will
    4674              :      * advance through the array during array_iterate().
    4675              :      */
    4676          289 :     iterator->data_ptr = ARR_DATA_PTR(arr);
    4677          289 :     iterator->current_item = 0;
    4678              : 
    4679          289 :     return iterator;
    4680              : }
    4681              : 
    4682              : /*
    4683              :  * Iterate through the array referenced by 'iterator'.
    4684              :  *
    4685              :  * As long as there is another element (or slice), return it into
    4686              :  * *value / *isnull, and return true.  Return false when no more data.
    4687              :  */
    4688              : bool
    4689         4712 : array_iterate(ArrayIterator iterator, Datum *value, bool *isnull)
    4690              : {
    4691              :     /* Done if we have reached the end of the array */
    4692         4712 :     if (iterator->current_item >= iterator->nitems)
    4693          199 :         return false;
    4694              : 
    4695         4513 :     if (iterator->slice_ndim == 0)
    4696              :     {
    4697              :         /*
    4698              :          * Scalar case: return one element.
    4699              :          */
    4700         4435 :         if (array_get_isnull(iterator->nullbitmap, iterator->current_item++))
    4701              :         {
    4702           33 :             *isnull = true;
    4703           33 :             *value = (Datum) 0;
    4704              :         }
    4705              :         else
    4706              :         {
    4707              :             /* non-NULL, so fetch the individual Datum to return */
    4708         4402 :             char       *p = iterator->data_ptr;
    4709              : 
    4710         4402 :             *isnull = false;
    4711         4402 :             *value = fetch_att(p, iterator->typbyval, iterator->typlen);
    4712              : 
    4713              :             /* Move our data pointer forward to the next element */
    4714         4402 :             p = att_addlength_pointer(p, iterator->typlen, p);
    4715         4402 :             p = (char *) att_nominal_alignby(p, iterator->typalignby);
    4716         4402 :             iterator->data_ptr = p;
    4717              :         }
    4718              :     }
    4719              :     else
    4720              :     {
    4721              :         /*
    4722              :          * Slice case: build and return an array of the requested size.
    4723              :          */
    4724              :         ArrayType  *result;
    4725           78 :         Datum      *values = iterator->slice_values;
    4726           78 :         bool       *nulls = iterator->slice_nulls;
    4727           78 :         char       *p = iterator->data_ptr;
    4728              :         int         i;
    4729              : 
    4730          249 :         for (i = 0; i < iterator->slice_len; i++)
    4731              :         {
    4732          171 :             if (array_get_isnull(iterator->nullbitmap,
    4733          171 :                                  iterator->current_item++))
    4734              :             {
    4735            0 :                 nulls[i] = true;
    4736            0 :                 values[i] = (Datum) 0;
    4737              :             }
    4738              :             else
    4739              :             {
    4740          171 :                 nulls[i] = false;
    4741          171 :                 values[i] = fetch_att(p, iterator->typbyval, iterator->typlen);
    4742              : 
    4743              :                 /* Move our data pointer forward to the next element */
    4744          171 :                 p = att_addlength_pointer(p, iterator->typlen, p);
    4745          171 :                 p = (char *) att_nominal_alignby(p, iterator->typalignby);
    4746              :             }
    4747              :         }
    4748              : 
    4749           78 :         iterator->data_ptr = p;
    4750              : 
    4751           78 :         result = construct_md_array(values,
    4752              :                                     nulls,
    4753              :                                     iterator->slice_ndim,
    4754              :                                     iterator->slice_dims,
    4755              :                                     iterator->slice_lbound,
    4756           78 :                                     ARR_ELEMTYPE(iterator->arr),
    4757           78 :                                     iterator->typlen,
    4758           78 :                                     iterator->typbyval,
    4759           78 :                                     iterator->typalign);
    4760              : 
    4761           78 :         *isnull = false;
    4762           78 :         *value = PointerGetDatum(result);
    4763              :     }
    4764              : 
    4765         4513 :     return true;
    4766              : }
    4767              : 
    4768              : /*
    4769              :  * Release an ArrayIterator data structure
    4770              :  */
    4771              : void
    4772          192 : array_free_iterator(ArrayIterator iterator)
    4773              : {
    4774          192 :     if (iterator->slice_ndim > 0)
    4775              :     {
    4776           21 :         pfree(iterator->slice_values);
    4777           21 :         pfree(iterator->slice_nulls);
    4778              :     }
    4779          192 :     pfree(iterator);
    4780          192 : }
    4781              : 
    4782              : 
    4783              : /***************************************************************************/
    4784              : /******************|          Support  Routines           |*****************/
    4785              : /***************************************************************************/
    4786              : 
    4787              : /*
    4788              :  * Check whether a specific array element is NULL
    4789              :  *
    4790              :  * nullbitmap: pointer to array's null bitmap (NULL if none)
    4791              :  * offset: 0-based linear element number of array element
    4792              :  */
    4793              : static bool
    4794       447217 : array_get_isnull(const bits8 *nullbitmap, int offset)
    4795              : {
    4796       447217 :     if (nullbitmap == NULL)
    4797       446801 :         return false;           /* assume not null */
    4798          416 :     if (nullbitmap[offset / 8] & (1 << (offset % 8)))
    4799          329 :         return false;           /* not null */
    4800           87 :     return true;
    4801              : }
    4802              : 
    4803              : /*
    4804              :  * Set a specific array element's null-bitmap entry
    4805              :  *
    4806              :  * nullbitmap: pointer to array's null bitmap (mustn't be NULL)
    4807              :  * offset: 0-based linear element number of array element
    4808              :  * isNull: null status to set
    4809              :  */
    4810              : static void
    4811           67 : array_set_isnull(bits8 *nullbitmap, int offset, bool isNull)
    4812              : {
    4813              :     int         bitmask;
    4814              : 
    4815           67 :     nullbitmap += offset / 8;
    4816           67 :     bitmask = 1 << (offset % 8);
    4817           67 :     if (isNull)
    4818           10 :         *nullbitmap &= ~bitmask;
    4819              :     else
    4820           57 :         *nullbitmap |= bitmask;
    4821           67 : }
    4822              : 
    4823              : /*
    4824              :  * Fetch array element at pointer, converted correctly to a Datum
    4825              :  *
    4826              :  * Caller must have handled case of NULL element
    4827              :  */
    4828              : static Datum
    4829       442271 : ArrayCast(char *value, bool byval, int len)
    4830              : {
    4831       442271 :     return fetch_att(value, byval, len);
    4832              : }
    4833              : 
    4834              : /*
    4835              :  * Copy datum to *dest and return total space used (including align padding)
    4836              :  *
    4837              :  * Caller must have handled case of NULL element
    4838              :  */
    4839              : static int
    4840      7220480 : ArrayCastAndSet(Datum src,
    4841              :                 int typlen,
    4842              :                 bool typbyval,
    4843              :                 uint8 typalignby,
    4844              :                 char *dest)
    4845              : {
    4846              :     int         inc;
    4847              : 
    4848      7220480 :     if (typlen > 0)
    4849              :     {
    4850      5520992 :         if (typbyval)
    4851      3555635 :             store_att_byval(dest, src, typlen);
    4852              :         else
    4853      1965357 :             memmove(dest, DatumGetPointer(src), typlen);
    4854      5520992 :         inc = att_nominal_alignby(typlen, typalignby);
    4855              :     }
    4856              :     else
    4857              :     {
    4858              :         Assert(!typbyval);
    4859      1699488 :         inc = att_addlength_datum(0, typlen, src);
    4860      1699488 :         memmove(dest, DatumGetPointer(src), inc);
    4861      1699488 :         inc = att_nominal_alignby(inc, typalignby);
    4862              :     }
    4863              : 
    4864      7220480 :     return inc;
    4865              : }
    4866              : 
    4867              : /*
    4868              :  * Advance ptr over nitems array elements
    4869              :  *
    4870              :  * ptr: starting location in array
    4871              :  * offset: 0-based linear element number of first element (the one at *ptr)
    4872              :  * nullbitmap: start of array's null bitmap, or NULL if none
    4873              :  * nitems: number of array elements to advance over (>= 0)
    4874              :  * typlen, typbyval, typalign: storage parameters of array element datatype
    4875              :  *
    4876              :  * It is caller's responsibility to ensure that nitems is within range
    4877              :  */
    4878              : static char *
    4879       443496 : array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
    4880              :            int typlen, bool typbyval, char typalign)
    4881              : {
    4882       443496 :     uint8       typalignby = typalign_to_alignby(typalign);
    4883              :     int         bitmask;
    4884              :     int         i;
    4885              : 
    4886              :     /* easy if fixed-size elements and no NULLs */
    4887       443496 :     if (typlen > 0 && !nullbitmap)
    4888       312739 :         return ptr + nitems * ((Size) att_nominal_alignby(typlen, typalignby));
    4889              : 
    4890              :     /* seems worth having separate loops for NULL and no-NULLs cases */
    4891       130757 :     if (nullbitmap)
    4892              :     {
    4893          302 :         nullbitmap += offset / 8;
    4894          302 :         bitmask = 1 << (offset % 8);
    4895              : 
    4896          926 :         for (i = 0; i < nitems; i++)
    4897              :         {
    4898          624 :             if (*nullbitmap & bitmask)
    4899              :             {
    4900          444 :                 ptr = att_addlength_pointer(ptr, typlen, ptr);
    4901          444 :                 ptr = (char *) att_nominal_alignby(ptr, typalignby);
    4902              :             }
    4903          624 :             bitmask <<= 1;
    4904          624 :             if (bitmask == 0x100)
    4905              :             {
    4906           24 :                 nullbitmap++;
    4907           24 :                 bitmask = 1;
    4908              :             }
    4909              :         }
    4910              :     }
    4911              :     else
    4912              :     {
    4913       590022 :         for (i = 0; i < nitems; i++)
    4914              :         {
    4915       459567 :             ptr = att_addlength_pointer(ptr, typlen, ptr);
    4916       459567 :             ptr = (char *) att_nominal_alignby(ptr, typalignby);
    4917              :         }
    4918              :     }
    4919       130757 :     return ptr;
    4920              : }
    4921              : 
    4922              : /*
    4923              :  * Compute total size of the nitems array elements starting at *ptr
    4924              :  *
    4925              :  * Parameters same as for array_seek
    4926              :  */
    4927              : static int
    4928          819 : array_nelems_size(char *ptr, int offset, bits8 *nullbitmap, int nitems,
    4929              :                   int typlen, bool typbyval, char typalign)
    4930              : {
    4931          819 :     return array_seek(ptr, offset, nullbitmap, nitems,
    4932          819 :                       typlen, typbyval, typalign) - ptr;
    4933              : }
    4934              : 
    4935              : /*
    4936              :  * Copy nitems array elements from srcptr to destptr
    4937              :  *
    4938              :  * destptr: starting destination location (must be enough room!)
    4939              :  * nitems: number of array elements to copy (>= 0)
    4940              :  * srcptr: starting location in source array
    4941              :  * offset: 0-based linear element number of first element (the one at *srcptr)
    4942              :  * nullbitmap: start of source array's null bitmap, or NULL if none
    4943              :  * typlen, typbyval, typalign: storage parameters of array element datatype
    4944              :  *
    4945              :  * Returns number of bytes copied
    4946              :  *
    4947              :  * NB: this does not take care of setting up the destination's null bitmap!
    4948              :  */
    4949              : static int
    4950          585 : array_copy(char *destptr, int nitems,
    4951              :            char *srcptr, int offset, bits8 *nullbitmap,
    4952              :            int typlen, bool typbyval, char typalign)
    4953              : {
    4954              :     int         numbytes;
    4955              : 
    4956          585 :     numbytes = array_nelems_size(srcptr, offset, nullbitmap, nitems,
    4957              :                                  typlen, typbyval, typalign);
    4958          585 :     memcpy(destptr, srcptr, numbytes);
    4959          585 :     return numbytes;
    4960              : }
    4961              : 
    4962              : /*
    4963              :  * Copy nitems null-bitmap bits from source to destination
    4964              :  *
    4965              :  * destbitmap: start of destination array's null bitmap (mustn't be NULL)
    4966              :  * destoffset: 0-based linear element number of first dest element
    4967              :  * srcbitmap: start of source array's null bitmap, or NULL if none
    4968              :  * srcoffset: 0-based linear element number of first source element
    4969              :  * nitems: number of bits to copy (>= 0)
    4970              :  *
    4971              :  * If srcbitmap is NULL then we assume the source is all-non-NULL and
    4972              :  * fill 1's into the destination bitmap.  Note that only the specified
    4973              :  * bits in the destination map are changed, not any before or after.
    4974              :  *
    4975              :  * Note: this could certainly be optimized using standard bitblt methods.
    4976              :  * However, it's not clear that the typical Postgres array has enough elements
    4977              :  * to make it worth worrying too much.  For the moment, KISS.
    4978              :  */
    4979              : void
    4980        15566 : array_bitmap_copy(bits8 *destbitmap, int destoffset,
    4981              :                   const bits8 *srcbitmap, int srcoffset,
    4982              :                   int nitems)
    4983              : {
    4984              :     int         destbitmask,
    4985              :                 destbitval,
    4986              :                 srcbitmask,
    4987              :                 srcbitval;
    4988              : 
    4989              :     Assert(destbitmap);
    4990        15566 :     if (nitems <= 0)
    4991          100 :         return;                 /* don't risk fetch off end of memory */
    4992        15466 :     destbitmap += destoffset / 8;
    4993        15466 :     destbitmask = 1 << (destoffset % 8);
    4994        15466 :     destbitval = *destbitmap;
    4995        15466 :     if (srcbitmap)
    4996              :     {
    4997         7871 :         srcbitmap += srcoffset / 8;
    4998         7871 :         srcbitmask = 1 << (srcoffset % 8);
    4999         7871 :         srcbitval = *srcbitmap;
    5000        35744 :         while (nitems-- > 0)
    5001              :         {
    5002        27873 :             if (srcbitval & srcbitmask)
    5003        10454 :                 destbitval |= destbitmask;
    5004              :             else
    5005        17419 :                 destbitval &= ~destbitmask;
    5006        27873 :             destbitmask <<= 1;
    5007        27873 :             if (destbitmask == 0x100)
    5008              :             {
    5009         3276 :                 *destbitmap++ = destbitval;
    5010         3276 :                 destbitmask = 1;
    5011         3276 :                 if (nitems > 0)
    5012         2425 :                     destbitval = *destbitmap;
    5013              :             }
    5014        27873 :             srcbitmask <<= 1;
    5015        27873 :             if (srcbitmask == 0x100)
    5016              :             {
    5017         2440 :                 srcbitmap++;
    5018         2440 :                 srcbitmask = 1;
    5019         2440 :                 if (nitems > 0)
    5020         2401 :                     srcbitval = *srcbitmap;
    5021              :             }
    5022              :         }
    5023         7871 :         if (destbitmask != 1)
    5024         7020 :             *destbitmap = destbitval;
    5025              :     }
    5026              :     else
    5027              :     {
    5028        15402 :         while (nitems-- > 0)
    5029              :         {
    5030         7807 :             destbitval |= destbitmask;
    5031         7807 :             destbitmask <<= 1;
    5032         7807 :             if (destbitmask == 0x100)
    5033              :             {
    5034         1079 :                 *destbitmap++ = destbitval;
    5035         1079 :                 destbitmask = 1;
    5036         1079 :                 if (nitems > 0)
    5037            0 :                     destbitval = *destbitmap;
    5038              :             }
    5039              :         }
    5040         7595 :         if (destbitmask != 1)
    5041         6516 :             *destbitmap = destbitval;
    5042              :     }
    5043              : }
    5044              : 
    5045              : /*
    5046              :  * Compute space needed for a slice of an array
    5047              :  *
    5048              :  * We assume the caller has verified that the slice coordinates are valid.
    5049              :  */
    5050              : static int
    5051          153 : array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
    5052              :                  int ndim, int *dim, int *lb,
    5053              :                  int *st, int *endp,
    5054              :                  int typlen, bool typbyval, char typalign)
    5055              : {
    5056              :     int         src_offset,
    5057              :                 span[MAXDIM],
    5058              :                 prod[MAXDIM],
    5059              :                 dist[MAXDIM],
    5060              :                 indx[MAXDIM];
    5061              :     char       *ptr;
    5062              :     int         i,
    5063              :                 j,
    5064              :                 inc;
    5065          153 :     int         count = 0;
    5066          153 :     uint8       typalignby = typalign_to_alignby(typalign);
    5067              : 
    5068          153 :     mda_get_range(ndim, span, st, endp);
    5069              : 
    5070              :     /* Pretty easy for fixed element length without nulls ... */
    5071          153 :     if (typlen > 0 && !arraynullsptr)
    5072          114 :         return ArrayGetNItems(ndim, span) * att_nominal_alignby(typlen, typalignby);
    5073              : 
    5074              :     /* Else gotta do it the hard way */
    5075           39 :     src_offset = ArrayGetOffset(ndim, dim, lb, st);
    5076           39 :     ptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
    5077              :                      typlen, typbyval, typalign);
    5078           39 :     mda_get_prod(ndim, dim, prod);
    5079           39 :     mda_get_offset_values(ndim, dist, prod, span);
    5080           99 :     for (i = 0; i < ndim; i++)
    5081           60 :         indx[i] = 0;
    5082           39 :     j = ndim - 1;
    5083              :     do
    5084              :     {
    5085          129 :         if (dist[j])
    5086              :         {
    5087            0 :             ptr = array_seek(ptr, src_offset, arraynullsptr, dist[j],
    5088              :                              typlen, typbyval, typalign);
    5089            0 :             src_offset += dist[j];
    5090              :         }
    5091          129 :         if (!array_get_isnull(arraynullsptr, src_offset))
    5092              :         {
    5093          123 :             inc = att_addlength_pointer(0, typlen, ptr);
    5094          123 :             inc = att_nominal_alignby(inc, typalignby);
    5095          123 :             ptr += inc;
    5096          123 :             count += inc;
    5097              :         }
    5098          129 :         src_offset++;
    5099          129 :     } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
    5100           39 :     return count;
    5101              : }
    5102              : 
    5103              : /*
    5104              :  * Extract a slice of an array into consecutive elements in the destination
    5105              :  * array.
    5106              :  *
    5107              :  * We assume the caller has verified that the slice coordinates are valid,
    5108              :  * allocated enough storage for the result, and initialized the header
    5109              :  * of the new array.
    5110              :  */
    5111              : static void
    5112          138 : array_extract_slice(ArrayType *newarray,
    5113              :                     int ndim,
    5114              :                     int *dim,
    5115              :                     int *lb,
    5116              :                     char *arraydataptr,
    5117              :                     bits8 *arraynullsptr,
    5118              :                     int *st,
    5119              :                     int *endp,
    5120              :                     int typlen,
    5121              :                     bool typbyval,
    5122              :                     char typalign)
    5123              : {
    5124          138 :     char       *destdataptr = ARR_DATA_PTR(newarray);
    5125          138 :     bits8      *destnullsptr = ARR_NULLBITMAP(newarray);
    5126              :     char       *srcdataptr;
    5127              :     int         src_offset,
    5128              :                 dest_offset,
    5129              :                 prod[MAXDIM],
    5130              :                 span[MAXDIM],
    5131              :                 dist[MAXDIM],
    5132              :                 indx[MAXDIM];
    5133              :     int         i,
    5134              :                 j,
    5135              :                 inc;
    5136              : 
    5137          138 :     src_offset = ArrayGetOffset(ndim, dim, lb, st);
    5138          138 :     srcdataptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
    5139              :                             typlen, typbyval, typalign);
    5140          138 :     mda_get_prod(ndim, dim, prod);
    5141          138 :     mda_get_range(ndim, span, st, endp);
    5142          138 :     mda_get_offset_values(ndim, dist, prod, span);
    5143          348 :     for (i = 0; i < ndim; i++)
    5144          210 :         indx[i] = 0;
    5145          138 :     dest_offset = 0;
    5146          138 :     j = ndim - 1;
    5147              :     do
    5148              :     {
    5149          507 :         if (dist[j])
    5150              :         {
    5151              :             /* skip unwanted elements */
    5152           15 :             srcdataptr = array_seek(srcdataptr, src_offset, arraynullsptr,
    5153              :                                     dist[j],
    5154              :                                     typlen, typbyval, typalign);
    5155           15 :             src_offset += dist[j];
    5156              :         }
    5157          507 :         inc = array_copy(destdataptr, 1,
    5158              :                          srcdataptr, src_offset, arraynullsptr,
    5159              :                          typlen, typbyval, typalign);
    5160          507 :         if (destnullsptr)
    5161           90 :             array_bitmap_copy(destnullsptr, dest_offset,
    5162              :                               arraynullsptr, src_offset,
    5163              :                               1);
    5164          507 :         destdataptr += inc;
    5165          507 :         srcdataptr += inc;
    5166          507 :         src_offset++;
    5167          507 :         dest_offset++;
    5168          507 :     } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
    5169          138 : }
    5170              : 
    5171              : /*
    5172              :  * Insert a slice into an array.
    5173              :  *
    5174              :  * ndim/dim[]/lb[] are dimensions of the original array.  A new array with
    5175              :  * those same dimensions is to be constructed.  destArray must already
    5176              :  * have been allocated and its header initialized.
    5177              :  *
    5178              :  * st[]/endp[] identify the slice to be replaced.  Elements within the slice
    5179              :  * volume are taken from consecutive elements of the srcArray; elements
    5180              :  * outside it are copied from origArray.
    5181              :  *
    5182              :  * We assume the caller has verified that the slice coordinates are valid.
    5183              :  */
    5184              : static void
    5185           15 : array_insert_slice(ArrayType *destArray,
    5186              :                    ArrayType *origArray,
    5187              :                    ArrayType *srcArray,
    5188              :                    int ndim,
    5189              :                    int *dim,
    5190              :                    int *lb,
    5191              :                    int *st,
    5192              :                    int *endp,
    5193              :                    int typlen,
    5194              :                    bool typbyval,
    5195              :                    char typalign)
    5196              : {
    5197           15 :     char       *destPtr = ARR_DATA_PTR(destArray);
    5198           15 :     char       *origPtr = ARR_DATA_PTR(origArray);
    5199           15 :     char       *srcPtr = ARR_DATA_PTR(srcArray);
    5200           15 :     bits8      *destBitmap = ARR_NULLBITMAP(destArray);
    5201           15 :     bits8      *origBitmap = ARR_NULLBITMAP(origArray);
    5202           15 :     bits8      *srcBitmap = ARR_NULLBITMAP(srcArray);
    5203           15 :     int         orignitems = ArrayGetNItems(ARR_NDIM(origArray),
    5204              :                                             ARR_DIMS(origArray));
    5205              :     int         dest_offset,
    5206              :                 orig_offset,
    5207              :                 src_offset,
    5208              :                 prod[MAXDIM],
    5209              :                 span[MAXDIM],
    5210              :                 dist[MAXDIM],
    5211              :                 indx[MAXDIM];
    5212              :     int         i,
    5213              :                 j,
    5214              :                 inc;
    5215              : 
    5216           15 :     dest_offset = ArrayGetOffset(ndim, dim, lb, st);
    5217              :     /* copy items before the slice start */
    5218           15 :     inc = array_copy(destPtr, dest_offset,
    5219              :                      origPtr, 0, origBitmap,
    5220              :                      typlen, typbyval, typalign);
    5221           15 :     destPtr += inc;
    5222           15 :     origPtr += inc;
    5223           15 :     if (destBitmap)
    5224            0 :         array_bitmap_copy(destBitmap, 0, origBitmap, 0, dest_offset);
    5225           15 :     orig_offset = dest_offset;
    5226           15 :     mda_get_prod(ndim, dim, prod);
    5227           15 :     mda_get_range(ndim, span, st, endp);
    5228           15 :     mda_get_offset_values(ndim, dist, prod, span);
    5229           51 :     for (i = 0; i < ndim; i++)
    5230           36 :         indx[i] = 0;
    5231           15 :     src_offset = 0;
    5232           15 :     j = ndim - 1;
    5233              :     do
    5234              :     {
    5235              :         /* Copy/advance over elements between here and next part of slice */
    5236           39 :         if (dist[j])
    5237              :         {
    5238            9 :             inc = array_copy(destPtr, dist[j],
    5239              :                              origPtr, orig_offset, origBitmap,
    5240              :                              typlen, typbyval, typalign);
    5241            9 :             destPtr += inc;
    5242            9 :             origPtr += inc;
    5243            9 :             if (destBitmap)
    5244            0 :                 array_bitmap_copy(destBitmap, dest_offset,
    5245              :                                   origBitmap, orig_offset,
    5246              :                                   dist[j]);
    5247            9 :             dest_offset += dist[j];
    5248            9 :             orig_offset += dist[j];
    5249              :         }
    5250              :         /* Copy new element at this slice position */
    5251           39 :         inc = array_copy(destPtr, 1,
    5252              :                          srcPtr, src_offset, srcBitmap,
    5253              :                          typlen, typbyval, typalign);
    5254           39 :         if (destBitmap)
    5255            0 :             array_bitmap_copy(destBitmap, dest_offset,
    5256              :                               srcBitmap, src_offset,
    5257              :                               1);
    5258           39 :         destPtr += inc;
    5259           39 :         srcPtr += inc;
    5260           39 :         dest_offset++;
    5261           39 :         src_offset++;
    5262              :         /* Advance over old element at this slice position */
    5263           39 :         origPtr = array_seek(origPtr, orig_offset, origBitmap, 1,
    5264              :                              typlen, typbyval, typalign);
    5265           39 :         orig_offset++;
    5266           39 :     } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
    5267              : 
    5268              :     /* don't miss any data at the end */
    5269           15 :     array_copy(destPtr, orignitems - orig_offset,
    5270              :                origPtr, orig_offset, origBitmap,
    5271              :                typlen, typbyval, typalign);
    5272           15 :     if (destBitmap)
    5273            0 :         array_bitmap_copy(destBitmap, dest_offset,
    5274              :                           origBitmap, orig_offset,
    5275              :                           orignitems - orig_offset);
    5276           15 : }
    5277              : 
    5278              : /*
    5279              :  * initArrayResult - initialize an empty ArrayBuildState
    5280              :  *
    5281              :  *  element_type is the array element type (must be a valid array element type)
    5282              :  *  rcontext is where to keep working state
    5283              :  *  subcontext is a flag determining whether to use a separate memory context
    5284              :  *
    5285              :  * Note: there are two common schemes for using accumArrayResult().
    5286              :  * In the older scheme, you start with a NULL ArrayBuildState pointer, and
    5287              :  * call accumArrayResult once per element.  In this scheme you end up with
    5288              :  * a NULL pointer if there were no elements, which you need to special-case.
    5289              :  * In the newer scheme, call initArrayResult and then call accumArrayResult
    5290              :  * once per element.  In this scheme you always end with a non-NULL pointer
    5291              :  * that you can pass to makeArrayResult; you get an empty array if there
    5292              :  * were no elements.  This is preferred if an empty array is what you want.
    5293              :  *
    5294              :  * It's possible to choose whether to create a separate memory context for the
    5295              :  * array build state, or whether to allocate it directly within rcontext.
    5296              :  *
    5297              :  * When there are many concurrent small states (e.g. array_agg() using hash
    5298              :  * aggregation of many small groups), using a separate memory context for each
    5299              :  * one may result in severe memory bloat. In such cases, use the same memory
    5300              :  * context to initialize all such array build states, and pass
    5301              :  * subcontext=false.
    5302              :  *
    5303              :  * In cases when the array build states have different lifetimes, using a
    5304              :  * single memory context is impractical. Instead, pass subcontext=true so that
    5305              :  * the array build states can be freed individually.
    5306              :  */
    5307              : ArrayBuildState *
    5308       209673 : initArrayResult(Oid element_type, MemoryContext rcontext, bool subcontext)
    5309              : {
    5310              :     /*
    5311              :      * When using a subcontext, we can afford to start with a somewhat larger
    5312              :      * initial array size.  Without subcontexts, we'd better hope that most of
    5313              :      * the states stay small ...
    5314              :      */
    5315       209673 :     return initArrayResultWithSize(element_type, rcontext, subcontext,
    5316              :                                    subcontext ? 64 : 8);
    5317              : }
    5318              : 
    5319              : /*
    5320              :  * initArrayResultWithSize
    5321              :  *      As initArrayResult, but allow the initial size of the allocated arrays
    5322              :  *      to be specified.
    5323              :  */
    5324              : ArrayBuildState *
    5325       209763 : initArrayResultWithSize(Oid element_type, MemoryContext rcontext,
    5326              :                         bool subcontext, int initsize)
    5327              : {
    5328              :     ArrayBuildState *astate;
    5329       209763 :     MemoryContext arr_context = rcontext;
    5330              : 
    5331              :     /* Make a temporary context to hold all the junk */
    5332       209763 :     if (subcontext)
    5333       138675 :         arr_context = AllocSetContextCreate(rcontext,
    5334              :                                             "accumArrayResult",
    5335              :                                             ALLOCSET_DEFAULT_SIZES);
    5336              : 
    5337              :     astate = (ArrayBuildState *)
    5338       209763 :         MemoryContextAlloc(arr_context, sizeof(ArrayBuildState));
    5339       209763 :     astate->mcontext = arr_context;
    5340       209763 :     astate->private_cxt = subcontext;
    5341       209763 :     astate->alen = initsize;
    5342       209763 :     astate->dvalues = (Datum *)
    5343       209763 :         MemoryContextAlloc(arr_context, astate->alen * sizeof(Datum));
    5344       209763 :     astate->dnulls = (bool *)
    5345       209763 :         MemoryContextAlloc(arr_context, astate->alen * sizeof(bool));
    5346       209763 :     astate->nelems = 0;
    5347       209763 :     astate->element_type = element_type;
    5348       209763 :     get_typlenbyvalalign(element_type,
    5349              :                          &astate->typlen,
    5350              :                          &astate->typbyval,
    5351              :                          &astate->typalign);
    5352              : 
    5353       209763 :     return astate;
    5354              : }
    5355              : 
    5356              : /*
    5357              :  * accumArrayResult - accumulate one (more) Datum for an array result
    5358              :  *
    5359              :  *  astate is working state (can be NULL on first call)
    5360              :  *  dvalue/disnull represent the new Datum to append to the array
    5361              :  *  element_type is the Datum's type (must be a valid array element type)
    5362              :  *  rcontext is where to keep working state
    5363              :  */
    5364              : ArrayBuildState *
    5365      1840454 : accumArrayResult(ArrayBuildState *astate,
    5366              :                  Datum dvalue, bool disnull,
    5367              :                  Oid element_type,
    5368              :                  MemoryContext rcontext)
    5369              : {
    5370              :     MemoryContext oldcontext;
    5371              : 
    5372      1840454 :     if (astate == NULL)
    5373              :     {
    5374              :         /* First time through --- initialize */
    5375       109297 :         astate = initArrayResult(element_type, rcontext, true);
    5376              :     }
    5377              :     else
    5378              :     {
    5379              :         Assert(astate->element_type == element_type);
    5380              :     }
    5381              : 
    5382      1840454 :     oldcontext = MemoryContextSwitchTo(astate->mcontext);
    5383              : 
    5384              :     /* enlarge dvalues[]/dnulls[] if needed */
    5385      1840454 :     if (astate->nelems >= astate->alen)
    5386              :     {
    5387        43741 :         astate->alen *= 2;
    5388              :         /* give an array-related error if we go past MaxAllocSize */
    5389        43741 :         if (!AllocSizeIsValid(astate->alen * sizeof(Datum)))
    5390            0 :             ereport(ERROR,
    5391              :                     (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    5392              :                      errmsg("array size exceeds the maximum allowed (%zu)",
    5393              :                             MaxAllocSize)));
    5394        43741 :         astate->dvalues = (Datum *)
    5395        43741 :             repalloc(astate->dvalues, astate->alen * sizeof(Datum));
    5396        43741 :         astate->dnulls = (bool *)
    5397        43741 :             repalloc(astate->dnulls, astate->alen * sizeof(bool));
    5398              :     }
    5399              : 
    5400              :     /*
    5401              :      * Ensure pass-by-ref stuff is copied into mcontext; and detoast it too if
    5402              :      * it's varlena.  (You might think that detoasting is not needed here
    5403              :      * because construct_md_array can detoast the array elements later.
    5404              :      * However, we must not let construct_md_array modify the ArrayBuildState
    5405              :      * because that would mean array_agg_finalfn damages its input, which is
    5406              :      * verboten.  Also, this way frequently saves one copying step.)
    5407              :      */
    5408      1840454 :     if (!disnull && !astate->typbyval)
    5409              :     {
    5410      1249067 :         if (astate->typlen == -1)
    5411       932962 :             dvalue = PointerGetDatum(PG_DETOAST_DATUM_COPY(dvalue));
    5412              :         else
    5413       316105 :             dvalue = datumCopy(dvalue, astate->typbyval, astate->typlen);
    5414              :     }
    5415              : 
    5416      1840454 :     astate->dvalues[astate->nelems] = dvalue;
    5417      1840454 :     astate->dnulls[astate->nelems] = disnull;
    5418      1840454 :     astate->nelems++;
    5419              : 
    5420      1840454 :     MemoryContextSwitchTo(oldcontext);
    5421              : 
    5422      1840454 :     return astate;
    5423              : }
    5424              : 
    5425              : /*
    5426              :  * makeArrayResult - produce 1-D final result of accumArrayResult
    5427              :  *
    5428              :  * Note: only releases astate if it was initialized within a separate memory
    5429              :  * context (i.e. using subcontext=true when calling initArrayResult).
    5430              :  *
    5431              :  *  astate is working state (must not be NULL)
    5432              :  *  rcontext is where to construct result
    5433              :  */
    5434              : Datum
    5435       109391 : makeArrayResult(ArrayBuildState *astate,
    5436              :                 MemoryContext rcontext)
    5437              : {
    5438              :     int         ndims;
    5439              :     int         dims[1];
    5440              :     int         lbs[1];
    5441              : 
    5442              :     /* If no elements were presented, we want to create an empty array */
    5443       109391 :     ndims = (astate->nelems > 0) ? 1 : 0;
    5444       109391 :     dims[0] = astate->nelems;
    5445       109391 :     lbs[0] = 1;
    5446              : 
    5447       218782 :     return makeMdArrayResult(astate, ndims, dims, lbs, rcontext,
    5448       109391 :                              astate->private_cxt);
    5449              : }
    5450              : 
    5451              : /*
    5452              :  * makeMdArrayResult - produce multi-D final result of accumArrayResult
    5453              :  *
    5454              :  * beware: no check that specified dimensions match the number of values
    5455              :  * accumulated.
    5456              :  *
    5457              :  * Note: if the astate was not initialized within a separate memory context
    5458              :  * (that is, initArrayResult was called with subcontext=false), then using
    5459              :  * release=true is illegal. Instead, release astate along with the rest of its
    5460              :  * context when appropriate.
    5461              :  *
    5462              :  *  astate is working state (must not be NULL)
    5463              :  *  rcontext is where to construct result
    5464              :  *  release is true if okay to release working state
    5465              :  */
    5466              : Datum
    5467       209104 : makeMdArrayResult(ArrayBuildState *astate,
    5468              :                   int ndims,
    5469              :                   int *dims,
    5470              :                   int *lbs,
    5471              :                   MemoryContext rcontext,
    5472              :                   bool release)
    5473              : {
    5474              :     ArrayType  *result;
    5475              :     MemoryContext oldcontext;
    5476              : 
    5477              :     /* Build the final array result in rcontext */
    5478       209104 :     oldcontext = MemoryContextSwitchTo(rcontext);
    5479              : 
    5480       209104 :     result = construct_md_array(astate->dvalues,
    5481              :                                 astate->dnulls,
    5482              :                                 ndims,
    5483              :                                 dims,
    5484              :                                 lbs,
    5485              :                                 astate->element_type,
    5486       209104 :                                 astate->typlen,
    5487       209104 :                                 astate->typbyval,
    5488       209104 :                                 astate->typalign);
    5489              : 
    5490       209104 :     MemoryContextSwitchTo(oldcontext);
    5491              : 
    5492              :     /* Clean up all the junk */
    5493       209104 :     if (release)
    5494              :     {
    5495              :         Assert(astate->private_cxt);
    5496       138373 :         MemoryContextDelete(astate->mcontext);
    5497              :     }
    5498              : 
    5499       209104 :     return PointerGetDatum(result);
    5500              : }
    5501              : 
    5502              : /*
    5503              :  * The following three functions provide essentially the same API as
    5504              :  * initArrayResult/accumArrayResult/makeArrayResult, but instead of accepting
    5505              :  * inputs that are array elements, they accept inputs that are arrays and
    5506              :  * produce an output array having N+1 dimensions.  The inputs must all have
    5507              :  * identical dimensionality as well as element type.
    5508              :  */
    5509              : 
    5510              : /*
    5511              :  * initArrayResultArr - initialize an empty ArrayBuildStateArr
    5512              :  *
    5513              :  *  array_type is the array type (must be a valid varlena array type)
    5514              :  *  element_type is the type of the array's elements (lookup if InvalidOid)
    5515              :  *  rcontext is where to keep working state
    5516              :  *  subcontext is a flag determining whether to use a separate memory context
    5517              :  */
    5518              : ArrayBuildStateArr *
    5519          317 : initArrayResultArr(Oid array_type, Oid element_type, MemoryContext rcontext,
    5520              :                    bool subcontext)
    5521              : {
    5522              :     ArrayBuildStateArr *astate;
    5523          317 :     MemoryContext arr_context = rcontext;   /* by default use the parent ctx */
    5524              : 
    5525              :     /* Lookup element type, unless element_type already provided */
    5526          317 :     if (!OidIsValid(element_type))
    5527              :     {
    5528          257 :         element_type = get_element_type(array_type);
    5529              : 
    5530          257 :         if (!OidIsValid(element_type))
    5531            0 :             ereport(ERROR,
    5532              :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
    5533              :                      errmsg("data type %s is not an array type",
    5534              :                             format_type_be(array_type))));
    5535              :     }
    5536              : 
    5537              :     /* Make a temporary context to hold all the junk */
    5538          317 :     if (subcontext)
    5539           24 :         arr_context = AllocSetContextCreate(rcontext,
    5540              :                                             "accumArrayResultArr",
    5541              :                                             ALLOCSET_DEFAULT_SIZES);
    5542              : 
    5543              :     /* Note we initialize all fields to zero */
    5544              :     astate = (ArrayBuildStateArr *)
    5545          317 :         MemoryContextAllocZero(arr_context, sizeof(ArrayBuildStateArr));
    5546          317 :     astate->mcontext = arr_context;
    5547          317 :     astate->private_cxt = subcontext;
    5548              : 
    5549              :     /* Save relevant datatype information */
    5550          317 :     astate->array_type = array_type;
    5551          317 :     astate->element_type = element_type;
    5552              : 
    5553          317 :     return astate;
    5554              : }
    5555              : 
    5556              : /*
    5557              :  * accumArrayResultArr - accumulate one (more) sub-array for an array result
    5558              :  *
    5559              :  *  astate is working state (can be NULL on first call)
    5560              :  *  dvalue/disnull represent the new sub-array to append to the array
    5561              :  *  array_type is the array type (must be a valid varlena array type)
    5562              :  *  rcontext is where to keep working state
    5563              :  */
    5564              : ArrayBuildStateArr *
    5565        33572 : accumArrayResultArr(ArrayBuildStateArr *astate,
    5566              :                     Datum dvalue, bool disnull,
    5567              :                     Oid array_type,
    5568              :                     MemoryContext rcontext)
    5569              : {
    5570              :     ArrayType  *arg;
    5571              :     MemoryContext oldcontext;
    5572              :     int        *dims,
    5573              :                *lbs,
    5574              :                 ndims,
    5575              :                 nitems,
    5576              :                 ndatabytes;
    5577              :     char       *data;
    5578              :     int         i;
    5579              : 
    5580              :     /*
    5581              :      * We disallow accumulating null subarrays.  Another plausible definition
    5582              :      * is to ignore them, but callers that want that can just skip calling
    5583              :      * this function.
    5584              :      */
    5585        33572 :     if (disnull)
    5586            3 :         ereport(ERROR,
    5587              :                 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
    5588              :                  errmsg("cannot accumulate null arrays")));
    5589              : 
    5590              :     /* Detoast input array in caller's context */
    5591        33569 :     arg = DatumGetArrayTypeP(dvalue);
    5592              : 
    5593        33569 :     if (astate == NULL)
    5594            0 :         astate = initArrayResultArr(array_type, InvalidOid, rcontext, true);
    5595              :     else
    5596              :         Assert(astate->array_type == array_type);
    5597              : 
    5598        33569 :     oldcontext = MemoryContextSwitchTo(astate->mcontext);
    5599              : 
    5600              :     /* Collect this input's dimensions */
    5601        33569 :     ndims = ARR_NDIM(arg);
    5602        33569 :     dims = ARR_DIMS(arg);
    5603        33569 :     lbs = ARR_LBOUND(arg);
    5604        33569 :     data = ARR_DATA_PTR(arg);
    5605        33569 :     nitems = ArrayGetNItems(ndims, dims);
    5606        33569 :     ndatabytes = ARR_SIZE(arg) - ARR_DATA_OFFSET(arg);
    5607              : 
    5608        33569 :     if (astate->ndims == 0)
    5609              :     {
    5610              :         /* First input; check/save the dimensionality info */
    5611              : 
    5612              :         /* Should we allow empty inputs and just produce an empty output? */
    5613          224 :         if (ndims == 0)
    5614            3 :             ereport(ERROR,
    5615              :                     (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    5616              :                      errmsg("cannot accumulate empty arrays")));
    5617          221 :         if (ndims + 1 > MAXDIM)
    5618            0 :             ereport(ERROR,
    5619              :                     (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    5620              :                      errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
    5621              :                             ndims + 1, MAXDIM)));
    5622              : 
    5623              :         /*
    5624              :          * The output array will have n+1 dimensions, with the ones after the
    5625              :          * first matching the input's dimensions.
    5626              :          */
    5627          221 :         astate->ndims = ndims + 1;
    5628          221 :         astate->dims[0] = 0;
    5629          221 :         memcpy(&astate->dims[1], dims, ndims * sizeof(int));
    5630          221 :         astate->lbs[0] = 1;
    5631          221 :         memcpy(&astate->lbs[1], lbs, ndims * sizeof(int));
    5632              : 
    5633              :         /* Allocate at least enough data space for this item */
    5634          221 :         astate->abytes = pg_nextpower2_32(Max(1024, ndatabytes + 1));
    5635          221 :         astate->data = (char *) palloc(astate->abytes);
    5636              :     }
    5637              :     else
    5638              :     {
    5639              :         /* Second or later input: must match first input's dimensionality */
    5640        33345 :         if (astate->ndims != ndims + 1)
    5641            0 :             ereport(ERROR,
    5642              :                     (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    5643              :                      errmsg("cannot accumulate arrays of different dimensionality")));
    5644        66687 :         for (i = 0; i < ndims; i++)
    5645              :         {
    5646        33345 :             if (astate->dims[i + 1] != dims[i] || astate->lbs[i + 1] != lbs[i])
    5647            3 :                 ereport(ERROR,
    5648              :                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    5649              :                          errmsg("cannot accumulate arrays of different dimensionality")));
    5650              :         }
    5651              : 
    5652              :         /* Enlarge data space if needed */
    5653        33342 :         if (astate->nbytes + ndatabytes > astate->abytes)
    5654              :         {
    5655           66 :             astate->abytes = Max(astate->abytes * 2,
    5656              :                                  astate->nbytes + ndatabytes);
    5657           66 :             astate->data = (char *) repalloc(astate->data, astate->abytes);
    5658              :         }
    5659              :     }
    5660              : 
    5661              :     /*
    5662              :      * Copy the data portion of the sub-array.  Note we assume that the
    5663              :      * advertised data length of the sub-array is properly aligned.  We do not
    5664              :      * have to worry about detoasting elements since whatever's in the
    5665              :      * sub-array should be OK already.
    5666              :      */
    5667        33563 :     memcpy(astate->data + astate->nbytes, data, ndatabytes);
    5668        33563 :     astate->nbytes += ndatabytes;
    5669              : 
    5670              :     /* Deal with null bitmap if needed */
    5671        33563 :     if (astate->nullbitmap || ARR_HASNULL(arg))
    5672              :     {
    5673        15068 :         int         newnitems = astate->nitems + nitems;
    5674              : 
    5675        15068 :         if (astate->nullbitmap == NULL)
    5676              :         {
    5677              :             /*
    5678              :              * First input with nulls; we must retrospectively handle any
    5679              :              * previous inputs by marking all their items non-null.
    5680              :              */
    5681           70 :             astate->aitems = pg_nextpower2_32(Max(256, newnitems + 1));
    5682           70 :             astate->nullbitmap = (bits8 *) palloc((astate->aitems + 7) / 8);
    5683           70 :             array_bitmap_copy(astate->nullbitmap, 0,
    5684              :                               NULL, 0,
    5685              :                               astate->nitems);
    5686              :         }
    5687        14998 :         else if (newnitems > astate->aitems)
    5688              :         {
    5689           30 :             astate->aitems = Max(astate->aitems * 2, newnitems);
    5690           30 :             astate->nullbitmap = (bits8 *)
    5691           30 :                 repalloc(astate->nullbitmap, (astate->aitems + 7) / 8);
    5692              :         }
    5693        15068 :         array_bitmap_copy(astate->nullbitmap, astate->nitems,
    5694        15068 :                           ARR_NULLBITMAP(arg), 0,
    5695              :                           nitems);
    5696              :     }
    5697              : 
    5698        33563 :     astate->nitems += nitems;
    5699        33563 :     astate->dims[0] += 1;
    5700              : 
    5701        33563 :     MemoryContextSwitchTo(oldcontext);
    5702              : 
    5703              :     /* Release detoasted copy if any */
    5704        33563 :     if (arg != DatumGetPointer(dvalue))
    5705         3404 :         pfree(arg);
    5706              : 
    5707        33563 :     return astate;
    5708              : }
    5709              : 
    5710              : /*
    5711              :  * makeArrayResultArr - produce N+1-D final result of accumArrayResultArr
    5712              :  *
    5713              :  *  astate is working state (must not be NULL)
    5714              :  *  rcontext is where to construct result
    5715              :  *  release is true if okay to release working state
    5716              :  */
    5717              : Datum
    5718          188 : makeArrayResultArr(ArrayBuildStateArr *astate,
    5719              :                    MemoryContext rcontext,
    5720              :                    bool release)
    5721              : {
    5722              :     ArrayType  *result;
    5723              :     MemoryContext oldcontext;
    5724              : 
    5725              :     /* Build the final array result in rcontext */
    5726          188 :     oldcontext = MemoryContextSwitchTo(rcontext);
    5727              : 
    5728          188 :     if (astate->ndims == 0)
    5729              :     {
    5730              :         /* No inputs, return empty array */
    5731            0 :         result = construct_empty_array(astate->element_type);
    5732              :     }
    5733              :     else
    5734              :     {
    5735              :         int         dataoffset,
    5736              :                     nbytes;
    5737              : 
    5738              :         /* Check for overflow of the array dimensions */
    5739          188 :         (void) ArrayGetNItems(astate->ndims, astate->dims);
    5740          188 :         ArrayCheckBounds(astate->ndims, astate->dims, astate->lbs);
    5741              : 
    5742              :         /* Compute required space */
    5743          188 :         nbytes = astate->nbytes;
    5744          188 :         if (astate->nullbitmap != NULL)
    5745              :         {
    5746           55 :             dataoffset = ARR_OVERHEAD_WITHNULLS(astate->ndims, astate->nitems);
    5747           55 :             nbytes += dataoffset;
    5748              :         }
    5749              :         else
    5750              :         {
    5751          133 :             dataoffset = 0;
    5752          133 :             nbytes += ARR_OVERHEAD_NONULLS(astate->ndims);
    5753              :         }
    5754              : 
    5755          188 :         result = (ArrayType *) palloc0(nbytes);
    5756          188 :         SET_VARSIZE(result, nbytes);
    5757          188 :         result->ndim = astate->ndims;
    5758          188 :         result->dataoffset = dataoffset;
    5759          188 :         result->elemtype = astate->element_type;
    5760              : 
    5761          188 :         memcpy(ARR_DIMS(result), astate->dims, astate->ndims * sizeof(int));
    5762          188 :         memcpy(ARR_LBOUND(result), astate->lbs, astate->ndims * sizeof(int));
    5763          188 :         memcpy(ARR_DATA_PTR(result), astate->data, astate->nbytes);
    5764              : 
    5765          188 :         if (astate->nullbitmap != NULL)
    5766           55 :             array_bitmap_copy(ARR_NULLBITMAP(result), 0,
    5767           55 :                               astate->nullbitmap, 0,
    5768              :                               astate->nitems);
    5769              :     }
    5770              : 
    5771          188 :     MemoryContextSwitchTo(oldcontext);
    5772              : 
    5773              :     /* Clean up all the junk */
    5774          188 :     if (release)
    5775              :     {
    5776              :         Assert(astate->private_cxt);
    5777           24 :         MemoryContextDelete(astate->mcontext);
    5778              :     }
    5779              : 
    5780          188 :     return PointerGetDatum(result);
    5781              : }
    5782              : 
    5783              : /*
    5784              :  * The following three functions provide essentially the same API as
    5785              :  * initArrayResult/accumArrayResult/makeArrayResult, but can accept either
    5786              :  * scalar or array inputs, invoking the appropriate set of functions above.
    5787              :  */
    5788              : 
    5789              : /*
    5790              :  * initArrayResultAny - initialize an empty ArrayBuildStateAny
    5791              :  *
    5792              :  *  input_type is the input datatype (either element or array type)
    5793              :  *  rcontext is where to keep working state
    5794              :  *  subcontext is a flag determining whether to use a separate memory context
    5795              :  */
    5796              : ArrayBuildStateAny *
    5797        28185 : initArrayResultAny(Oid input_type, MemoryContext rcontext, bool subcontext)
    5798              : {
    5799              :     ArrayBuildStateAny *astate;
    5800              : 
    5801              :     /*
    5802              :      * int2vector and oidvector will satisfy both get_element_type and
    5803              :      * get_array_type.  We prefer to treat them as scalars, to be consistent
    5804              :      * with get_promoted_array_type.  Hence, check get_array_type not
    5805              :      * get_element_type.
    5806              :      */
    5807        28185 :     if (!OidIsValid(get_array_type(input_type)))
    5808              :     {
    5809              :         /* Array case */
    5810              :         ArrayBuildStateArr *arraystate;
    5811              : 
    5812           24 :         arraystate = initArrayResultArr(input_type, InvalidOid, rcontext, subcontext);
    5813              :         astate = (ArrayBuildStateAny *)
    5814           24 :             MemoryContextAlloc(arraystate->mcontext,
    5815              :                                sizeof(ArrayBuildStateAny));
    5816           24 :         astate->scalarstate = NULL;
    5817           24 :         astate->arraystate = arraystate;
    5818              :     }
    5819              :     else
    5820              :     {
    5821              :         /* Scalar case */
    5822              :         ArrayBuildState *scalarstate;
    5823              : 
    5824        28161 :         scalarstate = initArrayResult(input_type, rcontext, subcontext);
    5825              :         astate = (ArrayBuildStateAny *)
    5826        28161 :             MemoryContextAlloc(scalarstate->mcontext,
    5827              :                                sizeof(ArrayBuildStateAny));
    5828        28161 :         astate->scalarstate = scalarstate;
    5829        28161 :         astate->arraystate = NULL;
    5830              :     }
    5831              : 
    5832        28185 :     return astate;
    5833              : }
    5834              : 
    5835              : /*
    5836              :  * accumArrayResultAny - accumulate one (more) input for an array result
    5837              :  *
    5838              :  *  astate is working state (can be NULL on first call)
    5839              :  *  dvalue/disnull represent the new input to append to the array
    5840              :  *  input_type is the input datatype (either element or array type)
    5841              :  *  rcontext is where to keep working state
    5842              :  */
    5843              : ArrayBuildStateAny *
    5844         8826 : accumArrayResultAny(ArrayBuildStateAny *astate,
    5845              :                     Datum dvalue, bool disnull,
    5846              :                     Oid input_type,
    5847              :                     MemoryContext rcontext)
    5848              : {
    5849         8826 :     if (astate == NULL)
    5850           60 :         astate = initArrayResultAny(input_type, rcontext, true);
    5851              : 
    5852         8826 :     if (astate->scalarstate)
    5853         8757 :         (void) accumArrayResult(astate->scalarstate,
    5854              :                                 dvalue, disnull,
    5855              :                                 input_type, rcontext);
    5856              :     else
    5857           69 :         (void) accumArrayResultArr(astate->arraystate,
    5858              :                                    dvalue, disnull,
    5859              :                                    input_type, rcontext);
    5860              : 
    5861         8826 :     return astate;
    5862              : }
    5863              : 
    5864              : /*
    5865              :  * makeArrayResultAny - produce final result of accumArrayResultAny
    5866              :  *
    5867              :  *  astate is working state (must not be NULL)
    5868              :  *  rcontext is where to construct result
    5869              :  *  release is true if okay to release working state
    5870              :  */
    5871              : Datum
    5872        28185 : makeArrayResultAny(ArrayBuildStateAny *astate,
    5873              :                    MemoryContext rcontext, bool release)
    5874              : {
    5875              :     Datum       result;
    5876              : 
    5877        28185 :     if (astate->scalarstate)
    5878              :     {
    5879              :         /* Must use makeMdArrayResult to support "release" parameter */
    5880              :         int         ndims;
    5881              :         int         dims[1];
    5882              :         int         lbs[1];
    5883              : 
    5884              :         /* If no elements were presented, we want to create an empty array */
    5885        28161 :         ndims = (astate->scalarstate->nelems > 0) ? 1 : 0;
    5886        28161 :         dims[0] = astate->scalarstate->nelems;
    5887        28161 :         lbs[0] = 1;
    5888              : 
    5889        28161 :         result = makeMdArrayResult(astate->scalarstate, ndims, dims, lbs,
    5890              :                                    rcontext, release);
    5891              :     }
    5892              :     else
    5893              :     {
    5894           24 :         result = makeArrayResultArr(astate->arraystate,
    5895              :                                     rcontext, release);
    5896              :     }
    5897        28185 :     return result;
    5898              : }
    5899              : 
    5900              : 
    5901              : Datum
    5902          144 : array_larger(PG_FUNCTION_ARGS)
    5903              : {
    5904          144 :     if (array_cmp(fcinfo) > 0)
    5905           72 :         PG_RETURN_DATUM(PG_GETARG_DATUM(0));
    5906              :     else
    5907           69 :         PG_RETURN_DATUM(PG_GETARG_DATUM(1));
    5908              : }
    5909              : 
    5910              : Datum
    5911          129 : array_smaller(PG_FUNCTION_ARGS)
    5912              : {
    5913          129 :     if (array_cmp(fcinfo) < 0)
    5914           87 :         PG_RETURN_DATUM(PG_GETARG_DATUM(0));
    5915              :     else
    5916           42 :         PG_RETURN_DATUM(PG_GETARG_DATUM(1));
    5917              : }
    5918              : 
    5919              : 
    5920              : typedef struct generate_subscripts_fctx
    5921              : {
    5922              :     int32       lower;
    5923              :     int32       upper;
    5924              :     bool        reverse;
    5925              : } generate_subscripts_fctx;
    5926              : 
    5927              : /*
    5928              :  * generate_subscripts(array anyarray, dim int [, reverse bool])
    5929              :  *      Returns all subscripts of the array for any dimension
    5930              :  */
    5931              : Datum
    5932         2453 : generate_subscripts(PG_FUNCTION_ARGS)
    5933              : {
    5934              :     FuncCallContext *funcctx;
    5935              :     MemoryContext oldcontext;
    5936              :     generate_subscripts_fctx *fctx;
    5937              : 
    5938              :     /* stuff done only on the first call of the function */
    5939         2453 :     if (SRF_IS_FIRSTCALL())
    5940              :     {
    5941         1144 :         AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
    5942         1144 :         int         reqdim = PG_GETARG_INT32(1);
    5943              :         int        *lb,
    5944              :                    *dimv;
    5945              : 
    5946              :         /* create a function context for cross-call persistence */
    5947         1144 :         funcctx = SRF_FIRSTCALL_INIT();
    5948              : 
    5949              :         /* Sanity check: does it look like an array at all? */
    5950         1144 :         if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
    5951            3 :             SRF_RETURN_DONE(funcctx);
    5952              : 
    5953              :         /* Sanity check: was the requested dim valid */
    5954         1141 :         if (reqdim <= 0 || reqdim > AARR_NDIM(v))
    5955            0 :             SRF_RETURN_DONE(funcctx);
    5956              : 
    5957              :         /*
    5958              :          * switch to memory context appropriate for multiple function calls
    5959              :          */
    5960         1141 :         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
    5961         1141 :         fctx = palloc_object(generate_subscripts_fctx);
    5962              : 
    5963         1141 :         lb = AARR_LBOUND(v);
    5964         1141 :         dimv = AARR_DIMS(v);
    5965              : 
    5966         1141 :         fctx->lower = lb[reqdim - 1];
    5967         1141 :         fctx->upper = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
    5968         1141 :         fctx->reverse = (PG_NARGS() < 3) ? false : PG_GETARG_BOOL(2);
    5969              : 
    5970         1141 :         funcctx->user_fctx = fctx;
    5971              : 
    5972         1141 :         MemoryContextSwitchTo(oldcontext);
    5973              :     }
    5974              : 
    5975         2450 :     funcctx = SRF_PERCALL_SETUP();
    5976              : 
    5977         2450 :     fctx = funcctx->user_fctx;
    5978              : 
    5979         2450 :     if (fctx->lower <= fctx->upper)
    5980              :     {
    5981         1309 :         if (!fctx->reverse)
    5982         1309 :             SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->lower++));
    5983              :         else
    5984            0 :             SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->upper--));
    5985              :     }
    5986              :     else
    5987              :         /* done when there are no more elements left */
    5988         1141 :         SRF_RETURN_DONE(funcctx);
    5989              : }
    5990              : 
    5991              : /*
    5992              :  * generate_subscripts_nodir
    5993              :  *      Implements the 2-argument version of generate_subscripts
    5994              :  */
    5995              : Datum
    5996         2453 : generate_subscripts_nodir(PG_FUNCTION_ARGS)
    5997              : {
    5998              :     /* just call the other one -- it can handle both cases */
    5999         2453 :     return generate_subscripts(fcinfo);
    6000              : }
    6001              : 
    6002              : /*
    6003              :  * array_fill_with_lower_bounds
    6004              :  *      Create and fill array with defined lower bounds.
    6005              :  */
    6006              : Datum
    6007           33 : array_fill_with_lower_bounds(PG_FUNCTION_ARGS)
    6008              : {
    6009              :     ArrayType  *dims;
    6010              :     ArrayType  *lbs;
    6011              :     ArrayType  *result;
    6012              :     Oid         elmtype;
    6013              :     Datum       value;
    6014              :     bool        isnull;
    6015              : 
    6016           33 :     if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
    6017            6 :         ereport(ERROR,
    6018              :                 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
    6019              :                  errmsg("dimension array or low bound array cannot be null")));
    6020              : 
    6021           27 :     dims = PG_GETARG_ARRAYTYPE_P(1);
    6022           27 :     lbs = PG_GETARG_ARRAYTYPE_P(2);
    6023              : 
    6024           27 :     if (!PG_ARGISNULL(0))
    6025              :     {
    6026           21 :         value = PG_GETARG_DATUM(0);
    6027           21 :         isnull = false;
    6028              :     }
    6029              :     else
    6030              :     {
    6031            6 :         value = 0;
    6032            6 :         isnull = true;
    6033              :     }
    6034              : 
    6035           27 :     elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
    6036           27 :     if (!OidIsValid(elmtype))
    6037            0 :         elog(ERROR, "could not determine data type of input");
    6038              : 
    6039           27 :     result = array_fill_internal(dims, lbs, value, isnull, elmtype, fcinfo);
    6040           21 :     PG_RETURN_ARRAYTYPE_P(result);
    6041              : }
    6042              : 
    6043              : /*
    6044              :  * array_fill
    6045              :  *      Create and fill array with default lower bounds.
    6046              :  */
    6047              : Datum
    6048           45 : array_fill(PG_FUNCTION_ARGS)
    6049              : {
    6050              :     ArrayType  *dims;
    6051              :     ArrayType  *result;
    6052              :     Oid         elmtype;
    6053              :     Datum       value;
    6054              :     bool        isnull;
    6055              : 
    6056           45 :     if (PG_ARGISNULL(1))
    6057            0 :         ereport(ERROR,
    6058              :                 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
    6059              :                  errmsg("dimension array or low bound array cannot be null")));
    6060              : 
    6061           45 :     dims = PG_GETARG_ARRAYTYPE_P(1);
    6062              : 
    6063           45 :     if (!PG_ARGISNULL(0))
    6064              :     {
    6065           39 :         value = PG_GETARG_DATUM(0);
    6066           39 :         isnull = false;
    6067              :     }
    6068              :     else
    6069              :     {
    6070            6 :         value = 0;
    6071            6 :         isnull = true;
    6072              :     }
    6073              : 
    6074           45 :     elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
    6075           45 :     if (!OidIsValid(elmtype))
    6076            0 :         elog(ERROR, "could not determine data type of input");
    6077              : 
    6078           45 :     result = array_fill_internal(dims, NULL, value, isnull, elmtype, fcinfo);
    6079           39 :     PG_RETURN_ARRAYTYPE_P(result);
    6080              : }
    6081              : 
    6082              : static ArrayType *
    6083           33 : create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
    6084              :                       Oid elmtype, int dataoffset)
    6085              : {
    6086              :     ArrayType  *result;
    6087              : 
    6088           33 :     result = (ArrayType *) palloc0(nbytes);
    6089           33 :     SET_VARSIZE(result, nbytes);
    6090           33 :     result->ndim = ndims;
    6091           33 :     result->dataoffset = dataoffset;
    6092           33 :     result->elemtype = elmtype;
    6093           33 :     memcpy(ARR_DIMS(result), dimv, ndims * sizeof(int));
    6094           33 :     memcpy(ARR_LBOUND(result), lbsv, ndims * sizeof(int));
    6095              : 
    6096           33 :     return result;
    6097              : }
    6098              : 
    6099              : static ArrayType *
    6100           72 : array_fill_internal(ArrayType *dims, ArrayType *lbs,
    6101              :                     Datum value, bool isnull, Oid elmtype,
    6102              :                     FunctionCallInfo fcinfo)
    6103              : {
    6104              :     ArrayType  *result;
    6105              :     int        *dimv;
    6106              :     int        *lbsv;
    6107              :     int         ndims;
    6108              :     int         nitems;
    6109              :     int         deflbs[MAXDIM];
    6110              :     int16       elmlen;
    6111              :     bool        elmbyval;
    6112              :     char        elmalign;
    6113              :     uint8       elmalignby;
    6114              :     ArrayMetaState *my_extra;
    6115              : 
    6116              :     /*
    6117              :      * Params checks
    6118              :      */
    6119           72 :     if (ARR_NDIM(dims) > 1)
    6120            3 :         ereport(ERROR,
    6121              :                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    6122              :                  errmsg("wrong number of array subscripts"),
    6123              :                  errdetail("Dimension array must be one dimensional.")));
    6124              : 
    6125           69 :     if (array_contains_nulls(dims))
    6126            3 :         ereport(ERROR,
    6127              :                 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
    6128              :                  errmsg("dimension values cannot be null")));
    6129              : 
    6130           66 :     dimv = (int *) ARR_DATA_PTR(dims);
    6131           66 :     ndims = (ARR_NDIM(dims) > 0) ? ARR_DIMS(dims)[0] : 0;
    6132              : 
    6133           66 :     if (ndims < 0)               /* we do allow zero-dimension arrays */
    6134            0 :         ereport(ERROR,
    6135              :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    6136              :                  errmsg("invalid number of dimensions: %d", ndims)));
    6137           66 :     if (ndims > MAXDIM)
    6138            0 :         ereport(ERROR,
    6139              :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    6140              :                  errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
    6141              :                         ndims, MAXDIM)));
    6142              : 
    6143           66 :     if (lbs != NULL)
    6144              :     {
    6145           27 :         if (ARR_NDIM(lbs) > 1)
    6146            0 :             ereport(ERROR,
    6147              :                     (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    6148              :                      errmsg("wrong number of array subscripts"),
    6149              :                      errdetail("Dimension array must be one dimensional.")));
    6150              : 
    6151           27 :         if (array_contains_nulls(lbs))
    6152            0 :             ereport(ERROR,
    6153              :                     (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
    6154              :                      errmsg("dimension values cannot be null")));
    6155              : 
    6156           27 :         if (ndims != ((ARR_NDIM(lbs) > 0) ? ARR_DIMS(lbs)[0] : 0))
    6157            6 :             ereport(ERROR,
    6158              :                     (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    6159              :                      errmsg("wrong number of array subscripts"),
    6160              :                      errdetail("Low bound array has different size than dimensions array.")));
    6161              : 
    6162           21 :         lbsv = (int *) ARR_DATA_PTR(lbs);
    6163              :     }
    6164              :     else
    6165              :     {
    6166              :         int         i;
    6167              : 
    6168          273 :         for (i = 0; i < MAXDIM; i++)
    6169          234 :             deflbs[i] = 1;
    6170              : 
    6171           39 :         lbsv = deflbs;
    6172              :     }
    6173              : 
    6174              :     /* This checks for overflow of the array dimensions */
    6175           60 :     nitems = ArrayGetNItems(ndims, dimv);
    6176           60 :     ArrayCheckBounds(ndims, dimv, lbsv);
    6177              : 
    6178              :     /* fast track for empty array */
    6179           60 :     if (nitems <= 0)
    6180           27 :         return construct_empty_array(elmtype);
    6181              : 
    6182              :     /*
    6183              :      * We arrange to look up info about element type only once per series of
    6184              :      * calls, assuming the element type doesn't change underneath us.
    6185              :      */
    6186           33 :     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
    6187           33 :     if (my_extra == NULL)
    6188              :     {
    6189           33 :         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
    6190              :                                                       sizeof(ArrayMetaState));
    6191           33 :         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
    6192           33 :         my_extra->element_type = InvalidOid;
    6193              :     }
    6194              : 
    6195           33 :     if (my_extra->element_type != elmtype)
    6196              :     {
    6197              :         /* Get info about element type */
    6198           33 :         get_typlenbyvalalign(elmtype,
    6199              :                              &my_extra->typlen,
    6200              :                              &my_extra->typbyval,
    6201              :                              &my_extra->typalign);
    6202           33 :         my_extra->element_type = elmtype;
    6203              :     }
    6204              : 
    6205           33 :     elmlen = my_extra->typlen;
    6206           33 :     elmbyval = my_extra->typbyval;
    6207           33 :     elmalign = my_extra->typalign;
    6208           33 :     elmalignby = typalign_to_alignby(elmalign);
    6209              : 
    6210              :     /* compute required space */
    6211           33 :     if (!isnull)
    6212              :     {
    6213              :         int         i;
    6214              :         char       *p;
    6215              :         int         nbytes;
    6216              :         int         totbytes;
    6217              : 
    6218              :         /* make sure data is not toasted */
    6219           21 :         if (elmlen == -1)
    6220            6 :             value = PointerGetDatum(PG_DETOAST_DATUM(value));
    6221              : 
    6222           21 :         nbytes = att_addlength_datum(0, elmlen, value);
    6223           21 :         nbytes = att_nominal_alignby(nbytes, elmalignby);
    6224              :         Assert(nbytes > 0);
    6225              : 
    6226           21 :         totbytes = nbytes * nitems;
    6227              : 
    6228              :         /* check for overflow of multiplication or total request */
    6229           21 :         if (totbytes / nbytes != nitems ||
    6230           21 :             !AllocSizeIsValid(totbytes))
    6231            0 :             ereport(ERROR,
    6232              :                     (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    6233              :                      errmsg("array size exceeds the maximum allowed (%zu)",
    6234              :                             MaxAllocSize)));
    6235              : 
    6236              :         /*
    6237              :          * This addition can't overflow, but it might cause us to go past
    6238              :          * MaxAllocSize.  We leave it to palloc to complain in that case.
    6239              :          */
    6240           21 :         totbytes += ARR_OVERHEAD_NONULLS(ndims);
    6241              : 
    6242           21 :         result = create_array_envelope(ndims, dimv, lbsv, totbytes,
    6243              :                                        elmtype, 0);
    6244              : 
    6245           21 :         p = ARR_DATA_PTR(result);
    6246      1500153 :         for (i = 0; i < nitems; i++)
    6247      1500132 :             p += ArrayCastAndSet(value, elmlen, elmbyval, elmalignby, p);
    6248              :     }
    6249              :     else
    6250              :     {
    6251              :         int         nbytes;
    6252              :         int         dataoffset;
    6253              : 
    6254           12 :         dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
    6255           12 :         nbytes = dataoffset;
    6256              : 
    6257           12 :         result = create_array_envelope(ndims, dimv, lbsv, nbytes,
    6258              :                                        elmtype, dataoffset);
    6259              : 
    6260              :         /* create_array_envelope already zeroed the bitmap, so we're done */
    6261              :     }
    6262              : 
    6263           33 :     return result;
    6264              : }
    6265              : 
    6266              : 
    6267              : /*
    6268              :  * UNNEST
    6269              :  */
    6270              : Datum
    6271       270367 : array_unnest(PG_FUNCTION_ARGS)
    6272              : {
    6273              :     typedef struct
    6274              :     {
    6275              :         array_iter  iter;
    6276              :         int         nextelem;
    6277              :         int         numelems;
    6278              :     } array_unnest_fctx;
    6279              : 
    6280              :     FuncCallContext *funcctx;
    6281              :     array_unnest_fctx *fctx;
    6282              :     MemoryContext oldcontext;
    6283              : 
    6284              :     /* stuff done only on the first call of the function */
    6285       270367 :     if (SRF_IS_FIRSTCALL())
    6286              :     {
    6287              :         AnyArrayType *arr;
    6288              :         int16       elmlen;
    6289              :         bool        elmbyval;
    6290              :         char        elmalign;
    6291              : 
    6292              :         /* create a function context for cross-call persistence */
    6293        44172 :         funcctx = SRF_FIRSTCALL_INIT();
    6294              : 
    6295              :         /*
    6296              :          * switch to memory context appropriate for multiple function calls
    6297              :          */
    6298        44172 :         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
    6299              : 
    6300              :         /*
    6301              :          * Get the array value and detoast if needed.  We can't do this
    6302              :          * earlier because if we have to detoast, we want the detoasted copy
    6303              :          * to be in multi_call_memory_ctx, so it will go away when we're done
    6304              :          * and not before.  (If no detoast happens, we assume the originally
    6305              :          * passed array will stick around till then.)
    6306              :          */
    6307        44172 :         arr = PG_GETARG_ANY_ARRAY_P(0);
    6308              : 
    6309              :         /* allocate memory for user context */
    6310        44172 :         fctx = palloc_object(array_unnest_fctx);
    6311              : 
    6312              :         /* get element-type data */
    6313        44172 :         if (VARATT_IS_EXPANDED_HEADER(arr))
    6314              :         {
    6315              :             /* we can just grab the type data from expanded array */
    6316            1 :             elmlen = arr->xpn.typlen;
    6317            1 :             elmbyval = arr->xpn.typbyval;
    6318            1 :             elmalign = arr->xpn.typalign;
    6319              :         }
    6320              :         else
    6321        44171 :             get_typlenbyvalalign(AARR_ELEMTYPE(arr),
    6322              :                                  &elmlen,
    6323              :                                  &elmbyval,
    6324              :                                  &elmalign);
    6325              : 
    6326              :         /* initialize state */
    6327        44172 :         array_iter_setup(&fctx->iter, arr, elmlen, elmbyval, elmalign);
    6328        44172 :         fctx->nextelem = 0;
    6329        44172 :         fctx->numelems = ArrayGetNItems(AARR_NDIM(arr), AARR_DIMS(arr));
    6330              : 
    6331        44172 :         funcctx->user_fctx = fctx;
    6332        44172 :         MemoryContextSwitchTo(oldcontext);
    6333              :     }
    6334              : 
    6335              :     /* stuff done on every call of the function */
    6336       270367 :     funcctx = SRF_PERCALL_SETUP();
    6337       270367 :     fctx = funcctx->user_fctx;
    6338              : 
    6339       270367 :     if (fctx->nextelem < fctx->numelems)
    6340              :     {
    6341       226195 :         int         offset = fctx->nextelem++;
    6342              :         Datum       elem;
    6343              : 
    6344       226195 :         elem = array_iter_next(&fctx->iter, &fcinfo->isnull, offset);
    6345              : 
    6346       226195 :         SRF_RETURN_NEXT(funcctx, elem);
    6347              :     }
    6348              :     else
    6349              :     {
    6350              :         /* do when there is no more left */
    6351        44172 :         SRF_RETURN_DONE(funcctx);
    6352              :     }
    6353              : }
    6354              : 
    6355              : /*
    6356              :  * Planner support function for array_unnest(anyarray)
    6357              :  *
    6358              :  * Note: this is now also used for information_schema._pg_expandarray(),
    6359              :  * which is simply a wrapper around array_unnest().
    6360              :  */
    6361              : Datum
    6362        14291 : array_unnest_support(PG_FUNCTION_ARGS)
    6363              : {
    6364        14291 :     Node       *rawreq = (Node *) PG_GETARG_POINTER(0);
    6365        14291 :     Node       *ret = NULL;
    6366              : 
    6367        14291 :     if (IsA(rawreq, SupportRequestRows))
    6368              :     {
    6369              :         /* Try to estimate the number of rows returned */
    6370         3861 :         SupportRequestRows *req = (SupportRequestRows *) rawreq;
    6371              : 
    6372         3861 :         if (is_funcclause(req->node))    /* be paranoid */
    6373              :         {
    6374         3858 :             List       *args = ((FuncExpr *) req->node)->args;
    6375              :             Node       *arg1;
    6376              : 
    6377              :             /* We can use estimated argument values here */
    6378         3858 :             arg1 = estimate_expression_value(req->root, linitial(args));
    6379              : 
    6380         3858 :             req->rows = estimate_array_length(req->root, arg1);
    6381         3858 :             ret = (Node *) req;
    6382              :         }
    6383              :     }
    6384              : 
    6385        14291 :     PG_RETURN_POINTER(ret);
    6386              : }
    6387              : 
    6388              : 
    6389              : /*
    6390              :  * array_replace/array_remove support
    6391              :  *
    6392              :  * Find all array entries matching (not distinct from) search/search_isnull,
    6393              :  * and delete them if remove is true, else replace them with
    6394              :  * replace/replace_isnull.  Comparisons are done using the specified
    6395              :  * collation.  fcinfo is passed only for caching purposes.
    6396              :  */
    6397              : static ArrayType *
    6398         2117 : array_replace_internal(ArrayType *array,
    6399              :                        Datum search, bool search_isnull,
    6400              :                        Datum replace, bool replace_isnull,
    6401              :                        bool remove, Oid collation,
    6402              :                        FunctionCallInfo fcinfo)
    6403              : {
    6404         2117 :     LOCAL_FCINFO(locfcinfo, 2);
    6405              :     ArrayType  *result;
    6406              :     Oid         element_type;
    6407              :     Datum      *values;
    6408              :     bool       *nulls;
    6409              :     int        *dim;
    6410              :     int         ndim;
    6411              :     int         nitems,
    6412              :                 nresult;
    6413              :     int         i;
    6414         2117 :     int32       nbytes = 0;
    6415              :     int32       dataoffset;
    6416              :     bool        hasnulls;
    6417              :     int         typlen;
    6418              :     bool        typbyval;
    6419              :     char        typalign;
    6420              :     uint8       typalignby;
    6421              :     char       *arraydataptr;
    6422              :     bits8      *bitmap;
    6423              :     int         bitmask;
    6424         2117 :     bool        changed = false;
    6425              :     TypeCacheEntry *typentry;
    6426              : 
    6427         2117 :     element_type = ARR_ELEMTYPE(array);
    6428         2117 :     ndim = ARR_NDIM(array);
    6429         2117 :     dim = ARR_DIMS(array);
    6430         2117 :     nitems = ArrayGetNItems(ndim, dim);
    6431              : 
    6432              :     /* Return input array unmodified if it is empty */
    6433         2117 :     if (nitems <= 0)
    6434            0 :         return array;
    6435              : 
    6436              :     /*
    6437              :      * We can't remove elements from multi-dimensional arrays, since the
    6438              :      * result might not be rectangular.
    6439              :      */
    6440         2117 :     if (remove && ndim > 1)
    6441            3 :         ereport(ERROR,
    6442              :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    6443              :                  errmsg("removing elements from multidimensional arrays is not supported")));
    6444              : 
    6445              :     /*
    6446              :      * We arrange to look up the equality function only once per series of
    6447              :      * calls, assuming the element type doesn't change underneath us.
    6448              :      */
    6449         2114 :     typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
    6450         2114 :     if (typentry == NULL ||
    6451         1578 :         typentry->type_id != element_type)
    6452              :     {
    6453          536 :         typentry = lookup_type_cache(element_type,
    6454              :                                      TYPECACHE_EQ_OPR_FINFO);
    6455          536 :         if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
    6456            0 :             ereport(ERROR,
    6457              :                     (errcode(ERRCODE_UNDEFINED_FUNCTION),
    6458              :                      errmsg("could not identify an equality operator for type %s",
    6459              :                             format_type_be(element_type))));
    6460          536 :         fcinfo->flinfo->fn_extra = typentry;
    6461              :     }
    6462         2114 :     typlen = typentry->typlen;
    6463         2114 :     typbyval = typentry->typbyval;
    6464         2114 :     typalign = typentry->typalign;
    6465         2114 :     typalignby = typalign_to_alignby(typalign);
    6466              : 
    6467              :     /*
    6468              :      * Detoast values if they are toasted.  The replacement value must be
    6469              :      * detoasted for insertion into the result array, while detoasting the
    6470              :      * search value only once saves cycles.
    6471              :      */
    6472         2114 :     if (typlen == -1)
    6473              :     {
    6474         2093 :         if (!search_isnull)
    6475         2090 :             search = PointerGetDatum(PG_DETOAST_DATUM(search));
    6476         2093 :         if (!replace_isnull)
    6477            6 :             replace = PointerGetDatum(PG_DETOAST_DATUM(replace));
    6478              :     }
    6479              : 
    6480              :     /* Prepare to apply the comparison operator */
    6481         2114 :     InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
    6482              :                              collation, NULL, NULL);
    6483              : 
    6484              :     /* Allocate temporary arrays for new values */
    6485         2114 :     values = (Datum *) palloc(nitems * sizeof(Datum));
    6486         2114 :     nulls = (bool *) palloc(nitems * sizeof(bool));
    6487              : 
    6488              :     /* Loop over source data */
    6489         2114 :     arraydataptr = ARR_DATA_PTR(array);
    6490         2114 :     bitmap = ARR_NULLBITMAP(array);
    6491         2114 :     bitmask = 1;
    6492         2114 :     hasnulls = false;
    6493         2114 :     nresult = 0;
    6494              : 
    6495         4538 :     for (i = 0; i < nitems; i++)
    6496              :     {
    6497              :         Datum       elt;
    6498              :         bool        isNull;
    6499              :         bool        oprresult;
    6500         2424 :         bool        skip = false;
    6501              : 
    6502              :         /* Get source element, checking for NULL */
    6503         2424 :         if (bitmap && (*bitmap & bitmask) == 0)
    6504              :         {
    6505           18 :             isNull = true;
    6506              :             /* If searching for NULL, we have a match */
    6507           18 :             if (search_isnull)
    6508              :             {
    6509           18 :                 if (remove)
    6510              :                 {
    6511            6 :                     skip = true;
    6512            6 :                     changed = true;
    6513              :                 }
    6514           12 :                 else if (!replace_isnull)
    6515              :                 {
    6516            9 :                     values[nresult] = replace;
    6517            9 :                     isNull = false;
    6518            9 :                     changed = true;
    6519              :                 }
    6520              :             }
    6521              :         }
    6522              :         else
    6523              :         {
    6524         2406 :             isNull = false;
    6525         2406 :             elt = fetch_att(arraydataptr, typbyval, typlen);
    6526         2406 :             arraydataptr = att_addlength_datum(arraydataptr, typlen, elt);
    6527         2406 :             arraydataptr = (char *) att_nominal_alignby(arraydataptr, typalignby);
    6528              : 
    6529         2406 :             if (search_isnull)
    6530              :             {
    6531              :                 /* no match possible, keep element */
    6532           27 :                 values[nresult] = elt;
    6533              :             }
    6534              :             else
    6535              :             {
    6536              :                 /*
    6537              :                  * Apply the operator to the element pair; treat NULL as false
    6538              :                  */
    6539         2379 :                 locfcinfo->args[0].value = elt;
    6540         2379 :                 locfcinfo->args[0].isnull = false;
    6541         2379 :                 locfcinfo->args[1].value = search;
    6542         2379 :                 locfcinfo->args[1].isnull = false;
    6543         2379 :                 locfcinfo->isnull = false;
    6544         2379 :                 oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
    6545         2379 :                 if (locfcinfo->isnull || !oprresult)
    6546              :                 {
    6547              :                     /* no match, keep element */
    6548         2300 :                     values[nresult] = elt;
    6549              :                 }
    6550              :                 else
    6551              :                 {
    6552              :                     /* match, so replace or delete */
    6553           79 :                     changed = true;
    6554           79 :                     if (remove)
    6555           67 :                         skip = true;
    6556              :                     else
    6557              :                     {
    6558           12 :                         values[nresult] = replace;
    6559           12 :                         isNull = replace_isnull;
    6560              :                     }
    6561              :                 }
    6562              :             }
    6563              :         }
    6564              : 
    6565         2424 :         if (!skip)
    6566              :         {
    6567         2351 :             nulls[nresult] = isNull;
    6568         2351 :             if (isNull)
    6569            6 :                 hasnulls = true;
    6570              :             else
    6571              :             {
    6572              :                 /* Update total result size */
    6573         2345 :                 nbytes = att_addlength_datum(nbytes, typlen, values[nresult]);
    6574         2345 :                 nbytes = att_nominal_alignby(nbytes, typalignby);
    6575              :                 /* check for overflow of total request */
    6576         2345 :                 if (!AllocSizeIsValid(nbytes))
    6577            0 :                     ereport(ERROR,
    6578              :                             (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    6579              :                              errmsg("array size exceeds the maximum allowed (%zu)",
    6580              :                                     MaxAllocSize)));
    6581              :             }
    6582         2351 :             nresult++;
    6583              :         }
    6584              : 
    6585              :         /* advance bitmap pointer if any */
    6586         2424 :         if (bitmap)
    6587              :         {
    6588           45 :             bitmask <<= 1;
    6589           45 :             if (bitmask == 0x100)
    6590              :             {
    6591            0 :                 bitmap++;
    6592            0 :                 bitmask = 1;
    6593              :             }
    6594              :         }
    6595              :     }
    6596              : 
    6597              :     /*
    6598              :      * If not changed just return the original array
    6599              :      */
    6600         2114 :     if (!changed)
    6601              :     {
    6602         2038 :         pfree(values);
    6603         2038 :         pfree(nulls);
    6604         2038 :         return array;
    6605              :     }
    6606              : 
    6607              :     /* If all elements were removed return an empty array */
    6608           76 :     if (nresult == 0)
    6609              :     {
    6610            3 :         pfree(values);
    6611            3 :         pfree(nulls);
    6612            3 :         return construct_empty_array(element_type);
    6613              :     }
    6614              : 
    6615              :     /* Allocate and initialize the result array */
    6616           73 :     if (hasnulls)
    6617              :     {
    6618            3 :         dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nresult);
    6619            3 :         nbytes += dataoffset;
    6620              :     }
    6621              :     else
    6622              :     {
    6623           70 :         dataoffset = 0;         /* marker for no null bitmap */
    6624           70 :         nbytes += ARR_OVERHEAD_NONULLS(ndim);
    6625              :     }
    6626           73 :     result = (ArrayType *) palloc0(nbytes);
    6627           73 :     SET_VARSIZE(result, nbytes);
    6628           73 :     result->ndim = ndim;
    6629           73 :     result->dataoffset = dataoffset;
    6630           73 :     result->elemtype = element_type;
    6631           73 :     memcpy(ARR_DIMS(result), ARR_DIMS(array), ndim * sizeof(int));
    6632           73 :     memcpy(ARR_LBOUND(result), ARR_LBOUND(array), ndim * sizeof(int));
    6633              : 
    6634           73 :     if (remove)
    6635              :     {
    6636              :         /* Adjust the result length */
    6637           58 :         ARR_DIMS(result)[0] = nresult;
    6638              :     }
    6639              : 
    6640              :     /* Insert data into result array */
    6641           73 :     CopyArrayEls(result,
    6642              :                  values, nulls, nresult,
    6643              :                  typlen, typbyval, typalign,
    6644              :                  false);
    6645              : 
    6646           73 :     pfree(values);
    6647           73 :     pfree(nulls);
    6648              : 
    6649           73 :     return result;
    6650              : }
    6651              : 
    6652              : /*
    6653              :  * Remove any occurrences of an element from an array
    6654              :  *
    6655              :  * If used on a multi-dimensional array this will raise an error.
    6656              :  */
    6657              : Datum
    6658       126325 : array_remove(PG_FUNCTION_ARGS)
    6659              : {
    6660              :     ArrayType  *array;
    6661       126325 :     Datum       search = PG_GETARG_DATUM(1);
    6662       126325 :     bool        search_isnull = PG_ARGISNULL(1);
    6663              : 
    6664       126325 :     if (PG_ARGISNULL(0))
    6665       124226 :         PG_RETURN_NULL();
    6666         2099 :     array = PG_GETARG_ARRAYTYPE_P(0);
    6667              : 
    6668         2099 :     array = array_replace_internal(array,
    6669              :                                    search, search_isnull,
    6670              :                                    (Datum) 0, true,
    6671              :                                    true, PG_GET_COLLATION(),
    6672              :                                    fcinfo);
    6673         2096 :     PG_RETURN_ARRAYTYPE_P(array);
    6674              : }
    6675              : 
    6676              : /*
    6677              :  * Replace any occurrences of an element in an array
    6678              :  */
    6679              : Datum
    6680           18 : array_replace(PG_FUNCTION_ARGS)
    6681              : {
    6682              :     ArrayType  *array;
    6683           18 :     Datum       search = PG_GETARG_DATUM(1);
    6684           18 :     bool        search_isnull = PG_ARGISNULL(1);
    6685           18 :     Datum       replace = PG_GETARG_DATUM(2);
    6686           18 :     bool        replace_isnull = PG_ARGISNULL(2);
    6687              : 
    6688           18 :     if (PG_ARGISNULL(0))
    6689            0 :         PG_RETURN_NULL();
    6690           18 :     array = PG_GETARG_ARRAYTYPE_P(0);
    6691              : 
    6692           18 :     array = array_replace_internal(array,
    6693              :                                    search, search_isnull,
    6694              :                                    replace, replace_isnull,
    6695              :                                    false, PG_GET_COLLATION(),
    6696              :                                    fcinfo);
    6697           18 :     PG_RETURN_ARRAYTYPE_P(array);
    6698              : }
    6699              : 
    6700              : /*
    6701              :  * Implements width_bucket(anyelement, anyarray).
    6702              :  *
    6703              :  * 'thresholds' is an array containing lower bound values for each bucket;
    6704              :  * these must be sorted from smallest to largest, or bogus results will be
    6705              :  * produced.  If N thresholds are supplied, the output is from 0 to N:
    6706              :  * 0 is for inputs < first threshold, N is for inputs >= last threshold.
    6707              :  */
    6708              : Datum
    6709          405 : width_bucket_array(PG_FUNCTION_ARGS)
    6710              : {
    6711          405 :     Datum       operand = PG_GETARG_DATUM(0);
    6712          405 :     ArrayType  *thresholds = PG_GETARG_ARRAYTYPE_P(1);
    6713          405 :     Oid         collation = PG_GET_COLLATION();
    6714          405 :     Oid         element_type = ARR_ELEMTYPE(thresholds);
    6715              :     int         result;
    6716              : 
    6717              :     /* Check input */
    6718          405 :     if (ARR_NDIM(thresholds) > 1)
    6719            3 :         ereport(ERROR,
    6720              :                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    6721              :                  errmsg("thresholds must be one-dimensional array")));
    6722              : 
    6723          402 :     if (array_contains_nulls(thresholds))
    6724            3 :         ereport(ERROR,
    6725              :                 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
    6726              :                  errmsg("thresholds array must not contain NULLs")));
    6727              : 
    6728              :     /* We have a dedicated implementation for float8 data */
    6729          399 :     if (element_type == FLOAT8OID)
    6730          183 :         result = width_bucket_array_float8(operand, thresholds);
    6731              :     else
    6732              :     {
    6733              :         TypeCacheEntry *typentry;
    6734              : 
    6735              :         /* Cache information about the input type */
    6736          216 :         typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
    6737          216 :         if (typentry == NULL ||
    6738          195 :             typentry->type_id != element_type)
    6739              :         {
    6740           21 :             typentry = lookup_type_cache(element_type,
    6741              :                                          TYPECACHE_CMP_PROC_FINFO);
    6742           21 :             if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
    6743            0 :                 ereport(ERROR,
    6744              :                         (errcode(ERRCODE_UNDEFINED_FUNCTION),
    6745              :                          errmsg("could not identify a comparison function for type %s",
    6746              :                                 format_type_be(element_type))));
    6747           21 :             fcinfo->flinfo->fn_extra = typentry;
    6748              :         }
    6749              : 
    6750              :         /*
    6751              :          * We have separate implementation paths for fixed- and variable-width
    6752              :          * types, since indexing the array is a lot cheaper in the first case.
    6753              :          */
    6754          216 :         if (typentry->typlen > 0)
    6755           45 :             result = width_bucket_array_fixed(operand, thresholds,
    6756              :                                               collation, typentry);
    6757              :         else
    6758          171 :             result = width_bucket_array_variable(operand, thresholds,
    6759              :                                                  collation, typentry);
    6760              :     }
    6761              : 
    6762              :     /* Avoid leaking memory when handed toasted input. */
    6763          399 :     PG_FREE_IF_COPY(thresholds, 1);
    6764              : 
    6765          399 :     PG_RETURN_INT32(result);
    6766              : }
    6767              : 
    6768              : /*
    6769              :  * width_bucket_array for float8 data.
    6770              :  */
    6771              : static int
    6772          183 : width_bucket_array_float8(Datum operand, ArrayType *thresholds)
    6773              : {
    6774          183 :     float8      op = DatumGetFloat8(operand);
    6775              :     float8     *thresholds_data;
    6776              :     int         left;
    6777              :     int         right;
    6778              : 
    6779              :     /*
    6780              :      * Since we know the array contains no NULLs, we can just index it
    6781              :      * directly.
    6782              :      */
    6783          183 :     thresholds_data = (float8 *) ARR_DATA_PTR(thresholds);
    6784              : 
    6785          183 :     left = 0;
    6786          183 :     right = ArrayGetNItems(ARR_NDIM(thresholds), ARR_DIMS(thresholds));
    6787              : 
    6788              :     /*
    6789              :      * If the probe value is a NaN, it's greater than or equal to all possible
    6790              :      * threshold values (including other NaNs), so we need not search.  Note
    6791              :      * that this would give the same result as searching even if the array
    6792              :      * contains multiple NaNs (as long as they're correctly sorted), since the
    6793              :      * loop logic will find the rightmost of multiple equal threshold values.
    6794              :      */
    6795          183 :     if (isnan(op))
    6796            3 :         return right;
    6797              : 
    6798              :     /* Find the bucket */
    6799          567 :     while (left < right)
    6800              :     {
    6801          387 :         int         mid = (left + right) / 2;
    6802              : 
    6803          387 :         if (isnan(thresholds_data[mid]) || op < thresholds_data[mid])
    6804          168 :             right = mid;
    6805              :         else
    6806          219 :             left = mid + 1;
    6807              :     }
    6808              : 
    6809          180 :     return left;
    6810              : }
    6811              : 
    6812              : /*
    6813              :  * width_bucket_array for generic fixed-width data types.
    6814              :  */
    6815              : static int
    6816           45 : width_bucket_array_fixed(Datum operand,
    6817              :                          ArrayType *thresholds,
    6818              :                          Oid collation,
    6819              :                          TypeCacheEntry *typentry)
    6820              : {
    6821           45 :     LOCAL_FCINFO(locfcinfo, 2);
    6822              :     char       *thresholds_data;
    6823           45 :     int         typlen = typentry->typlen;
    6824           45 :     bool        typbyval = typentry->typbyval;
    6825              :     int         left;
    6826              :     int         right;
    6827              : 
    6828              :     /*
    6829              :      * Since we know the array contains no NULLs, we can just index it
    6830              :      * directly.
    6831              :      */
    6832           45 :     thresholds_data = (char *) ARR_DATA_PTR(thresholds);
    6833              : 
    6834           45 :     InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
    6835              :                              collation, NULL, NULL);
    6836              : 
    6837              :     /* Find the bucket */
    6838           45 :     left = 0;
    6839           45 :     right = ArrayGetNItems(ARR_NDIM(thresholds), ARR_DIMS(thresholds));
    6840          135 :     while (left < right)
    6841              :     {
    6842           90 :         int         mid = (left + right) / 2;
    6843              :         char       *ptr;
    6844              :         int32       cmpresult;
    6845              : 
    6846           90 :         ptr = thresholds_data + mid * typlen;
    6847              : 
    6848           90 :         locfcinfo->args[0].value = operand;
    6849           90 :         locfcinfo->args[0].isnull = false;
    6850           90 :         locfcinfo->args[1].value = fetch_att(ptr, typbyval, typlen);
    6851           90 :         locfcinfo->args[1].isnull = false;
    6852              : 
    6853           90 :         cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
    6854              : 
    6855              :         /* We don't expect comparison support functions to return null */
    6856              :         Assert(!locfcinfo->isnull);
    6857              : 
    6858           90 :         if (cmpresult < 0)
    6859           45 :             right = mid;
    6860              :         else
    6861           45 :             left = mid + 1;
    6862              :     }
    6863              : 
    6864           45 :     return left;
    6865              : }
    6866              : 
    6867              : /*
    6868              :  * width_bucket_array for generic variable-width data types.
    6869              :  */
    6870              : static int
    6871          171 : width_bucket_array_variable(Datum operand,
    6872              :                             ArrayType *thresholds,
    6873              :                             Oid collation,
    6874              :                             TypeCacheEntry *typentry)
    6875              : {
    6876          171 :     LOCAL_FCINFO(locfcinfo, 2);
    6877              :     char       *thresholds_data;
    6878          171 :     int         typlen = typentry->typlen;
    6879          171 :     bool        typbyval = typentry->typbyval;
    6880          171 :     char        typalign = typentry->typalign;
    6881          171 :     uint8       typalignby = typalign_to_alignby(typalign);
    6882              :     int         left;
    6883              :     int         right;
    6884              : 
    6885          171 :     thresholds_data = (char *) ARR_DATA_PTR(thresholds);
    6886              : 
    6887          171 :     InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
    6888              :                              collation, NULL, NULL);
    6889              : 
    6890              :     /* Find the bucket */
    6891          171 :     left = 0;
    6892          171 :     right = ArrayGetNItems(ARR_NDIM(thresholds), ARR_DIMS(thresholds));
    6893          534 :     while (left < right)
    6894              :     {
    6895          363 :         int         mid = (left + right) / 2;
    6896              :         char       *ptr;
    6897              :         int         i;
    6898              :         int32       cmpresult;
    6899              : 
    6900              :         /* Locate mid'th array element by advancing from left element */
    6901          363 :         ptr = thresholds_data;
    6902          621 :         for (i = left; i < mid; i++)
    6903              :         {
    6904          258 :             ptr = att_addlength_pointer(ptr, typlen, ptr);
    6905          258 :             ptr = (char *) att_nominal_alignby(ptr, typalignby);
    6906              :         }
    6907              : 
    6908          363 :         locfcinfo->args[0].value = operand;
    6909          363 :         locfcinfo->args[0].isnull = false;
    6910          363 :         locfcinfo->args[1].value = fetch_att(ptr, typbyval, typlen);
    6911          363 :         locfcinfo->args[1].isnull = false;
    6912              : 
    6913          363 :         cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
    6914              : 
    6915              :         /* We don't expect comparison support functions to return null */
    6916              :         Assert(!locfcinfo->isnull);
    6917              : 
    6918          363 :         if (cmpresult < 0)
    6919          150 :             right = mid;
    6920              :         else
    6921              :         {
    6922          213 :             left = mid + 1;
    6923              : 
    6924              :             /*
    6925              :              * Move the thresholds pointer to match new "left" index, so we
    6926              :              * don't have to seek over those elements again.  This trick
    6927              :              * ensures we do only O(N) array indexing work, not O(N^2).
    6928              :              */
    6929          213 :             ptr = att_addlength_pointer(ptr, typlen, ptr);
    6930          213 :             thresholds_data = (char *) att_nominal_alignby(ptr, typalignby);
    6931              :         }
    6932              :     }
    6933              : 
    6934          171 :     return left;
    6935              : }
    6936              : 
    6937              : /*
    6938              :  * Trim the last N elements from an array by building an appropriate slice.
    6939              :  * Only the first dimension is trimmed.
    6940              :  */
    6941              : Datum
    6942           24 : trim_array(PG_FUNCTION_ARGS)
    6943              : {
    6944           24 :     ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
    6945           24 :     int         n = PG_GETARG_INT32(1);
    6946           24 :     int         array_length = (ARR_NDIM(v) > 0) ? ARR_DIMS(v)[0] : 0;
    6947              :     int16       elmlen;
    6948              :     bool        elmbyval;
    6949              :     char        elmalign;
    6950              :     int         lower[MAXDIM];
    6951              :     int         upper[MAXDIM];
    6952              :     bool        lowerProvided[MAXDIM];
    6953              :     bool        upperProvided[MAXDIM];
    6954              :     Datum       result;
    6955              : 
    6956              :     /* Per spec, throw an error if out of bounds */
    6957           24 :     if (n < 0 || n > array_length)
    6958            9 :         ereport(ERROR,
    6959              :                 (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
    6960              :                  errmsg("number of elements to trim must be between 0 and %d",
    6961              :                         array_length)));
    6962              : 
    6963              :     /* Set all the bounds as unprovided except the first upper bound */
    6964           15 :     memset(lowerProvided, false, sizeof(lowerProvided));
    6965           15 :     memset(upperProvided, false, sizeof(upperProvided));
    6966           15 :     if (ARR_NDIM(v) > 0)
    6967              :     {
    6968           15 :         upper[0] = ARR_LBOUND(v)[0] + array_length - n - 1;
    6969           15 :         upperProvided[0] = true;
    6970              :     }
    6971              : 
    6972              :     /* Fetch the needed information about the element type */
    6973           15 :     get_typlenbyvalalign(ARR_ELEMTYPE(v), &elmlen, &elmbyval, &elmalign);
    6974              : 
    6975              :     /* Get the slice */
    6976           15 :     result = array_get_slice(PointerGetDatum(v), 1,
    6977              :                              upper, lower, upperProvided, lowerProvided,
    6978              :                              -1, elmlen, elmbyval, elmalign);
    6979              : 
    6980           15 :     PG_RETURN_DATUM(result);
    6981              : }
        

Generated by: LCOV version 2.0-1