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

Generated by: LCOV version 1.13