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

Generated by: LCOV version 1.13