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

Generated by: LCOV version 1.11