LCOV - code coverage report
Current view: top level - src/backend/utils/adt - arrayfuncs.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 2205 2349 93.9 %
Date: 2024-11-21 08:14:44 Functions: 90 90 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14