]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/arrayfuncs.c
Improve hash_array() logic for combining hash values.
[postgresql] / src / backend / utils / adt / arrayfuncs.c
1 /*-------------------------------------------------------------------------
2  *
3  * arrayfuncs.c
4  *        Support functions for arrays.
5  *
6  * Portions Copyright (c) 1996-2011, 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
19 #include "funcapi.h"
20 #include "libpq/pqformat.h"
21 #include "parser/parse_coerce.h"
22 #include "utils/array.h"
23 #include "utils/builtins.h"
24 #include "utils/datum.h"
25 #include "utils/lsyscache.h"
26 #include "utils/memutils.h"
27 #include "utils/typcache.h"
28
29
30 /*
31  * GUC parameter
32  */
33 bool            Array_nulls = true;
34
35 /*
36  * Local definitions
37  */
38 #define ASSGN    "="
39
40 typedef enum
41 {
42         ARRAY_NO_LEVEL,
43         ARRAY_LEVEL_STARTED,
44         ARRAY_ELEM_STARTED,
45         ARRAY_ELEM_COMPLETED,
46         ARRAY_QUOTED_ELEM_STARTED,
47         ARRAY_QUOTED_ELEM_COMPLETED,
48         ARRAY_ELEM_DELIMITED,
49         ARRAY_LEVEL_COMPLETED,
50         ARRAY_LEVEL_DELIMITED
51 } ArrayParseState;
52
53 /* Working state for array_iterate() */
54 typedef struct ArrayIteratorData
55 {
56         /* basic info about the array, set up during array_create_iterator() */
57         ArrayType  *arr;                        /* array we're iterating through */
58         bits8      *nullbitmap;         /* its null bitmap, if any */
59         int                     nitems;                 /* total number of elements in array */
60         int16           typlen;                 /* element type's length */
61         bool            typbyval;               /* element type's byval property */
62         char            typalign;               /* element type's align property */
63
64         /* information about the requested slice size */
65         int                     slice_ndim;             /* slice dimension, or 0 if not slicing */
66         int                     slice_len;              /* number of elements per slice */
67         int                *slice_dims;         /* slice dims array */
68         int                *slice_lbound;       /* slice lbound array */
69         Datum      *slice_values;       /* workspace of length slice_len */
70         bool       *slice_nulls;        /* workspace of length slice_len */
71
72         /* current position information, updated on each iteration */
73         char       *data_ptr;           /* our current position in the array */
74         int                     current_item;   /* the item # we're at in the array */
75 }       ArrayIteratorData;
76
77 static bool array_isspace(char ch);
78 static int      ArrayCount(const char *str, int *dim, char typdelim);
79 static void ReadArrayStr(char *arrayStr, const char *origStr,
80                          int nitems, int ndim, int *dim,
81                          FmgrInfo *inputproc, Oid typioparam, int32 typmod,
82                          char typdelim,
83                          int typlen, bool typbyval, char typalign,
84                          Datum *values, bool *nulls,
85                          bool *hasnulls, int32 *nbytes);
86 static void ReadArrayBinary(StringInfo buf, int nitems,
87                                 FmgrInfo *receiveproc, Oid typioparam, int32 typmod,
88                                 int typlen, bool typbyval, char typalign,
89                                 Datum *values, bool *nulls,
90                                 bool *hasnulls, int32 *nbytes);
91 static void CopyArrayEls(ArrayType *array,
92                          Datum *values, bool *nulls, int nitems,
93                          int typlen, bool typbyval, char typalign,
94                          bool freedata);
95 static bool array_get_isnull(const bits8 *nullbitmap, int offset);
96 static void array_set_isnull(bits8 *nullbitmap, int offset, bool isNull);
97 static Datum ArrayCast(char *value, bool byval, int len);
98 static int ArrayCastAndSet(Datum src,
99                                 int typlen, bool typbyval, char typalign,
100                                 char *dest);
101 static char *array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
102                    int typlen, bool typbyval, char typalign);
103 static int array_nelems_size(char *ptr, int offset, bits8 *nullbitmap,
104                                   int nitems, int typlen, bool typbyval, char typalign);
105 static int array_copy(char *destptr, int nitems,
106                    char *srcptr, int offset, bits8 *nullbitmap,
107                    int typlen, bool typbyval, char typalign);
108 static int array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
109                                  int ndim, int *dim, int *lb,
110                                  int *st, int *endp,
111                                  int typlen, bool typbyval, char typalign);
112 static void array_extract_slice(ArrayType *newarray,
113                                         int ndim, int *dim, int *lb,
114                                         char *arraydataptr, bits8 *arraynullsptr,
115                                         int *st, int *endp,
116                                         int typlen, bool typbyval, char typalign);
117 static void array_insert_slice(ArrayType *destArray, ArrayType *origArray,
118                                    ArrayType *srcArray,
119                                    int ndim, int *dim, int *lb,
120                                    int *st, int *endp,
121                                    int typlen, bool typbyval, char typalign);
122 static int      array_cmp(FunctionCallInfo fcinfo);
123 static ArrayType *create_array_envelope(int ndims, int *dimv, int *lbv, int nbytes,
124                                           Oid elmtype, int dataoffset);
125 static ArrayType *array_fill_internal(ArrayType *dims, ArrayType *lbs,
126                                         Datum value, bool isnull, Oid elmtype,
127                                         FunctionCallInfo fcinfo);
128
129
130 /*
131  * array_in :
132  *                converts an array from the external format in "string" to
133  *                its internal format.
134  *
135  * return value :
136  *                the internal representation of the input array
137  */
138 Datum
139 array_in(PG_FUNCTION_ARGS)
140 {
141         char       *string = PG_GETARG_CSTRING(0);      /* external form */
142         Oid                     element_type = PG_GETARG_OID(1);                /* type of an array
143                                                                                                                  * element */
144         int32           typmod = PG_GETARG_INT32(2);    /* typmod for array elements */
145         int                     typlen;
146         bool            typbyval;
147         char            typalign;
148         char            typdelim;
149         Oid                     typioparam;
150         char       *string_save,
151                            *p;
152         int                     i,
153                                 nitems;
154         Datum      *dataPtr;
155         bool       *nullsPtr;
156         bool            hasnulls;
157         int32           nbytes;
158         int32           dataoffset;
159         ArrayType  *retval;
160         int                     ndim,
161                                 dim[MAXDIM],
162                                 lBound[MAXDIM];
163         ArrayMetaState *my_extra;
164
165         /*
166          * We arrange to look up info about element type, including its input
167          * conversion proc, only once per series of calls, assuming the element
168          * type doesn't change underneath us.
169          */
170         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
171         if (my_extra == NULL)
172         {
173                 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
174                                                                                                           sizeof(ArrayMetaState));
175                 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
176                 my_extra->element_type = ~element_type;
177         }
178
179         if (my_extra->element_type != element_type)
180         {
181                 /*
182                  * Get info about element type, including its input conversion proc
183                  */
184                 get_type_io_data(element_type, IOFunc_input,
185                                                  &my_extra->typlen, &my_extra->typbyval,
186                                                  &my_extra->typalign, &my_extra->typdelim,
187                                                  &my_extra->typioparam, &my_extra->typiofunc);
188                 fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
189                                           fcinfo->flinfo->fn_mcxt);
190                 my_extra->element_type = element_type;
191         }
192         typlen = my_extra->typlen;
193         typbyval = my_extra->typbyval;
194         typalign = my_extra->typalign;
195         typdelim = my_extra->typdelim;
196         typioparam = my_extra->typioparam;
197
198         /* Make a modifiable copy of the input */
199         string_save = pstrdup(string);
200
201         /*
202          * If the input string starts with dimension info, read and use that.
203          * Otherwise, we require the input to be in curly-brace style, and we
204          * prescan the input to determine dimensions.
205          *
206          * Dimension info takes the form of one or more [n] or [m:n] items. The
207          * outer loop iterates once per dimension item.
208          */
209         p = string_save;
210         ndim = 0;
211         for (;;)
212         {
213                 char       *q;
214                 int                     ub;
215
216                 /*
217                  * Note: we currently allow whitespace between, but not within,
218                  * dimension items.
219                  */
220                 while (array_isspace(*p))
221                         p++;
222                 if (*p != '[')
223                         break;                          /* no more dimension items */
224                 p++;
225                 if (ndim >= MAXDIM)
226                         ereport(ERROR,
227                                         (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
228                                          errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
229                                                         ndim + 1, MAXDIM)));
230
231                 for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++);
232                 if (q == p)                             /* no digits? */
233                         ereport(ERROR,
234                                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
235                                          errmsg("missing dimension value")));
236
237                 if (*q == ':')
238                 {
239                         /* [m:n] format */
240                         *q = '\0';
241                         lBound[ndim] = atoi(p);
242                         p = q + 1;
243                         for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++);
244                         if (q == p)                     /* no digits? */
245                                 ereport(ERROR,
246                                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
247                                                  errmsg("missing dimension value")));
248                 }
249                 else
250                 {
251                         /* [n] format */
252                         lBound[ndim] = 1;
253                 }
254                 if (*q != ']')
255                         ereport(ERROR,
256                                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
257                                          errmsg("missing \"]\" in array dimensions")));
258
259                 *q = '\0';
260                 ub = atoi(p);
261                 p = q + 1;
262                 if (ub < lBound[ndim])
263                         ereport(ERROR,
264                                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
265                                          errmsg("upper bound cannot be less than lower bound")));
266
267                 dim[ndim] = ub - lBound[ndim] + 1;
268                 ndim++;
269         }
270
271         if (ndim == 0)
272         {
273                 /* No array dimensions, so intuit dimensions from brace structure */
274                 if (*p != '{')
275                         ereport(ERROR,
276                                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
277                                          errmsg("array value must start with \"{\" or dimension information")));
278                 ndim = ArrayCount(p, dim, typdelim);
279                 for (i = 0; i < ndim; i++)
280                         lBound[i] = 1;
281         }
282         else
283         {
284                 int                     ndim_braces,
285                                         dim_braces[MAXDIM];
286
287                 /* If array dimensions are given, expect '=' operator */
288                 if (strncmp(p, ASSGN, strlen(ASSGN)) != 0)
289                         ereport(ERROR,
290                                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
291                                          errmsg("missing assignment operator")));
292                 p += strlen(ASSGN);
293                 while (array_isspace(*p))
294                         p++;
295
296                 /*
297                  * intuit dimensions from brace structure -- it better match what we
298                  * were given
299                  */
300                 if (*p != '{')
301                         ereport(ERROR,
302                                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
303                                          errmsg("array value must start with \"{\" or dimension information")));
304                 ndim_braces = ArrayCount(p, dim_braces, typdelim);
305                 if (ndim_braces != ndim)
306                         ereport(ERROR,
307                                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
308                                 errmsg("array dimensions incompatible with array literal")));
309                 for (i = 0; i < ndim; ++i)
310                 {
311                         if (dim[i] != dim_braces[i])
312                                 ereport(ERROR,
313                                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
314                                 errmsg("array dimensions incompatible with array literal")));
315                 }
316         }
317
318 #ifdef ARRAYDEBUG
319         printf("array_in- ndim %d (", ndim);
320         for (i = 0; i < ndim; i++)
321         {
322                 printf(" %d", dim[i]);
323         };
324         printf(") for %s\n", string);
325 #endif
326
327         /* This checks for overflow of the array dimensions */
328         nitems = ArrayGetNItems(ndim, dim);
329         /* Empty array? */
330         if (nitems == 0)
331                 PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
332
333         dataPtr = (Datum *) palloc(nitems * sizeof(Datum));
334         nullsPtr = (bool *) palloc(nitems * sizeof(bool));
335         ReadArrayStr(p, string,
336                                  nitems, ndim, dim,
337                                  &my_extra->proc, typioparam, typmod,
338                                  typdelim,
339                                  typlen, typbyval, typalign,
340                                  dataPtr, nullsPtr,
341                                  &hasnulls, &nbytes);
342         if (hasnulls)
343         {
344                 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
345                 nbytes += dataoffset;
346         }
347         else
348         {
349                 dataoffset = 0;                 /* marker for no null bitmap */
350                 nbytes += ARR_OVERHEAD_NONULLS(ndim);
351         }
352         retval = (ArrayType *) palloc0(nbytes);
353         SET_VARSIZE(retval, nbytes);
354         retval->ndim = ndim;
355         retval->dataoffset = dataoffset;
356
357         /*
358          * This comes from the array's pg_type.typelem (which points to the base
359          * data type's pg_type.oid) and stores system oids in user tables. This
360          * oid must be preserved by binary upgrades.
361          */
362         retval->elemtype = element_type;
363         memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
364         memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
365
366         CopyArrayEls(retval,
367                                  dataPtr, nullsPtr, nitems,
368                                  typlen, typbyval, typalign,
369                                  true);
370
371         pfree(dataPtr);
372         pfree(nullsPtr);
373         pfree(string_save);
374
375         PG_RETURN_ARRAYTYPE_P(retval);
376 }
377
378 /*
379  * array_isspace() --- a non-locale-dependent isspace()
380  *
381  * We used to use isspace() for parsing array values, but that has
382  * undesirable results: an array value might be silently interpreted
383  * differently depending on the locale setting.  Now we just hard-wire
384  * the traditional ASCII definition of isspace().
385  */
386 static bool
387 array_isspace(char ch)
388 {
389         if (ch == ' ' ||
390                 ch == '\t' ||
391                 ch == '\n' ||
392                 ch == '\r' ||
393                 ch == '\v' ||
394                 ch == '\f')
395                 return true;
396         return false;
397 }
398
399 /*
400  * ArrayCount
401  *       Determines the dimensions for an array string.
402  *
403  * Returns number of dimensions as function result.  The axis lengths are
404  * returned in dim[], which must be of size MAXDIM.
405  */
406 static int
407 ArrayCount(const char *str, int *dim, char typdelim)
408 {
409         int                     nest_level = 0,
410                                 i;
411         int                     ndim = 1,
412                                 temp[MAXDIM],
413                                 nelems[MAXDIM],
414                                 nelems_last[MAXDIM];
415         bool            in_quotes = false;
416         bool            eoArray = false;
417         bool            empty_array = true;
418         const char *ptr;
419         ArrayParseState parse_state = ARRAY_NO_LEVEL;
420
421         for (i = 0; i < MAXDIM; ++i)
422         {
423                 temp[i] = dim[i] = 0;
424                 nelems_last[i] = nelems[i] = 1;
425         }
426
427         ptr = str;
428         while (!eoArray)
429         {
430                 bool            itemdone = false;
431
432                 while (!itemdone)
433                 {
434                         if (parse_state == ARRAY_ELEM_STARTED ||
435                                 parse_state == ARRAY_QUOTED_ELEM_STARTED)
436                                 empty_array = false;
437
438                         switch (*ptr)
439                         {
440                                 case '\0':
441                                         /* Signal a premature end of the string */
442                                         ereport(ERROR,
443                                                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
444                                                          errmsg("malformed array literal: \"%s\"", str)));
445                                         break;
446                                 case '\\':
447
448                                         /*
449                                          * An escape must be after a level start, after an element
450                                          * start, or after an element delimiter. In any case we
451                                          * now must be past an element start.
452                                          */
453                                         if (parse_state != ARRAY_LEVEL_STARTED &&
454                                                 parse_state != ARRAY_ELEM_STARTED &&
455                                                 parse_state != ARRAY_QUOTED_ELEM_STARTED &&
456                                                 parse_state != ARRAY_ELEM_DELIMITED)
457                                                 ereport(ERROR,
458                                                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
459                                                         errmsg("malformed array literal: \"%s\"", str)));
460                                         if (parse_state != ARRAY_QUOTED_ELEM_STARTED)
461                                                 parse_state = ARRAY_ELEM_STARTED;
462                                         /* skip the escaped character */
463                                         if (*(ptr + 1))
464                                                 ptr++;
465                                         else
466                                                 ereport(ERROR,
467                                                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
468                                                         errmsg("malformed array literal: \"%s\"", str)));
469                                         break;
470                                 case '\"':
471
472                                         /*
473                                          * A quote must be after a level start, after a quoted
474                                          * element start, or after an element delimiter. In any
475                                          * case we now must be past an element start.
476                                          */
477                                         if (parse_state != ARRAY_LEVEL_STARTED &&
478                                                 parse_state != ARRAY_QUOTED_ELEM_STARTED &&
479                                                 parse_state != ARRAY_ELEM_DELIMITED)
480                                                 ereport(ERROR,
481                                                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
482                                                         errmsg("malformed array literal: \"%s\"", str)));
483                                         in_quotes = !in_quotes;
484                                         if (in_quotes)
485                                                 parse_state = ARRAY_QUOTED_ELEM_STARTED;
486                                         else
487                                                 parse_state = ARRAY_QUOTED_ELEM_COMPLETED;
488                                         break;
489                                 case '{':
490                                         if (!in_quotes)
491                                         {
492                                                 /*
493                                                  * A left brace can occur if no nesting has occurred
494                                                  * yet, after a level start, or after a level
495                                                  * delimiter.
496                                                  */
497                                                 if (parse_state != ARRAY_NO_LEVEL &&
498                                                         parse_state != ARRAY_LEVEL_STARTED &&
499                                                         parse_state != ARRAY_LEVEL_DELIMITED)
500                                                         ereport(ERROR,
501                                                            (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
502                                                         errmsg("malformed array literal: \"%s\"", str)));
503                                                 parse_state = ARRAY_LEVEL_STARTED;
504                                                 if (nest_level >= MAXDIM)
505                                                         ereport(ERROR,
506                                                                         (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
507                                                                          errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
508                                                                                         nest_level + 1, MAXDIM)));
509                                                 temp[nest_level] = 0;
510                                                 nest_level++;
511                                                 if (ndim < nest_level)
512                                                         ndim = nest_level;
513                                         }
514                                         break;
515                                 case '}':
516                                         if (!in_quotes)
517                                         {
518                                                 /*
519                                                  * A right brace can occur after an element start, an
520                                                  * element completion, a quoted element completion, or
521                                                  * a level completion.
522                                                  */
523                                                 if (parse_state != ARRAY_ELEM_STARTED &&
524                                                         parse_state != ARRAY_ELEM_COMPLETED &&
525                                                         parse_state != ARRAY_QUOTED_ELEM_COMPLETED &&
526                                                         parse_state != ARRAY_LEVEL_COMPLETED &&
527                                                         !(nest_level == 1 && parse_state == ARRAY_LEVEL_STARTED))
528                                                         ereport(ERROR,
529                                                            (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
530                                                         errmsg("malformed array literal: \"%s\"", str)));
531                                                 parse_state = ARRAY_LEVEL_COMPLETED;
532                                                 if (nest_level == 0)
533                                                         ereport(ERROR,
534                                                            (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
535                                                         errmsg("malformed array literal: \"%s\"", str)));
536                                                 nest_level--;
537
538                                                 if ((nelems_last[nest_level] != 1) &&
539                                                         (nelems[nest_level] != nelems_last[nest_level]))
540                                                         ereport(ERROR,
541                                                            (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
542                                                                 errmsg("multidimensional arrays must have "
543                                                                            "array expressions with matching "
544                                                                            "dimensions")));
545                                                 nelems_last[nest_level] = nelems[nest_level];
546                                                 nelems[nest_level] = 1;
547                                                 if (nest_level == 0)
548                                                         eoArray = itemdone = true;
549                                                 else
550                                                 {
551                                                         /*
552                                                          * We don't set itemdone here; see comments in
553                                                          * ReadArrayStr
554                                                          */
555                                                         temp[nest_level - 1]++;
556                                                 }
557                                         }
558                                         break;
559                                 default:
560                                         if (!in_quotes)
561                                         {
562                                                 if (*ptr == typdelim)
563                                                 {
564                                                         /*
565                                                          * Delimiters can occur after an element start, an
566                                                          * element completion, a quoted element
567                                                          * completion, or a level completion.
568                                                          */
569                                                         if (parse_state != ARRAY_ELEM_STARTED &&
570                                                                 parse_state != ARRAY_ELEM_COMPLETED &&
571                                                                 parse_state != ARRAY_QUOTED_ELEM_COMPLETED &&
572                                                                 parse_state != ARRAY_LEVEL_COMPLETED)
573                                                                 ereport(ERROR,
574                                                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
575                                                                  errmsg("malformed array literal: \"%s\"", str)));
576                                                         if (parse_state == ARRAY_LEVEL_COMPLETED)
577                                                                 parse_state = ARRAY_LEVEL_DELIMITED;
578                                                         else
579                                                                 parse_state = ARRAY_ELEM_DELIMITED;
580                                                         itemdone = true;
581                                                         nelems[nest_level - 1]++;
582                                                 }
583                                                 else if (!array_isspace(*ptr))
584                                                 {
585                                                         /*
586                                                          * Other non-space characters must be after a
587                                                          * level start, after an element start, or after
588                                                          * an element delimiter. In any case we now must
589                                                          * be past an element start.
590                                                          */
591                                                         if (parse_state != ARRAY_LEVEL_STARTED &&
592                                                                 parse_state != ARRAY_ELEM_STARTED &&
593                                                                 parse_state != ARRAY_ELEM_DELIMITED)
594                                                                 ereport(ERROR,
595                                                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
596                                                                  errmsg("malformed array literal: \"%s\"", str)));
597                                                         parse_state = ARRAY_ELEM_STARTED;
598                                                 }
599                                         }
600                                         break;
601                         }
602                         if (!itemdone)
603                                 ptr++;
604                 }
605                 temp[ndim - 1]++;
606                 ptr++;
607         }
608
609         /* only whitespace is allowed after the closing brace */
610         while (*ptr)
611         {
612                 if (!array_isspace(*ptr++))
613                         ereport(ERROR,
614                                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
615                                          errmsg("malformed array literal: \"%s\"", str)));
616         }
617
618         /* special case for an empty array */
619         if (empty_array)
620                 return 0;
621
622         for (i = 0; i < ndim; ++i)
623                 dim[i] = temp[i];
624
625         return ndim;
626 }
627
628 /*
629  * ReadArrayStr :
630  *       parses the array string pointed to by "arrayStr" and converts the values
631  *       to internal format.  Unspecified elements are initialized to nulls.
632  *       The array dimensions must already have been determined.
633  *
634  * Inputs:
635  *      arrayStr: the string to parse.
636  *                        CAUTION: the contents of "arrayStr" will be modified!
637  *      origStr: the unmodified input string, used only in error messages.
638  *      nitems: total number of array elements, as already determined.
639  *      ndim: number of array dimensions
640  *      dim[]: array axis lengths
641  *      inputproc: type-specific input procedure for element datatype.
642  *      typioparam, typmod: auxiliary values to pass to inputproc.
643  *      typdelim: the value delimiter (type-specific).
644  *      typlen, typbyval, typalign: storage parameters of element datatype.
645  *
646  * Outputs:
647  *      values[]: filled with converted data values.
648  *      nulls[]: filled with is-null markers.
649  *      *hasnulls: set TRUE iff there are any null elements.
650  *      *nbytes: set to total size of data area needed (including alignment
651  *              padding but not including array header overhead).
652  *
653  * Note that values[] and nulls[] are allocated by the caller, and must have
654  * nitems elements.
655  */
656 static void
657 ReadArrayStr(char *arrayStr,
658                          const char *origStr,
659                          int nitems,
660                          int ndim,
661                          int *dim,
662                          FmgrInfo *inputproc,
663                          Oid typioparam,
664                          int32 typmod,
665                          char typdelim,
666                          int typlen,
667                          bool typbyval,
668                          char typalign,
669                          Datum *values,
670                          bool *nulls,
671                          bool *hasnulls,
672                          int32 *nbytes)
673 {
674         int                     i,
675                                 nest_level = 0;
676         char       *srcptr;
677         bool            in_quotes = false;
678         bool            eoArray = false;
679         bool            hasnull;
680         int32           totbytes;
681         int                     indx[MAXDIM],
682                                 prod[MAXDIM];
683
684         mda_get_prod(ndim, dim, prod);
685         MemSet(indx, 0, sizeof(indx));
686
687         /* Initialize is-null markers to true */
688         memset(nulls, true, nitems * sizeof(bool));
689
690         /*
691          * We have to remove " and \ characters to create a clean item value to
692          * pass to the datatype input routine.  We overwrite each item value
693          * in-place within arrayStr to do this.  srcptr is the current scan point,
694          * and dstptr is where we are copying to.
695          *
696          * We also want to suppress leading and trailing unquoted whitespace. We
697          * use the leadingspace flag to suppress leading space.  Trailing space is
698          * tracked by using dstendptr to point to the last significant output
699          * character.
700          *
701          * The error checking in this routine is mostly pro-forma, since we expect
702          * that ArrayCount() already validated the string.
703          */
704         srcptr = arrayStr;
705         while (!eoArray)
706         {
707                 bool            itemdone = false;
708                 bool            leadingspace = true;
709                 bool            hasquoting = false;
710                 char       *itemstart;
711                 char       *dstptr;
712                 char       *dstendptr;
713
714                 i = -1;
715                 itemstart = dstptr = dstendptr = srcptr;
716
717                 while (!itemdone)
718                 {
719                         switch (*srcptr)
720                         {
721                                 case '\0':
722                                         /* Signal a premature end of the string */
723                                         ereport(ERROR,
724                                                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
725                                                          errmsg("malformed array literal: \"%s\"",
726                                                                         origStr)));
727                                         break;
728                                 case '\\':
729                                         /* Skip backslash, copy next character as-is. */
730                                         srcptr++;
731                                         if (*srcptr == '\0')
732                                                 ereport(ERROR,
733                                                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
734                                                                  errmsg("malformed array literal: \"%s\"",
735                                                                                 origStr)));
736                                         *dstptr++ = *srcptr++;
737                                         /* Treat the escaped character as non-whitespace */
738                                         leadingspace = false;
739                                         dstendptr = dstptr;
740                                         hasquoting = true;      /* can't be a NULL marker */
741                                         break;
742                                 case '\"':
743                                         in_quotes = !in_quotes;
744                                         if (in_quotes)
745                                                 leadingspace = false;
746                                         else
747                                         {
748                                                 /*
749                                                  * Advance dstendptr when we exit in_quotes; this
750                                                  * saves having to do it in all the other in_quotes
751                                                  * cases.
752                                                  */
753                                                 dstendptr = dstptr;
754                                         }
755                                         hasquoting = true;      /* can't be a NULL marker */
756                                         srcptr++;
757                                         break;
758                                 case '{':
759                                         if (!in_quotes)
760                                         {
761                                                 if (nest_level >= ndim)
762                                                         ereport(ERROR,
763                                                            (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
764                                                                 errmsg("malformed array literal: \"%s\"",
765                                                                            origStr)));
766                                                 nest_level++;
767                                                 indx[nest_level - 1] = 0;
768                                                 srcptr++;
769                                         }
770                                         else
771                                                 *dstptr++ = *srcptr++;
772                                         break;
773                                 case '}':
774                                         if (!in_quotes)
775                                         {
776                                                 if (nest_level == 0)
777                                                         ereport(ERROR,
778                                                            (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
779                                                                 errmsg("malformed array literal: \"%s\"",
780                                                                            origStr)));
781                                                 if (i == -1)
782                                                         i = ArrayGetOffset0(ndim, indx, prod);
783                                                 indx[nest_level - 1] = 0;
784                                                 nest_level--;
785                                                 if (nest_level == 0)
786                                                         eoArray = itemdone = true;
787                                                 else
788                                                         indx[nest_level - 1]++;
789                                                 srcptr++;
790                                         }
791                                         else
792                                                 *dstptr++ = *srcptr++;
793                                         break;
794                                 default:
795                                         if (in_quotes)
796                                                 *dstptr++ = *srcptr++;
797                                         else if (*srcptr == typdelim)
798                                         {
799                                                 if (i == -1)
800                                                         i = ArrayGetOffset0(ndim, indx, prod);
801                                                 itemdone = true;
802                                                 indx[ndim - 1]++;
803                                                 srcptr++;
804                                         }
805                                         else if (array_isspace(*srcptr))
806                                         {
807                                                 /*
808                                                  * If leading space, drop it immediately.  Else, copy
809                                                  * but don't advance dstendptr.
810                                                  */
811                                                 if (leadingspace)
812                                                         srcptr++;
813                                                 else
814                                                         *dstptr++ = *srcptr++;
815                                         }
816                                         else
817                                         {
818                                                 *dstptr++ = *srcptr++;
819                                                 leadingspace = false;
820                                                 dstendptr = dstptr;
821                                         }
822                                         break;
823                         }
824                 }
825
826                 Assert(dstptr < srcptr);
827                 *dstendptr = '\0';
828
829                 if (i < 0 || i >= nitems)
830                         ereport(ERROR,
831                                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
832                                          errmsg("malformed array literal: \"%s\"",
833                                                         origStr)));
834
835                 if (Array_nulls && !hasquoting &&
836                         pg_strcasecmp(itemstart, "NULL") == 0)
837                 {
838                         /* it's a NULL item */
839                         values[i] = InputFunctionCall(inputproc, NULL,
840                                                                                   typioparam, typmod);
841                         nulls[i] = true;
842                 }
843                 else
844                 {
845                         values[i] = InputFunctionCall(inputproc, itemstart,
846                                                                                   typioparam, typmod);
847                         nulls[i] = false;
848                 }
849         }
850
851         /*
852          * Check for nulls, compute total data space needed
853          */
854         hasnull = false;
855         totbytes = 0;
856         for (i = 0; i < nitems; i++)
857         {
858                 if (nulls[i])
859                         hasnull = true;
860                 else
861                 {
862                         /* let's just make sure data is not toasted */
863                         if (typlen == -1)
864                                 values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
865                         totbytes = att_addlength_datum(totbytes, typlen, values[i]);
866                         totbytes = att_align_nominal(totbytes, typalign);
867                         /* check for overflow of total request */
868                         if (!AllocSizeIsValid(totbytes))
869                                 ereport(ERROR,
870                                                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
871                                                  errmsg("array size exceeds the maximum allowed (%d)",
872                                                                 (int) MaxAllocSize)));
873                 }
874         }
875         *hasnulls = hasnull;
876         *nbytes = totbytes;
877 }
878
879
880 /*
881  * Copy data into an array object from a temporary array of Datums.
882  *
883  * array: array object (with header fields already filled in)
884  * values: array of Datums to be copied
885  * nulls: array of is-null flags (can be NULL if no nulls)
886  * nitems: number of Datums to be copied
887  * typbyval, typlen, typalign: info about element datatype
888  * freedata: if TRUE and element type is pass-by-ref, pfree data values
889  * referenced by Datums after copying them.
890  *
891  * If the input data is of varlena type, the caller must have ensured that
892  * the values are not toasted.  (Doing it here doesn't work since the
893  * caller has already allocated space for the array...)
894  */
895 static void
896 CopyArrayEls(ArrayType *array,
897                          Datum *values,
898                          bool *nulls,
899                          int nitems,
900                          int typlen,
901                          bool typbyval,
902                          char typalign,
903                          bool freedata)
904 {
905         char       *p = ARR_DATA_PTR(array);
906         bits8      *bitmap = ARR_NULLBITMAP(array);
907         int                     bitval = 0;
908         int                     bitmask = 1;
909         int                     i;
910
911         if (typbyval)
912                 freedata = false;
913
914         for (i = 0; i < nitems; i++)
915         {
916                 if (nulls && nulls[i])
917                 {
918                         if (!bitmap)            /* shouldn't happen */
919                                 elog(ERROR, "null array element where not supported");
920                         /* bitmap bit stays 0 */
921                 }
922                 else
923                 {
924                         bitval |= bitmask;
925                         p += ArrayCastAndSet(values[i], typlen, typbyval, typalign, p);
926                         if (freedata)
927                                 pfree(DatumGetPointer(values[i]));
928                 }
929                 if (bitmap)
930                 {
931                         bitmask <<= 1;
932                         if (bitmask == 0x100)
933                         {
934                                 *bitmap++ = bitval;
935                                 bitval = 0;
936                                 bitmask = 1;
937                         }
938                 }
939         }
940
941         if (bitmap && bitmask != 1)
942                 *bitmap = bitval;
943 }
944
945 /*
946  * array_out :
947  *                 takes the internal representation of an array and returns a string
948  *                containing the array in its external format.
949  */
950 Datum
951 array_out(PG_FUNCTION_ARGS)
952 {
953         ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
954         Oid                     element_type = ARR_ELEMTYPE(v);
955         int                     typlen;
956         bool            typbyval;
957         char            typalign;
958         char            typdelim;
959         char       *p,
960                            *tmp,
961                            *retval,
962                           **values,
963                                 dims_str[(MAXDIM * 33) + 2];
964
965         /*
966          * 33 per dim since we assume 15 digits per number + ':' +'[]'
967          *
968          * +2 allows for assignment operator + trailing null
969          */
970         bits8      *bitmap;
971         int                     bitmask;
972         bool       *needquotes,
973                                 needdims = false;
974         int                     nitems,
975                                 overall_length,
976                                 i,
977                                 j,
978                                 k,
979                                 indx[MAXDIM];
980         int                     ndim,
981                            *dims,
982                            *lb;
983         ArrayMetaState *my_extra;
984
985         /*
986          * We arrange to look up info about element type, including its output
987          * conversion proc, only once per series of calls, assuming the element
988          * type doesn't change underneath us.
989          */
990         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
991         if (my_extra == NULL)
992         {
993                 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
994                                                                                                           sizeof(ArrayMetaState));
995                 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
996                 my_extra->element_type = ~element_type;
997         }
998
999         if (my_extra->element_type != element_type)
1000         {
1001                 /*
1002                  * Get info about element type, including its output conversion proc
1003                  */
1004                 get_type_io_data(element_type, IOFunc_output,
1005                                                  &my_extra->typlen, &my_extra->typbyval,
1006                                                  &my_extra->typalign, &my_extra->typdelim,
1007                                                  &my_extra->typioparam, &my_extra->typiofunc);
1008                 fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1009                                           fcinfo->flinfo->fn_mcxt);
1010                 my_extra->element_type = element_type;
1011         }
1012         typlen = my_extra->typlen;
1013         typbyval = my_extra->typbyval;
1014         typalign = my_extra->typalign;
1015         typdelim = my_extra->typdelim;
1016
1017         ndim = ARR_NDIM(v);
1018         dims = ARR_DIMS(v);
1019         lb = ARR_LBOUND(v);
1020         nitems = ArrayGetNItems(ndim, dims);
1021
1022         if (nitems == 0)
1023         {
1024                 retval = pstrdup("{}");
1025                 PG_RETURN_CSTRING(retval);
1026         }
1027
1028         /*
1029          * we will need to add explicit dimensions if any dimension has a lower
1030          * bound other than one
1031          */
1032         for (i = 0; i < ndim; i++)
1033         {
1034                 if (lb[i] != 1)
1035                 {
1036                         needdims = true;
1037                         break;
1038                 }
1039         }
1040
1041         /*
1042          * Convert all values to string form, count total space needed (including
1043          * any overhead such as escaping backslashes), and detect whether each
1044          * item needs double quotes.
1045          */
1046         values = (char **) palloc(nitems * sizeof(char *));
1047         needquotes = (bool *) palloc(nitems * sizeof(bool));
1048         overall_length = 1;                     /* don't forget to count \0 at end. */
1049
1050         p = ARR_DATA_PTR(v);
1051         bitmap = ARR_NULLBITMAP(v);
1052         bitmask = 1;
1053
1054         for (i = 0; i < nitems; i++)
1055         {
1056                 bool            needquote;
1057
1058                 /* Get source element, checking for NULL */
1059                 if (bitmap && (*bitmap & bitmask) == 0)
1060                 {
1061                         values[i] = pstrdup("NULL");
1062                         overall_length += 4;
1063                         needquote = false;
1064                 }
1065                 else
1066                 {
1067                         Datum           itemvalue;
1068
1069                         itemvalue = fetch_att(p, typbyval, typlen);
1070                         values[i] = OutputFunctionCall(&my_extra->proc, itemvalue);
1071                         p = att_addlength_pointer(p, typlen, p);
1072                         p = (char *) att_align_nominal(p, typalign);
1073
1074                         /* count data plus backslashes; detect chars needing quotes */
1075                         if (values[i][0] == '\0')
1076                                 needquote = true;               /* force quotes for empty string */
1077                         else if (pg_strcasecmp(values[i], "NULL") == 0)
1078                                 needquote = true;               /* force quotes for literal NULL */
1079                         else
1080                                 needquote = false;
1081
1082                         for (tmp = values[i]; *tmp != '\0'; tmp++)
1083                         {
1084                                 char            ch = *tmp;
1085
1086                                 overall_length += 1;
1087                                 if (ch == '"' || ch == '\\')
1088                                 {
1089                                         needquote = true;
1090                                         overall_length += 1;
1091                                 }
1092                                 else if (ch == '{' || ch == '}' || ch == typdelim ||
1093                                                  array_isspace(ch))
1094                                         needquote = true;
1095                         }
1096                 }
1097
1098                 needquotes[i] = needquote;
1099
1100                 /* Count the pair of double quotes, if needed */
1101                 if (needquote)
1102                         overall_length += 2;
1103                 /* and the comma */
1104                 overall_length += 1;
1105
1106                 /* advance bitmap pointer if any */
1107                 if (bitmap)
1108                 {
1109                         bitmask <<= 1;
1110                         if (bitmask == 0x100)
1111                         {
1112                                 bitmap++;
1113                                 bitmask = 1;
1114                         }
1115                 }
1116         }
1117
1118         /*
1119          * count total number of curly braces in output string
1120          */
1121         for (i = j = 0, k = 1; i < ndim; i++)
1122                 k *= dims[i], j += k;
1123
1124         dims_str[0] = '\0';
1125
1126         /* add explicit dimensions if required */
1127         if (needdims)
1128         {
1129                 char       *ptr = dims_str;
1130
1131                 for (i = 0; i < ndim; i++)
1132                 {
1133                         sprintf(ptr, "[%d:%d]", lb[i], lb[i] + dims[i] - 1);
1134                         ptr += strlen(ptr);
1135                 }
1136                 *ptr++ = *ASSGN;
1137                 *ptr = '\0';
1138         }
1139
1140         retval = (char *) palloc(strlen(dims_str) + overall_length + 2 * j);
1141         p = retval;
1142
1143 #define APPENDSTR(str)  (strcpy(p, (str)), p += strlen(p))
1144 #define APPENDCHAR(ch)  (*p++ = (ch), *p = '\0')
1145
1146         if (needdims)
1147                 APPENDSTR(dims_str);
1148         APPENDCHAR('{');
1149         for (i = 0; i < ndim; i++)
1150                 indx[i] = 0;
1151         j = 0;
1152         k = 0;
1153         do
1154         {
1155                 for (i = j; i < ndim - 1; i++)
1156                         APPENDCHAR('{');
1157
1158                 if (needquotes[k])
1159                 {
1160                         APPENDCHAR('"');
1161                         for (tmp = values[k]; *tmp; tmp++)
1162                         {
1163                                 char            ch = *tmp;
1164
1165                                 if (ch == '"' || ch == '\\')
1166                                         *p++ = '\\';
1167                                 *p++ = ch;
1168                         }
1169                         *p = '\0';
1170                         APPENDCHAR('"');
1171                 }
1172                 else
1173                         APPENDSTR(values[k]);
1174                 pfree(values[k++]);
1175
1176                 for (i = ndim - 1; i >= 0; i--)
1177                 {
1178                         indx[i] = (indx[i] + 1) % dims[i];
1179                         if (indx[i])
1180                         {
1181                                 APPENDCHAR(typdelim);
1182                                 break;
1183                         }
1184                         else
1185                                 APPENDCHAR('}');
1186                 }
1187                 j = i;
1188         } while (j != -1);
1189
1190 #undef APPENDSTR
1191 #undef APPENDCHAR
1192
1193         pfree(values);
1194         pfree(needquotes);
1195
1196         PG_RETURN_CSTRING(retval);
1197 }
1198
1199 /*
1200  * array_recv :
1201  *                converts an array from the external binary format to
1202  *                its internal format.
1203  *
1204  * return value :
1205  *                the internal representation of the input array
1206  */
1207 Datum
1208 array_recv(PG_FUNCTION_ARGS)
1209 {
1210         StringInfo      buf = (StringInfo) PG_GETARG_POINTER(0);
1211         Oid                     spec_element_type = PG_GETARG_OID(1);   /* type of an array
1212                                                                                                                  * element */
1213         int32           typmod = PG_GETARG_INT32(2);    /* typmod for array elements */
1214         Oid                     element_type;
1215         int                     typlen;
1216         bool            typbyval;
1217         char            typalign;
1218         Oid                     typioparam;
1219         int                     i,
1220                                 nitems;
1221         Datum      *dataPtr;
1222         bool       *nullsPtr;
1223         bool            hasnulls;
1224         int32           nbytes;
1225         int32           dataoffset;
1226         ArrayType  *retval;
1227         int                     ndim,
1228                                 flags,
1229                                 dim[MAXDIM],
1230                                 lBound[MAXDIM];
1231         ArrayMetaState *my_extra;
1232
1233         /* Get the array header information */
1234         ndim = pq_getmsgint(buf, 4);
1235         if (ndim < 0)                           /* we do allow zero-dimension arrays */
1236                 ereport(ERROR,
1237                                 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1238                                  errmsg("invalid number of dimensions: %d", ndim)));
1239         if (ndim > MAXDIM)
1240                 ereport(ERROR,
1241                                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1242                                  errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
1243                                                 ndim, MAXDIM)));
1244
1245         flags = pq_getmsgint(buf, 4);
1246         if (flags != 0 && flags != 1)
1247                 ereport(ERROR,
1248                                 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1249                                  errmsg("invalid array flags")));
1250
1251         element_type = pq_getmsgint(buf, sizeof(Oid));
1252         if (element_type != spec_element_type)
1253         {
1254                 /* XXX Can we allow taking the input element type in any cases? */
1255                 ereport(ERROR,
1256                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
1257                                  errmsg("wrong element type")));
1258         }
1259
1260         for (i = 0; i < ndim; i++)
1261         {
1262                 dim[i] = pq_getmsgint(buf, 4);
1263                 lBound[i] = pq_getmsgint(buf, 4);
1264
1265                 /*
1266                  * Check overflow of upper bound. (ArrayNItems() below checks that
1267                  * dim[i] >= 0)
1268                  */
1269                 if (dim[i] != 0)
1270                 {
1271                         int                     ub = lBound[i] + dim[i] - 1;
1272
1273                         if (lBound[i] > ub)
1274                                 ereport(ERROR,
1275                                                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
1276                                                  errmsg("integer out of range")));
1277                 }
1278         }
1279
1280         /* This checks for overflow of array dimensions */
1281         nitems = ArrayGetNItems(ndim, dim);
1282
1283         /*
1284          * We arrange to look up info about element type, including its receive
1285          * conversion proc, only once per series of calls, assuming the element
1286          * type doesn't change underneath us.
1287          */
1288         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1289         if (my_extra == NULL)
1290         {
1291                 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1292                                                                                                           sizeof(ArrayMetaState));
1293                 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1294                 my_extra->element_type = ~element_type;
1295         }
1296
1297         if (my_extra->element_type != element_type)
1298         {
1299                 /* Get info about element type, including its receive proc */
1300                 get_type_io_data(element_type, IOFunc_receive,
1301                                                  &my_extra->typlen, &my_extra->typbyval,
1302                                                  &my_extra->typalign, &my_extra->typdelim,
1303                                                  &my_extra->typioparam, &my_extra->typiofunc);
1304                 if (!OidIsValid(my_extra->typiofunc))
1305                         ereport(ERROR,
1306                                         (errcode(ERRCODE_UNDEFINED_FUNCTION),
1307                                          errmsg("no binary input function available for type %s",
1308                                                         format_type_be(element_type))));
1309                 fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1310                                           fcinfo->flinfo->fn_mcxt);
1311                 my_extra->element_type = element_type;
1312         }
1313
1314         if (nitems == 0)
1315         {
1316                 /* Return empty array ... but not till we've validated element_type */
1317                 PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
1318         }
1319
1320         typlen = my_extra->typlen;
1321         typbyval = my_extra->typbyval;
1322         typalign = my_extra->typalign;
1323         typioparam = my_extra->typioparam;
1324
1325         dataPtr = (Datum *) palloc(nitems * sizeof(Datum));
1326         nullsPtr = (bool *) palloc(nitems * sizeof(bool));
1327         ReadArrayBinary(buf, nitems,
1328                                         &my_extra->proc, typioparam, typmod,
1329                                         typlen, typbyval, typalign,
1330                                         dataPtr, nullsPtr,
1331                                         &hasnulls, &nbytes);
1332         if (hasnulls)
1333         {
1334                 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
1335                 nbytes += dataoffset;
1336         }
1337         else
1338         {
1339                 dataoffset = 0;                 /* marker for no null bitmap */
1340                 nbytes += ARR_OVERHEAD_NONULLS(ndim);
1341         }
1342         retval = (ArrayType *) palloc0(nbytes);
1343         SET_VARSIZE(retval, nbytes);
1344         retval->ndim = ndim;
1345         retval->dataoffset = dataoffset;
1346         retval->elemtype = element_type;
1347         memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
1348         memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
1349
1350         CopyArrayEls(retval,
1351                                  dataPtr, nullsPtr, nitems,
1352                                  typlen, typbyval, typalign,
1353                                  true);
1354
1355         pfree(dataPtr);
1356         pfree(nullsPtr);
1357
1358         PG_RETURN_ARRAYTYPE_P(retval);
1359 }
1360
1361 /*
1362  * ReadArrayBinary:
1363  *       collect the data elements of an array being read in binary style.
1364  *
1365  * Inputs:
1366  *      buf: the data buffer to read from.
1367  *      nitems: total number of array elements (already read).
1368  *      receiveproc: type-specific receive procedure for element datatype.
1369  *      typioparam, typmod: auxiliary values to pass to receiveproc.
1370  *      typlen, typbyval, typalign: storage parameters of element datatype.
1371  *
1372  * Outputs:
1373  *      values[]: filled with converted data values.
1374  *      nulls[]: filled with is-null markers.
1375  *      *hasnulls: set TRUE iff there are any null elements.
1376  *      *nbytes: set to total size of data area needed (including alignment
1377  *              padding but not including array header overhead).
1378  *
1379  * Note that values[] and nulls[] are allocated by the caller, and must have
1380  * nitems elements.
1381  */
1382 static void
1383 ReadArrayBinary(StringInfo buf,
1384                                 int nitems,
1385                                 FmgrInfo *receiveproc,
1386                                 Oid typioparam,
1387                                 int32 typmod,
1388                                 int typlen,
1389                                 bool typbyval,
1390                                 char typalign,
1391                                 Datum *values,
1392                                 bool *nulls,
1393                                 bool *hasnulls,
1394                                 int32 *nbytes)
1395 {
1396         int                     i;
1397         bool            hasnull;
1398         int32           totbytes;
1399
1400         for (i = 0; i < nitems; i++)
1401         {
1402                 int                     itemlen;
1403                 StringInfoData elem_buf;
1404                 char            csave;
1405
1406                 /* Get and check the item length */
1407                 itemlen = pq_getmsgint(buf, 4);
1408                 if (itemlen < -1 || itemlen > (buf->len - buf->cursor))
1409                         ereport(ERROR,
1410                                         (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1411                                          errmsg("insufficient data left in message")));
1412
1413                 if (itemlen == -1)
1414                 {
1415                         /* -1 length means NULL */
1416                         values[i] = ReceiveFunctionCall(receiveproc, NULL,
1417                                                                                         typioparam, typmod);
1418                         nulls[i] = true;
1419                         continue;
1420                 }
1421
1422                 /*
1423                  * Rather than copying data around, we just set up a phony StringInfo
1424                  * pointing to the correct portion of the input buffer. We assume we
1425                  * can scribble on the input buffer so as to maintain the convention
1426                  * that StringInfos have a trailing null.
1427                  */
1428                 elem_buf.data = &buf->data[buf->cursor];
1429                 elem_buf.maxlen = itemlen + 1;
1430                 elem_buf.len = itemlen;
1431                 elem_buf.cursor = 0;
1432
1433                 buf->cursor += itemlen;
1434
1435                 csave = buf->data[buf->cursor];
1436                 buf->data[buf->cursor] = '\0';
1437
1438                 /* Now call the element's receiveproc */
1439                 values[i] = ReceiveFunctionCall(receiveproc, &elem_buf,
1440                                                                                 typioparam, typmod);
1441                 nulls[i] = false;
1442
1443                 /* Trouble if it didn't eat the whole buffer */
1444                 if (elem_buf.cursor != itemlen)
1445                         ereport(ERROR,
1446                                         (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1447                                          errmsg("improper binary format in array element %d",
1448                                                         i + 1)));
1449
1450                 buf->data[buf->cursor] = csave;
1451         }
1452
1453         /*
1454          * Check for nulls, compute total data space needed
1455          */
1456         hasnull = false;
1457         totbytes = 0;
1458         for (i = 0; i < nitems; i++)
1459         {
1460                 if (nulls[i])
1461                         hasnull = true;
1462                 else
1463                 {
1464                         /* let's just make sure data is not toasted */
1465                         if (typlen == -1)
1466                                 values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
1467                         totbytes = att_addlength_datum(totbytes, typlen, values[i]);
1468                         totbytes = att_align_nominal(totbytes, typalign);
1469                         /* check for overflow of total request */
1470                         if (!AllocSizeIsValid(totbytes))
1471                                 ereport(ERROR,
1472                                                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1473                                                  errmsg("array size exceeds the maximum allowed (%d)",
1474                                                                 (int) MaxAllocSize)));
1475                 }
1476         }
1477         *hasnulls = hasnull;
1478         *nbytes = totbytes;
1479 }
1480
1481
1482 /*
1483  * array_send :
1484  *                takes the internal representation of an array and returns a bytea
1485  *                containing the array in its external binary format.
1486  */
1487 Datum
1488 array_send(PG_FUNCTION_ARGS)
1489 {
1490         ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
1491         Oid                     element_type = ARR_ELEMTYPE(v);
1492         int                     typlen;
1493         bool            typbyval;
1494         char            typalign;
1495         char       *p;
1496         bits8      *bitmap;
1497         int                     bitmask;
1498         int                     nitems,
1499                                 i;
1500         int                     ndim,
1501                            *dim;
1502         StringInfoData buf;
1503         ArrayMetaState *my_extra;
1504
1505         /*
1506          * We arrange to look up info about element type, including its send
1507          * conversion proc, only once per series of calls, assuming the element
1508          * type doesn't change underneath us.
1509          */
1510         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1511         if (my_extra == NULL)
1512         {
1513                 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1514                                                                                                           sizeof(ArrayMetaState));
1515                 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1516                 my_extra->element_type = ~element_type;
1517         }
1518
1519         if (my_extra->element_type != element_type)
1520         {
1521                 /* Get info about element type, including its send proc */
1522                 get_type_io_data(element_type, IOFunc_send,
1523                                                  &my_extra->typlen, &my_extra->typbyval,
1524                                                  &my_extra->typalign, &my_extra->typdelim,
1525                                                  &my_extra->typioparam, &my_extra->typiofunc);
1526                 if (!OidIsValid(my_extra->typiofunc))
1527                         ereport(ERROR,
1528                                         (errcode(ERRCODE_UNDEFINED_FUNCTION),
1529                                          errmsg("no binary output function available for type %s",
1530                                                         format_type_be(element_type))));
1531                 fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1532                                           fcinfo->flinfo->fn_mcxt);
1533                 my_extra->element_type = element_type;
1534         }
1535         typlen = my_extra->typlen;
1536         typbyval = my_extra->typbyval;
1537         typalign = my_extra->typalign;
1538
1539         ndim = ARR_NDIM(v);
1540         dim = ARR_DIMS(v);
1541         nitems = ArrayGetNItems(ndim, dim);
1542
1543         pq_begintypsend(&buf);
1544
1545         /* Send the array header information */
1546         pq_sendint(&buf, ndim, 4);
1547         pq_sendint(&buf, ARR_HASNULL(v) ? 1 : 0, 4);
1548         pq_sendint(&buf, element_type, sizeof(Oid));
1549         for (i = 0; i < ndim; i++)
1550         {
1551                 pq_sendint(&buf, ARR_DIMS(v)[i], 4);
1552                 pq_sendint(&buf, ARR_LBOUND(v)[i], 4);
1553         }
1554
1555         /* Send the array elements using the element's own sendproc */
1556         p = ARR_DATA_PTR(v);
1557         bitmap = ARR_NULLBITMAP(v);
1558         bitmask = 1;
1559
1560         for (i = 0; i < nitems; i++)
1561         {
1562                 /* Get source element, checking for NULL */
1563                 if (bitmap && (*bitmap & bitmask) == 0)
1564                 {
1565                         /* -1 length means a NULL */
1566                         pq_sendint(&buf, -1, 4);
1567                 }
1568                 else
1569                 {
1570                         Datum           itemvalue;
1571                         bytea      *outputbytes;
1572
1573                         itemvalue = fetch_att(p, typbyval, typlen);
1574                         outputbytes = SendFunctionCall(&my_extra->proc, itemvalue);
1575                         pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
1576                         pq_sendbytes(&buf, VARDATA(outputbytes),
1577                                                  VARSIZE(outputbytes) - VARHDRSZ);
1578                         pfree(outputbytes);
1579
1580                         p = att_addlength_pointer(p, typlen, p);
1581                         p = (char *) att_align_nominal(p, typalign);
1582                 }
1583
1584                 /* advance bitmap pointer if any */
1585                 if (bitmap)
1586                 {
1587                         bitmask <<= 1;
1588                         if (bitmask == 0x100)
1589                         {
1590                                 bitmap++;
1591                                 bitmask = 1;
1592                         }
1593                 }
1594         }
1595
1596         PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
1597 }
1598
1599 /*
1600  * array_ndims :
1601  *                returns the number of dimensions of the array pointed to by "v"
1602  */
1603 Datum
1604 array_ndims(PG_FUNCTION_ARGS)
1605 {
1606         ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
1607
1608         /* Sanity check: does it look like an array at all? */
1609         if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1610                 PG_RETURN_NULL();
1611
1612         PG_RETURN_INT32(ARR_NDIM(v));
1613 }
1614
1615 /*
1616  * array_dims :
1617  *                returns the dimensions of the array pointed to by "v", as a "text"
1618  */
1619 Datum
1620 array_dims(PG_FUNCTION_ARGS)
1621 {
1622         ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
1623         char       *p;
1624         int                     i;
1625         int                *dimv,
1626                            *lb;
1627
1628         /*
1629          * 33 since we assume 15 digits per number + ':' +'[]'
1630          *
1631          * +1 for trailing null
1632          */
1633         char            buf[MAXDIM * 33 + 1];
1634
1635         /* Sanity check: does it look like an array at all? */
1636         if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1637                 PG_RETURN_NULL();
1638
1639         dimv = ARR_DIMS(v);
1640         lb = ARR_LBOUND(v);
1641
1642         p = buf;
1643         for (i = 0; i < ARR_NDIM(v); i++)
1644         {
1645                 sprintf(p, "[%d:%d]", lb[i], dimv[i] + lb[i] - 1);
1646                 p += strlen(p);
1647         }
1648
1649         PG_RETURN_TEXT_P(cstring_to_text(buf));
1650 }
1651
1652 /*
1653  * array_lower :
1654  *              returns the lower dimension, of the DIM requested, for
1655  *              the array pointed to by "v", as an int4
1656  */
1657 Datum
1658 array_lower(PG_FUNCTION_ARGS)
1659 {
1660         ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
1661         int                     reqdim = PG_GETARG_INT32(1);
1662         int                *lb;
1663         int                     result;
1664
1665         /* Sanity check: does it look like an array at all? */
1666         if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1667                 PG_RETURN_NULL();
1668
1669         /* Sanity check: was the requested dim valid */
1670         if (reqdim <= 0 || reqdim > ARR_NDIM(v))
1671                 PG_RETURN_NULL();
1672
1673         lb = ARR_LBOUND(v);
1674         result = lb[reqdim - 1];
1675
1676         PG_RETURN_INT32(result);
1677 }
1678
1679 /*
1680  * array_upper :
1681  *              returns the upper dimension, of the DIM requested, for
1682  *              the array pointed to by "v", as an int4
1683  */
1684 Datum
1685 array_upper(PG_FUNCTION_ARGS)
1686 {
1687         ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
1688         int                     reqdim = PG_GETARG_INT32(1);
1689         int                *dimv,
1690                            *lb;
1691         int                     result;
1692
1693         /* Sanity check: does it look like an array at all? */
1694         if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1695                 PG_RETURN_NULL();
1696
1697         /* Sanity check: was the requested dim valid */
1698         if (reqdim <= 0 || reqdim > ARR_NDIM(v))
1699                 PG_RETURN_NULL();
1700
1701         lb = ARR_LBOUND(v);
1702         dimv = ARR_DIMS(v);
1703
1704         result = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
1705
1706         PG_RETURN_INT32(result);
1707 }
1708
1709 /*
1710  * array_length :
1711  *              returns the length, of the dimension requested, for
1712  *              the array pointed to by "v", as an int4
1713  */
1714 Datum
1715 array_length(PG_FUNCTION_ARGS)
1716 {
1717         ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
1718         int                     reqdim = PG_GETARG_INT32(1);
1719         int                *dimv;
1720         int                     result;
1721
1722         /* Sanity check: does it look like an array at all? */
1723         if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1724                 PG_RETURN_NULL();
1725
1726         /* Sanity check: was the requested dim valid */
1727         if (reqdim <= 0 || reqdim > ARR_NDIM(v))
1728                 PG_RETURN_NULL();
1729
1730         dimv = ARR_DIMS(v);
1731
1732         result = dimv[reqdim - 1];
1733
1734         PG_RETURN_INT32(result);
1735 }
1736
1737 /*
1738  * array_ref :
1739  *        This routine takes an array pointer and a subscript array and returns
1740  *        the referenced item as a Datum.  Note that for a pass-by-reference
1741  *        datatype, the returned Datum is a pointer into the array object.
1742  *
1743  * This handles both ordinary varlena arrays and fixed-length arrays.
1744  *
1745  * Inputs:
1746  *      array: the array object (mustn't be NULL)
1747  *      nSubscripts: number of subscripts supplied
1748  *      indx[]: the subscript values
1749  *      arraytyplen: pg_type.typlen for the array type
1750  *      elmlen: pg_type.typlen for the array's element type
1751  *      elmbyval: pg_type.typbyval for the array's element type
1752  *      elmalign: pg_type.typalign for the array's element type
1753  *
1754  * Outputs:
1755  *      The return value is the element Datum.
1756  *      *isNull is set to indicate whether the element is NULL.
1757  */
1758 Datum
1759 array_ref(ArrayType *array,
1760                   int nSubscripts,
1761                   int *indx,
1762                   int arraytyplen,
1763                   int elmlen,
1764                   bool elmbyval,
1765                   char elmalign,
1766                   bool *isNull)
1767 {
1768         int                     i,
1769                                 ndim,
1770                            *dim,
1771                            *lb,
1772                                 offset,
1773                                 fixedDim[1],
1774                                 fixedLb[1];
1775         char       *arraydataptr,
1776                            *retptr;
1777         bits8      *arraynullsptr;
1778
1779         if (arraytyplen > 0)
1780         {
1781                 /*
1782                  * fixed-length arrays -- these are assumed to be 1-d, 0-based
1783                  */
1784                 ndim = 1;
1785                 fixedDim[0] = arraytyplen / elmlen;
1786                 fixedLb[0] = 0;
1787                 dim = fixedDim;
1788                 lb = fixedLb;
1789                 arraydataptr = (char *) array;
1790                 arraynullsptr = NULL;
1791         }
1792         else
1793         {
1794                 /* detoast input array if necessary */
1795                 array = DatumGetArrayTypeP(PointerGetDatum(array));
1796
1797                 ndim = ARR_NDIM(array);
1798                 dim = ARR_DIMS(array);
1799                 lb = ARR_LBOUND(array);
1800                 arraydataptr = ARR_DATA_PTR(array);
1801                 arraynullsptr = ARR_NULLBITMAP(array);
1802         }
1803
1804         /*
1805          * Return NULL for invalid subscript
1806          */
1807         if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
1808         {
1809                 *isNull = true;
1810                 return (Datum) 0;
1811         }
1812         for (i = 0; i < ndim; i++)
1813         {
1814                 if (indx[i] < lb[i] || indx[i] >= (dim[i] + lb[i]))
1815                 {
1816                         *isNull = true;
1817                         return (Datum) 0;
1818                 }
1819         }
1820
1821         /*
1822          * Calculate the element number
1823          */
1824         offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
1825
1826         /*
1827          * Check for NULL array element
1828          */
1829         if (array_get_isnull(arraynullsptr, offset))
1830         {
1831                 *isNull = true;
1832                 return (Datum) 0;
1833         }
1834
1835         /*
1836          * OK, get the element
1837          */
1838         *isNull = false;
1839         retptr = array_seek(arraydataptr, 0, arraynullsptr, offset,
1840                                                 elmlen, elmbyval, elmalign);
1841         return ArrayCast(retptr, elmbyval, elmlen);
1842 }
1843
1844 /*
1845  * array_get_slice :
1846  *                 This routine takes an array and a range of indices (upperIndex and
1847  *                 lowerIndx), creates a new array structure for the referred elements
1848  *                 and returns a pointer to it.
1849  *
1850  * This handles both ordinary varlena arrays and fixed-length arrays.
1851  *
1852  * Inputs:
1853  *      array: the array object (mustn't be NULL)
1854  *      nSubscripts: number of subscripts supplied (must be same for upper/lower)
1855  *      upperIndx[]: the upper subscript values
1856  *      lowerIndx[]: the lower subscript values
1857  *      arraytyplen: pg_type.typlen for the array type
1858  *      elmlen: pg_type.typlen for the array's element type
1859  *      elmbyval: pg_type.typbyval for the array's element type
1860  *      elmalign: pg_type.typalign for the array's element type
1861  *
1862  * Outputs:
1863  *      The return value is the new array Datum (it's never NULL)
1864  *
1865  * NOTE: we assume it is OK to scribble on the provided subscript arrays
1866  * lowerIndx[] and upperIndx[].  These are generally just temporaries.
1867  */
1868 ArrayType *
1869 array_get_slice(ArrayType *array,
1870                                 int nSubscripts,
1871                                 int *upperIndx,
1872                                 int *lowerIndx,
1873                                 int arraytyplen,
1874                                 int elmlen,
1875                                 bool elmbyval,
1876                                 char elmalign)
1877 {
1878         ArrayType  *newarray;
1879         int                     i,
1880                                 ndim,
1881                            *dim,
1882                            *lb,
1883                            *newlb;
1884         int                     fixedDim[1],
1885                                 fixedLb[1];
1886         Oid                     elemtype;
1887         char       *arraydataptr;
1888         bits8      *arraynullsptr;
1889         int32           dataoffset;
1890         int                     bytes,
1891                                 span[MAXDIM];
1892
1893         if (arraytyplen > 0)
1894         {
1895                 /*
1896                  * fixed-length arrays -- currently, cannot slice these because parser
1897                  * labels output as being of the fixed-length array type! Code below
1898                  * shows how we could support it if the parser were changed to label
1899                  * output as a suitable varlena array type.
1900                  */
1901                 ereport(ERROR,
1902                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1903                                  errmsg("slices of fixed-length arrays not implemented")));
1904
1905                 /*
1906                  * fixed-length arrays -- these are assumed to be 1-d, 0-based
1907                  *
1908                  * XXX where would we get the correct ELEMTYPE from?
1909                  */
1910                 ndim = 1;
1911                 fixedDim[0] = arraytyplen / elmlen;
1912                 fixedLb[0] = 0;
1913                 dim = fixedDim;
1914                 lb = fixedLb;
1915                 elemtype = InvalidOid;  /* XXX */
1916                 arraydataptr = (char *) array;
1917                 arraynullsptr = NULL;
1918         }
1919         else
1920         {
1921                 /* detoast input array if necessary */
1922                 array = DatumGetArrayTypeP(PointerGetDatum(array));
1923
1924                 ndim = ARR_NDIM(array);
1925                 dim = ARR_DIMS(array);
1926                 lb = ARR_LBOUND(array);
1927                 elemtype = ARR_ELEMTYPE(array);
1928                 arraydataptr = ARR_DATA_PTR(array);
1929                 arraynullsptr = ARR_NULLBITMAP(array);
1930         }
1931
1932         /*
1933          * Check provided subscripts.  A slice exceeding the current array limits
1934          * is silently truncated to the array limits.  If we end up with an empty
1935          * slice, return an empty array.
1936          */
1937         if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
1938                 return construct_empty_array(elemtype);
1939
1940         for (i = 0; i < nSubscripts; i++)
1941         {
1942                 if (lowerIndx[i] < lb[i])
1943                         lowerIndx[i] = lb[i];
1944                 if (upperIndx[i] >= (dim[i] + lb[i]))
1945                         upperIndx[i] = dim[i] + lb[i] - 1;
1946                 if (lowerIndx[i] > upperIndx[i])
1947                         return construct_empty_array(elemtype);
1948         }
1949         /* fill any missing subscript positions with full array range */
1950         for (; i < ndim; i++)
1951         {
1952                 lowerIndx[i] = lb[i];
1953                 upperIndx[i] = dim[i] + lb[i] - 1;
1954                 if (lowerIndx[i] > upperIndx[i])
1955                         return construct_empty_array(elemtype);
1956         }
1957
1958         mda_get_range(ndim, span, lowerIndx, upperIndx);
1959
1960         bytes = array_slice_size(arraydataptr, arraynullsptr,
1961                                                          ndim, dim, lb,
1962                                                          lowerIndx, upperIndx,
1963                                                          elmlen, elmbyval, elmalign);
1964
1965         /*
1966          * Currently, we put a null bitmap in the result if the source has one;
1967          * could be smarter ...
1968          */
1969         if (arraynullsptr)
1970         {
1971                 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, ArrayGetNItems(ndim, span));
1972                 bytes += dataoffset;
1973         }
1974         else
1975         {
1976                 dataoffset = 0;                 /* marker for no null bitmap */
1977                 bytes += ARR_OVERHEAD_NONULLS(ndim);
1978         }
1979
1980         newarray = (ArrayType *) palloc0(bytes);
1981         SET_VARSIZE(newarray, bytes);
1982         newarray->ndim = ndim;
1983         newarray->dataoffset = dataoffset;
1984         newarray->elemtype = elemtype;
1985         memcpy(ARR_DIMS(newarray), span, ndim * sizeof(int));
1986
1987         /*
1988          * Lower bounds of the new array are set to 1.  Formerly (before 7.3) we
1989          * copied the given lowerIndx values ... but that seems confusing.
1990          */
1991         newlb = ARR_LBOUND(newarray);
1992         for (i = 0; i < ndim; i++)
1993                 newlb[i] = 1;
1994
1995         array_extract_slice(newarray,
1996                                                 ndim, dim, lb,
1997                                                 arraydataptr, arraynullsptr,
1998                                                 lowerIndx, upperIndx,
1999                                                 elmlen, elmbyval, elmalign);
2000
2001         return newarray;
2002 }
2003
2004 /*
2005  * array_set :
2006  *                This routine sets the value of an array element (specified by
2007  *                a subscript array) to a new value specified by "dataValue".
2008  *
2009  * This handles both ordinary varlena arrays and fixed-length arrays.
2010  *
2011  * Inputs:
2012  *      array: the initial array object (mustn't be NULL)
2013  *      nSubscripts: number of subscripts supplied
2014  *      indx[]: the subscript values
2015  *      dataValue: the datum to be inserted at the given position
2016  *      isNull: whether dataValue is NULL
2017  *      arraytyplen: pg_type.typlen for the array type
2018  *      elmlen: pg_type.typlen for the array's element type
2019  *      elmbyval: pg_type.typbyval for the array's element type
2020  *      elmalign: pg_type.typalign for the array's element type
2021  *
2022  * Result:
2023  *                A new array is returned, just like the old except for the one
2024  *                modified entry.  The original array object is not changed.
2025  *
2026  * For one-dimensional arrays only, we allow the array to be extended
2027  * by assigning to a position outside the existing subscript range; any
2028  * positions between the existing elements and the new one are set to NULLs.
2029  * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
2030  *
2031  * NOTE: For assignments, we throw an error for invalid subscripts etc,
2032  * rather than returning a NULL as the fetch operations do.
2033  */
2034 ArrayType *
2035 array_set(ArrayType *array,
2036                   int nSubscripts,
2037                   int *indx,
2038                   Datum dataValue,
2039                   bool isNull,
2040                   int arraytyplen,
2041                   int elmlen,
2042                   bool elmbyval,
2043                   char elmalign)
2044 {
2045         ArrayType  *newarray;
2046         int                     i,
2047                                 ndim,
2048                                 dim[MAXDIM],
2049                                 lb[MAXDIM],
2050                                 offset;
2051         char       *elt_ptr;
2052         bool            newhasnulls;
2053         bits8      *oldnullbitmap;
2054         int                     oldnitems,
2055                                 newnitems,
2056                                 olddatasize,
2057                                 newsize,
2058                                 olditemlen,
2059                                 newitemlen,
2060                                 overheadlen,
2061                                 oldoverheadlen,
2062                                 addedbefore,
2063                                 addedafter,
2064                                 lenbefore,
2065                                 lenafter;
2066
2067         if (arraytyplen > 0)
2068         {
2069                 /*
2070                  * fixed-length arrays -- these are assumed to be 1-d, 0-based. We
2071                  * cannot extend them, either.
2072                  */
2073                 if (nSubscripts != 1)
2074                         ereport(ERROR,
2075                                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2076                                          errmsg("wrong number of array subscripts")));
2077
2078                 if (indx[0] < 0 || indx[0] * elmlen >= arraytyplen)
2079                         ereport(ERROR,
2080                                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2081                                          errmsg("array subscript out of range")));
2082
2083                 if (isNull)
2084                         ereport(ERROR,
2085                                         (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
2086                                          errmsg("cannot assign null value to an element of a fixed-length array")));
2087
2088                 newarray = (ArrayType *) palloc(arraytyplen);
2089                 memcpy(newarray, array, arraytyplen);
2090                 elt_ptr = (char *) newarray + indx[0] * elmlen;
2091                 ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign, elt_ptr);
2092                 return newarray;
2093         }
2094
2095         if (nSubscripts <= 0 || nSubscripts > MAXDIM)
2096                 ereport(ERROR,
2097                                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2098                                  errmsg("wrong number of array subscripts")));
2099
2100         /* make sure item to be inserted is not toasted */
2101         if (elmlen == -1 && !isNull)
2102                 dataValue = PointerGetDatum(PG_DETOAST_DATUM(dataValue));
2103
2104         /* detoast input array if necessary */
2105         array = DatumGetArrayTypeP(PointerGetDatum(array));
2106
2107         ndim = ARR_NDIM(array);
2108
2109         /*
2110          * if number of dims is zero, i.e. an empty array, create an array with
2111          * nSubscripts dimensions, and set the lower bounds to the supplied
2112          * subscripts
2113          */
2114         if (ndim == 0)
2115         {
2116                 Oid                     elmtype = ARR_ELEMTYPE(array);
2117
2118                 for (i = 0; i < nSubscripts; i++)
2119                 {
2120                         dim[i] = 1;
2121                         lb[i] = indx[i];
2122                 }
2123
2124                 return construct_md_array(&dataValue, &isNull, nSubscripts,
2125                                                                   dim, lb, elmtype,
2126                                                                   elmlen, elmbyval, elmalign);
2127         }
2128
2129         if (ndim != nSubscripts)
2130                 ereport(ERROR,
2131                                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2132                                  errmsg("wrong number of array subscripts")));
2133
2134         /* copy dim/lb since we may modify them */
2135         memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
2136         memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
2137
2138         newhasnulls = (ARR_HASNULL(array) || isNull);
2139         addedbefore = addedafter = 0;
2140
2141         /*
2142          * Check subscripts
2143          */
2144         if (ndim == 1)
2145         {
2146                 if (indx[0] < lb[0])
2147                 {
2148                         addedbefore = lb[0] - indx[0];
2149                         dim[0] += addedbefore;
2150                         lb[0] = indx[0];
2151                         if (addedbefore > 1)
2152                                 newhasnulls = true;             /* will insert nulls */
2153                 }
2154                 if (indx[0] >= (dim[0] + lb[0]))
2155                 {
2156                         addedafter = indx[0] - (dim[0] + lb[0]) + 1;
2157                         dim[0] += addedafter;
2158                         if (addedafter > 1)
2159                                 newhasnulls = true;             /* will insert nulls */
2160                 }
2161         }
2162         else
2163         {
2164                 /*
2165                  * XXX currently we do not support extending multi-dimensional arrays
2166                  * during assignment
2167                  */
2168                 for (i = 0; i < ndim; i++)
2169                 {
2170                         if (indx[i] < lb[i] ||
2171                                 indx[i] >= (dim[i] + lb[i]))
2172                                 ereport(ERROR,
2173                                                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2174                                                  errmsg("array subscript out of range")));
2175                 }
2176         }
2177
2178         /*
2179          * Compute sizes of items and areas to copy
2180          */
2181         newnitems = ArrayGetNItems(ndim, dim);
2182         if (newhasnulls)
2183                 overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, newnitems);
2184         else
2185                 overheadlen = ARR_OVERHEAD_NONULLS(ndim);
2186         oldnitems = ArrayGetNItems(ndim, ARR_DIMS(array));
2187         oldnullbitmap = ARR_NULLBITMAP(array);
2188         oldoverheadlen = ARR_DATA_OFFSET(array);
2189         olddatasize = ARR_SIZE(array) - oldoverheadlen;
2190         if (addedbefore)
2191         {
2192                 offset = 0;
2193                 lenbefore = 0;
2194                 olditemlen = 0;
2195                 lenafter = olddatasize;
2196         }
2197         else if (addedafter)
2198         {
2199                 offset = oldnitems;
2200                 lenbefore = olddatasize;
2201                 olditemlen = 0;
2202                 lenafter = 0;
2203         }
2204         else
2205         {
2206                 offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
2207                 elt_ptr = array_seek(ARR_DATA_PTR(array), 0, oldnullbitmap, offset,
2208                                                          elmlen, elmbyval, elmalign);
2209                 lenbefore = (int) (elt_ptr - ARR_DATA_PTR(array));
2210                 if (array_get_isnull(oldnullbitmap, offset))
2211                         olditemlen = 0;
2212                 else
2213                 {
2214                         olditemlen = att_addlength_pointer(0, elmlen, elt_ptr);
2215                         olditemlen = att_align_nominal(olditemlen, elmalign);
2216                 }
2217                 lenafter = (int) (olddatasize - lenbefore - olditemlen);
2218         }
2219
2220         if (isNull)
2221                 newitemlen = 0;
2222         else
2223         {
2224                 newitemlen = att_addlength_datum(0, elmlen, dataValue);
2225                 newitemlen = att_align_nominal(newitemlen, elmalign);
2226         }
2227
2228         newsize = overheadlen + lenbefore + newitemlen + lenafter;
2229
2230         /*
2231          * OK, create the new array and fill in header/dimensions
2232          */
2233         newarray = (ArrayType *) palloc0(newsize);
2234         SET_VARSIZE(newarray, newsize);
2235         newarray->ndim = ndim;
2236         newarray->dataoffset = newhasnulls ? overheadlen : 0;
2237         newarray->elemtype = ARR_ELEMTYPE(array);
2238         memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
2239         memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
2240
2241         /*
2242          * Fill in data
2243          */
2244         memcpy((char *) newarray + overheadlen,
2245                    (char *) array + oldoverheadlen,
2246                    lenbefore);
2247         if (!isNull)
2248                 ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign,
2249                                                 (char *) newarray + overheadlen + lenbefore);
2250         memcpy((char *) newarray + overheadlen + lenbefore + newitemlen,
2251                    (char *) array + oldoverheadlen + lenbefore + olditemlen,
2252                    lenafter);
2253
2254         /*
2255          * Fill in nulls bitmap if needed
2256          *
2257          * Note: it's possible we just replaced the last NULL with a non-NULL, and
2258          * could get rid of the bitmap.  Seems not worth testing for though.
2259          */
2260         if (newhasnulls)
2261         {
2262                 bits8      *newnullbitmap = ARR_NULLBITMAP(newarray);
2263
2264                 /* Zero the bitmap to take care of marking inserted positions null */
2265                 MemSet(newnullbitmap, 0, (newnitems + 7) / 8);
2266                 /* Fix the inserted value */
2267                 if (addedafter)
2268                         array_set_isnull(newnullbitmap, newnitems - 1, isNull);
2269                 else
2270                         array_set_isnull(newnullbitmap, offset, isNull);
2271                 /* Fix the copied range(s) */
2272                 if (addedbefore)
2273                         array_bitmap_copy(newnullbitmap, addedbefore,
2274                                                           oldnullbitmap, 0,
2275                                                           oldnitems);
2276                 else
2277                 {
2278                         array_bitmap_copy(newnullbitmap, 0,
2279                                                           oldnullbitmap, 0,
2280                                                           offset);
2281                         if (addedafter == 0)
2282                                 array_bitmap_copy(newnullbitmap, offset + 1,
2283                                                                   oldnullbitmap, offset + 1,
2284                                                                   oldnitems - offset - 1);
2285                 }
2286         }
2287
2288         return newarray;
2289 }
2290
2291 /*
2292  * array_set_slice :
2293  *                This routine sets the value of a range of array locations (specified
2294  *                by upper and lower subscript values) to new values passed as
2295  *                another array.
2296  *
2297  * This handles both ordinary varlena arrays and fixed-length arrays.
2298  *
2299  * Inputs:
2300  *      array: the initial array object (mustn't be NULL)
2301  *      nSubscripts: number of subscripts supplied (must be same for upper/lower)
2302  *      upperIndx[]: the upper subscript values
2303  *      lowerIndx[]: the lower subscript values
2304  *      srcArray: the source for the inserted values
2305  *      isNull: indicates whether srcArray is NULL
2306  *      arraytyplen: pg_type.typlen for the array type
2307  *      elmlen: pg_type.typlen for the array's element type
2308  *      elmbyval: pg_type.typbyval for the array's element type
2309  *      elmalign: pg_type.typalign for the array's element type
2310  *
2311  * Result:
2312  *                A new array is returned, just like the old except for the
2313  *                modified range.  The original array object is not changed.
2314  *
2315  * For one-dimensional arrays only, we allow the array to be extended
2316  * by assigning to positions outside the existing subscript range; any
2317  * positions between the existing elements and the new ones are set to NULLs.
2318  * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
2319  *
2320  * NOTE: we assume it is OK to scribble on the provided index arrays
2321  * lowerIndx[] and upperIndx[].  These are generally just temporaries.
2322  *
2323  * NOTE: For assignments, we throw an error for silly subscripts etc,
2324  * rather than returning a NULL or empty array as the fetch operations do.
2325  */
2326 ArrayType *
2327 array_set_slice(ArrayType *array,
2328                                 int nSubscripts,
2329                                 int *upperIndx,
2330                                 int *lowerIndx,
2331                                 ArrayType *srcArray,
2332                                 bool isNull,
2333                                 int arraytyplen,
2334                                 int elmlen,
2335                                 bool elmbyval,
2336                                 char elmalign)
2337 {
2338         ArrayType  *newarray;
2339         int                     i,
2340                                 ndim,
2341                                 dim[MAXDIM],
2342                                 lb[MAXDIM],
2343                                 span[MAXDIM];
2344         bool            newhasnulls;
2345         int                     nitems,
2346                                 nsrcitems,
2347                                 olddatasize,
2348                                 newsize,
2349                                 olditemsize,
2350                                 newitemsize,
2351                                 overheadlen,
2352                                 oldoverheadlen,
2353                                 addedbefore,
2354                                 addedafter,
2355                                 lenbefore,
2356                                 lenafter,
2357                                 itemsbefore,
2358                                 itemsafter,
2359                                 nolditems;
2360
2361         /* Currently, assignment from a NULL source array is a no-op */
2362         if (isNull)
2363                 return array;
2364
2365         if (arraytyplen > 0)
2366         {
2367                 /*
2368                  * fixed-length arrays -- not got round to doing this...
2369                  */
2370                 ereport(ERROR,
2371                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2372                 errmsg("updates on slices of fixed-length arrays not implemented")));
2373         }
2374
2375         /* detoast arrays if necessary */
2376         array = DatumGetArrayTypeP(PointerGetDatum(array));
2377         srcArray = DatumGetArrayTypeP(PointerGetDatum(srcArray));
2378
2379         /* note: we assume srcArray contains no toasted elements */
2380
2381         ndim = ARR_NDIM(array);
2382
2383         /*
2384          * if number of dims is zero, i.e. an empty array, create an array with
2385          * nSubscripts dimensions, and set the upper and lower bounds to the
2386          * supplied subscripts
2387          */
2388         if (ndim == 0)
2389         {
2390                 Datum      *dvalues;
2391                 bool       *dnulls;
2392                 int                     nelems;
2393                 Oid                     elmtype = ARR_ELEMTYPE(array);
2394
2395                 deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
2396                                                   &dvalues, &dnulls, &nelems);
2397
2398                 for (i = 0; i < nSubscripts; i++)
2399                 {
2400                         dim[i] = 1 + upperIndx[i] - lowerIndx[i];
2401                         lb[i] = lowerIndx[i];
2402                 }
2403
2404                 /* complain if too few source items; we ignore extras, however */
2405                 if (nelems < ArrayGetNItems(nSubscripts, dim))
2406                         ereport(ERROR,
2407                                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2408                                          errmsg("source array too small")));
2409
2410                 return construct_md_array(dvalues, dnulls, nSubscripts,
2411                                                                   dim, lb, elmtype,
2412                                                                   elmlen, elmbyval, elmalign);
2413         }
2414
2415         if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
2416                 ereport(ERROR,
2417                                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2418                                  errmsg("wrong number of array subscripts")));
2419
2420         /* copy dim/lb since we may modify them */
2421         memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
2422         memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
2423
2424         newhasnulls = (ARR_HASNULL(array) || ARR_HASNULL(srcArray));
2425         addedbefore = addedafter = 0;
2426
2427         /*
2428          * Check subscripts
2429          */
2430         if (ndim == 1)
2431         {
2432                 Assert(nSubscripts == 1);
2433                 if (lowerIndx[0] > upperIndx[0])
2434                         ereport(ERROR,
2435                                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2436                                          errmsg("upper bound cannot be less than lower bound")));
2437                 if (lowerIndx[0] < lb[0])
2438                 {
2439                         if (upperIndx[0] < lb[0] - 1)
2440                                 newhasnulls = true;             /* will insert nulls */
2441                         addedbefore = lb[0] - lowerIndx[0];
2442                         dim[0] += addedbefore;
2443                         lb[0] = lowerIndx[0];
2444                 }
2445                 if (upperIndx[0] >= (dim[0] + lb[0]))
2446                 {
2447                         if (lowerIndx[0] > (dim[0] + lb[0]))
2448                                 newhasnulls = true;             /* will insert nulls */
2449                         addedafter = upperIndx[0] - (dim[0] + lb[0]) + 1;
2450                         dim[0] += addedafter;
2451                 }
2452         }
2453         else
2454         {
2455                 /*
2456                  * XXX currently we do not support extending multi-dimensional arrays
2457                  * during assignment
2458                  */
2459                 for (i = 0; i < nSubscripts; i++)
2460                 {
2461                         if (lowerIndx[i] > upperIndx[i])
2462                                 ereport(ERROR,
2463                                                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2464                                          errmsg("upper bound cannot be less than lower bound")));
2465                         if (lowerIndx[i] < lb[i] ||
2466                                 upperIndx[i] >= (dim[i] + lb[i]))
2467                                 ereport(ERROR,
2468                                                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2469                                                  errmsg("array subscript out of range")));
2470                 }
2471                 /* fill any missing subscript positions with full array range */
2472                 for (; i < ndim; i++)
2473                 {
2474                         lowerIndx[i] = lb[i];
2475                         upperIndx[i] = dim[i] + lb[i] - 1;
2476                         if (lowerIndx[i] > upperIndx[i])
2477                                 ereport(ERROR,
2478                                                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2479                                          errmsg("upper bound cannot be less than lower bound")));
2480                 }
2481         }
2482
2483         /* Do this mainly to check for overflow */
2484         nitems = ArrayGetNItems(ndim, dim);
2485
2486         /*
2487          * Make sure source array has enough entries.  Note we ignore the shape of
2488          * the source array and just read entries serially.
2489          */
2490         mda_get_range(ndim, span, lowerIndx, upperIndx);
2491         nsrcitems = ArrayGetNItems(ndim, span);
2492         if (nsrcitems > ArrayGetNItems(ARR_NDIM(srcArray), ARR_DIMS(srcArray)))
2493                 ereport(ERROR,
2494                                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2495                                  errmsg("source array too small")));
2496
2497         /*
2498          * Compute space occupied by new entries, space occupied by replaced
2499          * entries, and required space for new array.
2500          */
2501         if (newhasnulls)
2502                 overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
2503         else
2504                 overheadlen = ARR_OVERHEAD_NONULLS(ndim);
2505         newitemsize = array_nelems_size(ARR_DATA_PTR(srcArray), 0,
2506                                                                         ARR_NULLBITMAP(srcArray), nsrcitems,
2507                                                                         elmlen, elmbyval, elmalign);
2508         oldoverheadlen = ARR_DATA_OFFSET(array);
2509         olddatasize = ARR_SIZE(array) - oldoverheadlen;
2510         if (ndim > 1)
2511         {
2512                 /*
2513                  * here we do not need to cope with extension of the array; it would
2514                  * be a lot more complicated if we had to do so...
2515                  */
2516                 olditemsize = array_slice_size(ARR_DATA_PTR(array),
2517                                                                            ARR_NULLBITMAP(array),
2518                                                                            ndim, dim, lb,
2519                                                                            lowerIndx, upperIndx,
2520                                                                            elmlen, elmbyval, elmalign);
2521                 lenbefore = lenafter = 0;               /* keep compiler quiet */
2522                 itemsbefore = itemsafter = nolditems = 0;
2523         }
2524         else
2525         {
2526                 /*
2527                  * here we must allow for possibility of slice larger than orig array
2528                  * and/or not adjacent to orig array subscripts
2529                  */
2530                 int                     oldlb = ARR_LBOUND(array)[0];
2531                 int                     oldub = oldlb + ARR_DIMS(array)[0] - 1;
2532                 int                     slicelb = Max(oldlb, lowerIndx[0]);
2533                 int                     sliceub = Min(oldub, upperIndx[0]);
2534                 char       *oldarraydata = ARR_DATA_PTR(array);
2535                 bits8      *oldarraybitmap = ARR_NULLBITMAP(array);
2536
2537                 /* count/size of old array entries that will go before the slice */
2538                 itemsbefore = Min(slicelb, oldub + 1) - oldlb;
2539                 lenbefore = array_nelems_size(oldarraydata, 0, oldarraybitmap,
2540                                                                           itemsbefore,
2541                                                                           elmlen, elmbyval, elmalign);
2542                 /* count/size of old array entries that will be replaced by slice */
2543                 if (slicelb > sliceub)
2544                 {
2545                         nolditems = 0;
2546                         olditemsize = 0;
2547                 }
2548                 else
2549                 {
2550                         nolditems = sliceub - slicelb + 1;
2551                         olditemsize = array_nelems_size(oldarraydata + lenbefore,
2552                                                                                         itemsbefore, oldarraybitmap,
2553                                                                                         nolditems,
2554                                                                                         elmlen, elmbyval, elmalign);
2555                 }
2556                 /* count/size of old array entries that will go after the slice */
2557                 itemsafter = oldub + 1 - Max(sliceub + 1, oldlb);
2558                 lenafter = olddatasize - lenbefore - olditemsize;
2559         }
2560
2561         newsize = overheadlen + olddatasize - olditemsize + newitemsize;
2562
2563         newarray = (ArrayType *) palloc0(newsize);
2564         SET_VARSIZE(newarray, newsize);
2565         newarray->ndim = ndim;
2566         newarray->dataoffset = newhasnulls ? overheadlen : 0;
2567         newarray->elemtype = ARR_ELEMTYPE(array);
2568         memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
2569         memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
2570
2571         if (ndim > 1)
2572         {
2573                 /*
2574                  * here we do not need to cope with extension of the array; it would
2575                  * be a lot more complicated if we had to do so...
2576                  */
2577                 array_insert_slice(newarray, array, srcArray,
2578                                                    ndim, dim, lb,
2579                                                    lowerIndx, upperIndx,
2580                                                    elmlen, elmbyval, elmalign);
2581         }
2582         else
2583         {
2584                 /* fill in data */
2585                 memcpy((char *) newarray + overheadlen,
2586                            (char *) array + oldoverheadlen,
2587                            lenbefore);
2588                 memcpy((char *) newarray + overheadlen + lenbefore,
2589                            ARR_DATA_PTR(srcArray),
2590                            newitemsize);
2591                 memcpy((char *) newarray + overheadlen + lenbefore + newitemsize,
2592                            (char *) array + oldoverheadlen + lenbefore + olditemsize,
2593                            lenafter);
2594                 /* fill in nulls bitmap if needed */
2595                 if (newhasnulls)
2596                 {
2597                         bits8      *newnullbitmap = ARR_NULLBITMAP(newarray);
2598                         bits8      *oldnullbitmap = ARR_NULLBITMAP(array);
2599
2600                         /* Zero the bitmap to handle marking inserted positions null */
2601                         MemSet(newnullbitmap, 0, (nitems + 7) / 8);
2602                         array_bitmap_copy(newnullbitmap, addedbefore,
2603                                                           oldnullbitmap, 0,
2604                                                           itemsbefore);
2605                         array_bitmap_copy(newnullbitmap, lowerIndx[0] - lb[0],
2606                                                           ARR_NULLBITMAP(srcArray), 0,
2607                                                           nsrcitems);
2608                         array_bitmap_copy(newnullbitmap, addedbefore + itemsbefore + nolditems,
2609                                                           oldnullbitmap, itemsbefore + nolditems,
2610                                                           itemsafter);
2611                 }
2612         }
2613
2614         return newarray;
2615 }
2616
2617 /*
2618  * array_map()
2619  *
2620  * Map an array through an arbitrary function.  Return a new array with
2621  * same dimensions and each source element transformed by fn().  Each
2622  * source element is passed as the first argument to fn(); additional
2623  * arguments to be passed to fn() can be specified by the caller.
2624  * The output array can have a different element type than the input.
2625  *
2626  * Parameters are:
2627  * * fcinfo: a function-call data structure pre-constructed by the caller
2628  *       to be ready to call the desired function, with everything except the
2629  *       first argument position filled in.  In particular, flinfo identifies
2630  *       the function fn(), and if nargs > 1 then argument positions after the
2631  *       first must be preset to the additional values to be passed.  The
2632  *       first argument position initially holds the input array value.
2633  * * inpType: OID of element type of input array.  This must be the same as,
2634  *       or binary-compatible with, the first argument type of fn().
2635  * * retType: OID of element type of output array.      This must be the same as,
2636  *       or binary-compatible with, the result type of fn().
2637  * * amstate: workspace for array_map.  Must be zeroed by caller before
2638  *       first call, and not touched after that.
2639  *
2640  * It is legitimate to pass a freshly-zeroed ArrayMapState on each call,
2641  * but better performance can be had if the state can be preserved across
2642  * a series of calls.
2643  *
2644  * NB: caller must assure that input array is not NULL.  NULL elements in
2645  * the array are OK however.
2646  */
2647 Datum
2648 array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType,
2649                   ArrayMapState *amstate)
2650 {
2651         ArrayType  *v;
2652         ArrayType  *result;
2653         Datum      *values;
2654         bool       *nulls;
2655         Datum           elt;
2656         int                *dim;
2657         int                     ndim;
2658         int                     nitems;
2659         int                     i;
2660         int32           nbytes = 0;
2661         int32           dataoffset;
2662         bool            hasnulls;
2663         int                     inp_typlen;
2664         bool            inp_typbyval;
2665         char            inp_typalign;
2666         int                     typlen;
2667         bool            typbyval;
2668         char            typalign;
2669         char       *s;
2670         bits8      *bitmap;
2671         int                     bitmask;
2672         ArrayMetaState *inp_extra;
2673         ArrayMetaState *ret_extra;
2674
2675         /* Get input array */
2676         if (fcinfo->nargs < 1)
2677                 elog(ERROR, "invalid nargs: %d", fcinfo->nargs);
2678         if (PG_ARGISNULL(0))
2679                 elog(ERROR, "null input array");
2680         v = PG_GETARG_ARRAYTYPE_P(0);
2681
2682         Assert(ARR_ELEMTYPE(v) == inpType);
2683
2684         ndim = ARR_NDIM(v);
2685         dim = ARR_DIMS(v);
2686         nitems = ArrayGetNItems(ndim, dim);
2687
2688         /* Check for empty array */
2689         if (nitems <= 0)
2690         {
2691                 /* Return empty array */
2692                 PG_RETURN_ARRAYTYPE_P(construct_empty_array(retType));
2693         }
2694
2695         /*
2696          * We arrange to look up info about input and return element types only
2697          * once per series of calls, assuming the element type doesn't change
2698          * underneath us.
2699          */
2700         inp_extra = &amstate->inp_extra;
2701         ret_extra = &amstate->ret_extra;
2702
2703         if (inp_extra->element_type != inpType)
2704         {
2705                 get_typlenbyvalalign(inpType,
2706                                                          &inp_extra->typlen,
2707                                                          &inp_extra->typbyval,
2708                                                          &inp_extra->typalign);
2709                 inp_extra->element_type = inpType;
2710         }
2711         inp_typlen = inp_extra->typlen;
2712         inp_typbyval = inp_extra->typbyval;
2713         inp_typalign = inp_extra->typalign;
2714
2715         if (ret_extra->element_type != retType)
2716         {
2717                 get_typlenbyvalalign(retType,
2718                                                          &ret_extra->typlen,
2719                                                          &ret_extra->typbyval,
2720                                                          &ret_extra->typalign);
2721                 ret_extra->element_type = retType;
2722         }
2723         typlen = ret_extra->typlen;
2724         typbyval = ret_extra->typbyval;
2725         typalign = ret_extra->typalign;
2726
2727         /* Allocate temporary arrays for new values */
2728         values = (Datum *) palloc(nitems * sizeof(Datum));
2729         nulls = (bool *) palloc(nitems * sizeof(bool));
2730
2731         /* Loop over source data */
2732         s = ARR_DATA_PTR(v);
2733         bitmap = ARR_NULLBITMAP(v);
2734         bitmask = 1;
2735         hasnulls = false;
2736
2737         for (i = 0; i < nitems; i++)
2738         {
2739                 bool            callit = true;
2740
2741                 /* Get source element, checking for NULL */
2742                 if (bitmap && (*bitmap & bitmask) == 0)
2743                 {
2744                         fcinfo->argnull[0] = true;
2745                 }
2746                 else
2747                 {
2748                         elt = fetch_att(s, inp_typbyval, inp_typlen);
2749                         s = att_addlength_datum(s, inp_typlen, elt);
2750                         s = (char *) att_align_nominal(s, inp_typalign);
2751                         fcinfo->arg[0] = elt;
2752                         fcinfo->argnull[0] = false;
2753                 }
2754
2755                 /*
2756                  * Apply the given function to source elt and extra args.
2757                  */
2758                 if (fcinfo->flinfo->fn_strict)
2759                 {
2760                         int                     j;
2761
2762                         for (j = 0; j < fcinfo->nargs; j++)
2763                         {
2764                                 if (fcinfo->argnull[j])
2765                                 {
2766                                         callit = false;
2767                                         break;
2768                                 }
2769                         }
2770                 }
2771
2772                 if (callit)
2773                 {
2774                         fcinfo->isnull = false;
2775                         values[i] = FunctionCallInvoke(fcinfo);
2776                 }
2777                 else
2778                         fcinfo->isnull = true;
2779
2780                 nulls[i] = fcinfo->isnull;
2781                 if (fcinfo->isnull)
2782                         hasnulls = true;
2783                 else
2784                 {
2785                         /* Ensure data is not toasted */
2786                         if (typlen == -1)
2787                                 values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
2788                         /* Update total result size */
2789                         nbytes = att_addlength_datum(nbytes, typlen, values[i]);
2790                         nbytes = att_align_nominal(nbytes, typalign);
2791                         /* check for overflow of total request */
2792                         if (!AllocSizeIsValid(nbytes))
2793                                 ereport(ERROR,
2794                                                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2795                                                  errmsg("array size exceeds the maximum allowed (%d)",
2796                                                                 (int) MaxAllocSize)));
2797                 }
2798
2799                 /* advance bitmap pointer if any */
2800                 if (bitmap)
2801                 {
2802                         bitmask <<= 1;
2803                         if (bitmask == 0x100)
2804                         {
2805                                 bitmap++;
2806                                 bitmask = 1;
2807                         }
2808                 }
2809         }
2810
2811         /* Allocate and initialize the result array */
2812         if (hasnulls)
2813         {
2814                 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
2815                 nbytes += dataoffset;
2816         }
2817         else
2818         {
2819                 dataoffset = 0;                 /* marker for no null bitmap */
2820                 nbytes += ARR_OVERHEAD_NONULLS(ndim);
2821         }
2822         result = (ArrayType *) palloc0(nbytes);
2823         SET_VARSIZE(result, nbytes);
2824         result->ndim = ndim;
2825         result->dataoffset = dataoffset;
2826         result->elemtype = retType;
2827         memcpy(ARR_DIMS(result), ARR_DIMS(v), 2 * ndim * sizeof(int));
2828
2829         /*
2830          * Note: do not risk trying to pfree the results of the called function
2831          */
2832         CopyArrayEls(result,
2833                                  values, nulls, nitems,
2834                                  typlen, typbyval, typalign,
2835                                  false);
2836
2837         pfree(values);
2838         pfree(nulls);
2839
2840         PG_RETURN_ARRAYTYPE_P(result);
2841 }
2842
2843 /*
2844  * construct_array      --- simple method for constructing an array object
2845  *
2846  * elems: array of Datum items to become the array contents
2847  *                (NULL element values are not supported).
2848  * nelems: number of items
2849  * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
2850  *
2851  * A palloc'd 1-D array object is constructed and returned.  Note that
2852  * elem values will be copied into the object even if pass-by-ref type.
2853  *
2854  * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
2855  * from the system catalogs, given the elmtype.  However, the caller is
2856  * in a better position to cache this info across multiple uses, or even
2857  * to hard-wire values if the element type is hard-wired.
2858  */
2859 ArrayType *
2860 construct_array(Datum *elems, int nelems,
2861                                 Oid elmtype,
2862                                 int elmlen, bool elmbyval, char elmalign)
2863 {
2864         int                     dims[1];
2865         int                     lbs[1];
2866
2867         dims[0] = nelems;
2868         lbs[0] = 1;
2869
2870         return construct_md_array(elems, NULL, 1, dims, lbs,
2871                                                           elmtype, elmlen, elmbyval, elmalign);
2872 }
2873
2874 /*
2875  * construct_md_array   --- simple method for constructing an array object
2876  *                                                      with arbitrary dimensions and possible NULLs
2877  *
2878  * elems: array of Datum items to become the array contents
2879  * nulls: array of is-null flags (can be NULL if no nulls)
2880  * ndims: number of dimensions
2881  * dims: integer array with size of each dimension
2882  * lbs: integer array with lower bound of each dimension
2883  * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
2884  *
2885  * A palloc'd ndims-D array object is constructed and returned.  Note that
2886  * elem values will be copied into the object even if pass-by-ref type.
2887  *
2888  * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
2889  * from the system catalogs, given the elmtype.  However, the caller is
2890  * in a better position to cache this info across multiple uses, or even
2891  * to hard-wire values if the element type is hard-wired.
2892  */
2893 ArrayType *
2894 construct_md_array(Datum *elems,
2895                                    bool *nulls,
2896                                    int ndims,
2897                                    int *dims,
2898                                    int *lbs,
2899                                    Oid elmtype, int elmlen, bool elmbyval, char elmalign)
2900 {
2901         ArrayType  *result;
2902         bool            hasnulls;
2903         int32           nbytes;
2904         int32           dataoffset;
2905         int                     i;
2906         int                     nelems;
2907
2908         if (ndims < 0)                          /* we do allow zero-dimension arrays */
2909                 ereport(ERROR,
2910                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2911                                  errmsg("invalid number of dimensions: %d", ndims)));
2912         if (ndims > MAXDIM)
2913                 ereport(ERROR,
2914                                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2915                                  errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
2916                                                 ndims, MAXDIM)));
2917
2918         /* fast track for empty array */
2919         if (ndims == 0)
2920                 return construct_empty_array(elmtype);
2921
2922         nelems = ArrayGetNItems(ndims, dims);
2923
2924         /* compute required space */
2925         nbytes = 0;
2926         hasnulls = false;
2927         for (i = 0; i < nelems; i++)
2928         {
2929                 if (nulls && nulls[i])
2930                 {
2931                         hasnulls = true;
2932                         continue;
2933                 }
2934                 /* make sure data is not toasted */
2935                 if (elmlen == -1)
2936                         elems[i] = PointerGetDatum(PG_DETOAST_DATUM(elems[i]));
2937                 nbytes = att_addlength_datum(nbytes, elmlen, elems[i]);
2938                 nbytes = att_align_nominal(nbytes, elmalign);
2939                 /* check for overflow of total request */
2940                 if (!AllocSizeIsValid(nbytes))
2941                         ereport(ERROR,
2942                                         (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2943                                          errmsg("array size exceeds the maximum allowed (%d)",
2944                                                         (int) MaxAllocSize)));
2945         }
2946
2947         /* Allocate and initialize result array */
2948         if (hasnulls)
2949         {
2950                 dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nelems);
2951                 nbytes += dataoffset;
2952         }
2953         else
2954         {
2955                 dataoffset = 0;                 /* marker for no null bitmap */
2956                 nbytes += ARR_OVERHEAD_NONULLS(ndims);
2957         }
2958         result = (ArrayType *) palloc0(nbytes);
2959         SET_VARSIZE(result, nbytes);
2960         result->ndim = ndims;
2961         result->dataoffset = dataoffset;
2962         result->elemtype = elmtype;
2963         memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
2964         memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
2965
2966         CopyArrayEls(result,
2967                                  elems, nulls, nelems,
2968                                  elmlen, elmbyval, elmalign,
2969                                  false);
2970
2971         return result;
2972 }
2973
2974 /*
2975  * construct_empty_array        --- make a zero-dimensional array of given type
2976  */
2977 ArrayType *
2978 construct_empty_array(Oid elmtype)
2979 {
2980         ArrayType  *result;
2981
2982         result = (ArrayType *) palloc0(sizeof(ArrayType));
2983         SET_VARSIZE(result, sizeof(ArrayType));
2984         result->ndim = 0;
2985         result->dataoffset = 0;
2986         result->elemtype = elmtype;
2987         return result;
2988 }
2989
2990 /*
2991  * deconstruct_array  --- simple method for extracting data from an array
2992  *
2993  * array: array object to examine (must not be NULL)
2994  * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
2995  * elemsp: return value, set to point to palloc'd array of Datum values
2996  * nullsp: return value, set to point to palloc'd array of isnull markers
2997  * nelemsp: return value, set to number of extracted values
2998  *
2999  * The caller may pass nullsp == NULL if it does not support NULLs in the
3000  * array.  Note that this produces a very uninformative error message,
3001  * so do it only in cases where a NULL is really not expected.
3002  *
3003  * If array elements are pass-by-ref data type, the returned Datums will
3004  * be pointers into the array object.
3005  *
3006  * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
3007  * from the system catalogs, given the elmtype.  However, in most current
3008  * uses the type is hard-wired into the caller and so we can save a lookup
3009  * cycle by hard-wiring the type info as well.
3010  */
3011 void
3012 deconstruct_array(ArrayType *array,
3013                                   Oid elmtype,
3014                                   int elmlen, bool elmbyval, char elmalign,
3015                                   Datum **elemsp, bool **nullsp, int *nelemsp)
3016 {
3017         Datum      *elems;
3018         bool       *nulls;
3019         int                     nelems;
3020         char       *p;
3021         bits8      *bitmap;
3022         int                     bitmask;
3023         int                     i;
3024
3025         Assert(ARR_ELEMTYPE(array) == elmtype);
3026
3027         nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
3028         *elemsp = elems = (Datum *) palloc(nelems * sizeof(Datum));
3029         if (nullsp)
3030                 *nullsp = nulls = (bool *) palloc0(nelems * sizeof(bool));
3031         else
3032                 nulls = NULL;
3033         *nelemsp = nelems;
3034
3035         p = ARR_DATA_PTR(array);
3036         bitmap = ARR_NULLBITMAP(array);
3037         bitmask = 1;
3038
3039         for (i = 0; i < nelems; i++)
3040         {
3041                 /* Get source element, checking for NULL */
3042                 if (bitmap && (*bitmap & bitmask) == 0)
3043                 {
3044                         elems[i] = (Datum) 0;
3045                         if (nulls)
3046                                 nulls[i] = true;
3047                         else
3048                                 ereport(ERROR,
3049                                                 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
3050                                   errmsg("null array element not allowed in this context")));
3051                 }
3052                 else
3053                 {
3054                         elems[i] = fetch_att(p, elmbyval, elmlen);
3055                         p = att_addlength_pointer(p, elmlen, p);
3056                         p = (char *) att_align_nominal(p, elmalign);
3057                 }
3058
3059                 /* advance bitmap pointer if any */
3060                 if (bitmap)
3061                 {
3062                         bitmask <<= 1;
3063                         if (bitmask == 0x100)
3064                         {
3065                                 bitmap++;
3066                                 bitmask = 1;
3067                         }
3068                 }
3069         }
3070 }
3071
3072 /*
3073  * array_contains_nulls --- detect whether an array has any null elements
3074  *
3075  * This gives an accurate answer, whereas testing ARR_HASNULL only tells
3076  * if the array *might* contain a null.
3077  */
3078 bool
3079 array_contains_nulls(ArrayType *array)
3080 {
3081         int                     nelems;
3082         bits8      *bitmap;
3083         int                     bitmask;
3084
3085         /* Easy answer if there's no null bitmap */
3086         if (!ARR_HASNULL(array))
3087                 return false;
3088
3089         nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
3090
3091         bitmap = ARR_NULLBITMAP(array);
3092
3093         /* check whole bytes of the bitmap byte-at-a-time */
3094         while (nelems >= 8)
3095         {
3096                 if (*bitmap != 0xFF)
3097                         return true;
3098                 bitmap++;
3099                 nelems -= 8;
3100         }
3101
3102         /* check last partial byte */
3103         bitmask = 1;
3104         while (nelems > 0)
3105         {
3106                 if ((*bitmap & bitmask) == 0)
3107                         return true;
3108                 bitmask <<= 1;
3109                 nelems--;
3110         }
3111
3112         return false;
3113 }
3114
3115
3116 /*
3117  * array_eq :
3118  *                compares two arrays for equality
3119  * result :
3120  *                returns true if the arrays are equal, false otherwise.
3121  *
3122  * Note: we do not use array_cmp here, since equality may be meaningful in
3123  * datatypes that don't have a total ordering (and hence no btree support).
3124  */
3125 Datum
3126 array_eq(PG_FUNCTION_ARGS)
3127 {
3128         ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
3129         ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
3130         Oid                     collation = PG_GET_COLLATION();
3131         int                     ndims1 = ARR_NDIM(array1);
3132         int                     ndims2 = ARR_NDIM(array2);
3133         int                *dims1 = ARR_DIMS(array1);
3134         int                *dims2 = ARR_DIMS(array2);
3135         Oid                     element_type = ARR_ELEMTYPE(array1);
3136         bool            result = true;
3137         int                     nitems;
3138         TypeCacheEntry *typentry;
3139         int                     typlen;
3140         bool            typbyval;
3141         char            typalign;
3142         char       *ptr1;
3143         char       *ptr2;
3144         bits8      *bitmap1;
3145         bits8      *bitmap2;
3146         int                     bitmask;
3147         int                     i;
3148         FunctionCallInfoData locfcinfo;
3149
3150         if (element_type != ARR_ELEMTYPE(array2))
3151                 ereport(ERROR,
3152                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
3153                                  errmsg("cannot compare arrays of different element types")));
3154
3155         /* fast path if the arrays do not have the same dimensionality */
3156         if (ndims1 != ndims2 ||
3157                 memcmp(dims1, dims2, 2 * ndims1 * sizeof(int)) != 0)
3158                 result = false;
3159         else
3160         {
3161                 /*
3162                  * We arrange to look up the equality function only once per series of
3163                  * calls, assuming the element type doesn't change underneath us.  The
3164                  * typcache is used so that we have no memory leakage when being used
3165                  * as an index support function.
3166                  */
3167                 typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3168                 if (typentry == NULL ||
3169                         typentry->type_id != element_type)
3170                 {
3171                         typentry = lookup_type_cache(element_type,
3172                                                                                  TYPECACHE_EQ_OPR_FINFO);
3173                         if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
3174                                 ereport(ERROR,
3175                                                 (errcode(ERRCODE_UNDEFINED_FUNCTION),
3176                                 errmsg("could not identify an equality operator for type %s",
3177                                            format_type_be(element_type))));
3178                         fcinfo->flinfo->fn_extra = (void *) typentry;
3179                 }
3180                 typlen = typentry->typlen;
3181                 typbyval = typentry->typbyval;
3182                 typalign = typentry->typalign;
3183
3184                 /*
3185                  * apply the operator to each pair of array elements.
3186                  */
3187                 InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
3188                                                                  collation, NULL, NULL);
3189
3190                 /* Loop over source data */
3191                 nitems = ArrayGetNItems(ndims1, dims1);
3192                 ptr1 = ARR_DATA_PTR(array1);
3193                 ptr2 = ARR_DATA_PTR(array2);
3194                 bitmap1 = ARR_NULLBITMAP(array1);
3195                 bitmap2 = ARR_NULLBITMAP(array2);
3196                 bitmask = 1;                    /* use same bitmask for both arrays */
3197
3198                 for (i = 0; i < nitems; i++)
3199                 {
3200                         Datum           elt1;
3201                         Datum           elt2;
3202                         bool            isnull1;
3203                         bool            isnull2;
3204                         bool            oprresult;
3205
3206                         /* Get elements, checking for NULL */
3207                         if (bitmap1 && (*bitmap1 & bitmask) == 0)
3208                         {
3209                                 isnull1 = true;
3210                                 elt1 = (Datum) 0;
3211                         }
3212                         else
3213                         {
3214                                 isnull1 = false;
3215                                 elt1 = fetch_att(ptr1, typbyval, typlen);
3216                                 ptr1 = att_addlength_pointer(ptr1, typlen, ptr1);
3217                                 ptr1 = (char *) att_align_nominal(ptr1, typalign);
3218                         }
3219
3220                         if (bitmap2 && (*bitmap2 & bitmask) == 0)
3221                         {
3222                                 isnull2 = true;
3223                                 elt2 = (Datum) 0;
3224                         }
3225                         else
3226                         {
3227                                 isnull2 = false;
3228                                 elt2 = fetch_att(ptr2, typbyval, typlen);
3229                                 ptr2 = att_addlength_pointer(ptr2, typlen, ptr2);
3230                                 ptr2 = (char *) att_align_nominal(ptr2, typalign);
3231                         }
3232
3233                         /* advance bitmap pointers if any */
3234                         bitmask <<= 1;
3235                         if (bitmask == 0x100)
3236                         {
3237                                 if (bitmap1)
3238                                         bitmap1++;
3239                                 if (bitmap2)
3240                                         bitmap2++;
3241                                 bitmask = 1;
3242                         }
3243
3244                         /*
3245                          * We consider two NULLs equal; NULL and not-NULL are unequal.
3246                          */
3247                         if (isnull1 && isnull2)
3248                                 continue;
3249                         if (isnull1 || isnull2)
3250                         {
3251                                 result = false;
3252                                 break;
3253                         }
3254
3255                         /*
3256                          * Apply the operator to the element pair
3257                          */
3258                         locfcinfo.arg[0] = elt1;
3259                         locfcinfo.arg[1] = elt2;
3260                         locfcinfo.argnull[0] = false;
3261                         locfcinfo.argnull[1] = false;
3262                         locfcinfo.isnull = false;
3263                         oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
3264                         if (!oprresult)
3265                         {
3266                                 result = false;
3267                                 break;
3268                         }
3269                 }
3270         }
3271
3272         /* Avoid leaking memory when handed toasted input. */
3273         PG_FREE_IF_COPY(array1, 0);
3274         PG_FREE_IF_COPY(array2, 1);
3275
3276         PG_RETURN_BOOL(result);
3277 }
3278
3279
3280 /*-----------------------------------------------------------------------------
3281  * array-array bool operators:
3282  *              Given two arrays, iterate comparison operators
3283  *              over the array. Uses logic similar to text comparison
3284  *              functions, except element-by-element instead of
3285  *              character-by-character.
3286  *----------------------------------------------------------------------------
3287  */
3288
3289 Datum
3290 array_ne(PG_FUNCTION_ARGS)
3291 {
3292         PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo)));
3293 }
3294
3295 Datum
3296 array_lt(PG_FUNCTION_ARGS)
3297 {
3298         PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
3299 }
3300
3301 Datum
3302 array_gt(PG_FUNCTION_ARGS)
3303 {
3304         PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
3305 }
3306
3307 Datum
3308 array_le(PG_FUNCTION_ARGS)
3309 {
3310         PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
3311 }
3312
3313 Datum
3314 array_ge(PG_FUNCTION_ARGS)
3315 {
3316         PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
3317 }
3318
3319 Datum
3320 btarraycmp(PG_FUNCTION_ARGS)
3321 {
3322         PG_RETURN_INT32(array_cmp(fcinfo));
3323 }
3324
3325 /*
3326  * array_cmp()
3327  * Internal comparison function for arrays.
3328  *
3329  * Returns -1, 0 or 1
3330  */
3331 static int
3332 array_cmp(FunctionCallInfo fcinfo)
3333 {
3334         ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
3335         ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
3336         Oid                     collation = PG_GET_COLLATION();
3337         int                     ndims1 = ARR_NDIM(array1);
3338         int                     ndims2 = ARR_NDIM(array2);
3339         int                *dims1 = ARR_DIMS(array1);
3340         int                *dims2 = ARR_DIMS(array2);
3341         int                     nitems1 = ArrayGetNItems(ndims1, dims1);
3342         int                     nitems2 = ArrayGetNItems(ndims2, dims2);
3343         Oid                     element_type = ARR_ELEMTYPE(array1);
3344         int                     result = 0;
3345         TypeCacheEntry *typentry;
3346         int                     typlen;
3347         bool            typbyval;
3348         char            typalign;
3349         int                     min_nitems;
3350         char       *ptr1;
3351         char       *ptr2;
3352         bits8      *bitmap1;
3353         bits8      *bitmap2;
3354         int                     bitmask;
3355         int                     i;
3356         FunctionCallInfoData locfcinfo;
3357
3358         if (element_type != ARR_ELEMTYPE(array2))
3359                 ereport(ERROR,
3360                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
3361                                  errmsg("cannot compare arrays of different element types")));
3362
3363         /*
3364          * We arrange to look up the comparison function only once per series of
3365          * calls, assuming the element type doesn't change underneath us. The
3366          * typcache is used so that we have no memory leakage when being used as
3367          * an index support function.
3368          */
3369         typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3370         if (typentry == NULL ||
3371                 typentry->type_id != element_type)
3372         {
3373                 typentry = lookup_type_cache(element_type,
3374                                                                          TYPECACHE_CMP_PROC_FINFO);
3375                 if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
3376                         ereport(ERROR,
3377                                         (errcode(ERRCODE_UNDEFINED_FUNCTION),
3378                            errmsg("could not identify a comparison function for type %s",
3379                                           format_type_be(element_type))));
3380                 fcinfo->flinfo->fn_extra = (void *) typentry;
3381         }
3382         typlen = typentry->typlen;
3383         typbyval = typentry->typbyval;
3384         typalign = typentry->typalign;
3385
3386         /*
3387          * apply the operator to each pair of array elements.
3388          */
3389         InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2,
3390                                                          collation, NULL, NULL);
3391
3392         /* Loop over source data */
3393         min_nitems = Min(nitems1, nitems2);
3394         ptr1 = ARR_DATA_PTR(array1);
3395         ptr2 = ARR_DATA_PTR(array2);
3396         bitmap1 = ARR_NULLBITMAP(array1);
3397         bitmap2 = ARR_NULLBITMAP(array2);
3398         bitmask = 1;                            /* use same bitmask for both arrays */
3399
3400         for (i = 0; i < min_nitems; i++)
3401         {
3402                 Datum           elt1;
3403                 Datum           elt2;
3404                 bool            isnull1;
3405                 bool            isnull2;
3406                 int32           cmpresult;
3407
3408                 /* Get elements, checking for NULL */
3409                 if (bitmap1 && (*bitmap1 & bitmask) == 0)
3410                 {
3411                         isnull1 = true;
3412                         elt1 = (Datum) 0;
3413                 }
3414                 else
3415                 {
3416                         isnull1 = false;
3417                         elt1 = fetch_att(ptr1, typbyval, typlen);
3418                         ptr1 = att_addlength_pointer(ptr1, typlen, ptr1);
3419                         ptr1 = (char *) att_align_nominal(ptr1, typalign);
3420                 }
3421
3422                 if (bitmap2 && (*bitmap2 & bitmask) == 0)
3423                 {
3424                         isnull2 = true;
3425                         elt2 = (Datum) 0;
3426                 }
3427                 else
3428                 {
3429                         isnull2 = false;
3430                         elt2 = fetch_att(ptr2, typbyval, typlen);
3431                         ptr2 = att_addlength_pointer(ptr2, typlen, ptr2);
3432                         ptr2 = (char *) att_align_nominal(ptr2, typalign);
3433                 }
3434
3435                 /* advance bitmap pointers if any */
3436                 bitmask <<= 1;
3437                 if (bitmask == 0x100)
3438                 {
3439                         if (bitmap1)
3440                                 bitmap1++;
3441                         if (bitmap2)
3442                                 bitmap2++;
3443                         bitmask = 1;
3444                 }
3445
3446                 /*
3447                  * We consider two NULLs equal; NULL > not-NULL.
3448                  */
3449                 if (isnull1 && isnull2)
3450                         continue;
3451                 if (isnull1)
3452                 {
3453                         /* arg1 is greater than arg2 */
3454                         result = 1;
3455                         break;
3456                 }
3457                 if (isnull2)
3458                 {
3459                         /* arg1 is less than arg2 */
3460                         result = -1;
3461                         break;
3462                 }
3463
3464                 /* Compare the pair of elements */
3465                 locfcinfo.arg[0] = elt1;
3466                 locfcinfo.arg[1] = elt2;
3467                 locfcinfo.argnull[0] = false;
3468                 locfcinfo.argnull[1] = false;
3469                 locfcinfo.isnull = false;
3470                 cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
3471
3472                 if (cmpresult == 0)
3473                         continue;                       /* equal */
3474
3475                 if (cmpresult < 0)
3476                 {
3477                         /* arg1 is less than arg2 */
3478                         result = -1;
3479                         break;
3480                 }
3481                 else
3482                 {
3483                         /* arg1 is greater than arg2 */
3484                         result = 1;
3485                         break;
3486                 }
3487         }
3488
3489         /*
3490          * If arrays contain same data (up to end of shorter one), apply
3491          * additional rules to sort by dimensionality.  The relative significance
3492          * of the different bits of information is historical; mainly we just care
3493          * that we don't say "equal" for arrays of different dimensionality.
3494          */
3495         if (result == 0)
3496         {
3497                 if (nitems1 != nitems2)
3498                         result = (nitems1 < nitems2) ? -1 : 1;
3499                 else if (ndims1 != ndims2)
3500                         result = (ndims1 < ndims2) ? -1 : 1;
3501                 else
3502                 {
3503                         /* this relies on LB array immediately following DIMS array */
3504                         for (i = 0; i < ndims1 * 2; i++)
3505                         {
3506                                 if (dims1[i] != dims2[i])
3507                                 {
3508                                         result = (dims1[i] < dims2[i]) ? -1 : 1;
3509                                         break;
3510                                 }
3511                         }
3512                 }
3513         }
3514
3515         /* Avoid leaking memory when handed toasted input. */
3516         PG_FREE_IF_COPY(array1, 0);
3517         PG_FREE_IF_COPY(array2, 1);
3518
3519         return result;
3520 }
3521
3522
3523 /*-----------------------------------------------------------------------------
3524  * array hashing
3525  *              Hash the elements and combine the results.
3526  *----------------------------------------------------------------------------
3527  */
3528
3529 Datum
3530 hash_array(PG_FUNCTION_ARGS)
3531 {
3532         ArrayType  *array = PG_GETARG_ARRAYTYPE_P(0);
3533         int                     ndims = ARR_NDIM(array);
3534         int                *dims = ARR_DIMS(array);
3535         Oid                     element_type = ARR_ELEMTYPE(array);
3536         uint32          result = 1;
3537         int                     nitems;
3538         TypeCacheEntry *typentry;
3539         int                     typlen;
3540         bool            typbyval;
3541         char            typalign;
3542         char       *ptr;
3543         bits8      *bitmap;
3544         int                     bitmask;
3545         int                     i;
3546         FunctionCallInfoData locfcinfo;
3547
3548         /*
3549          * We arrange to look up the hash function only once per series of calls,
3550          * assuming the element type doesn't change underneath us.  The typcache
3551          * is used so that we have no memory leakage when being used as an index
3552          * support function.
3553          */
3554         typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3555         if (typentry == NULL ||
3556                 typentry->type_id != element_type)
3557         {
3558                 typentry = lookup_type_cache(element_type,
3559                                                                          TYPECACHE_HASH_PROC_FINFO);
3560                 if (!OidIsValid(typentry->hash_proc_finfo.fn_oid))
3561                         ereport(ERROR,
3562                                         (errcode(ERRCODE_UNDEFINED_FUNCTION),
3563                                          errmsg("could not identify a hash function for type %s",
3564                                                         format_type_be(element_type))));
3565                 fcinfo->flinfo->fn_extra = (void *) typentry;
3566         }
3567         typlen = typentry->typlen;
3568         typbyval = typentry->typbyval;
3569         typalign = typentry->typalign;
3570
3571         /*
3572          * apply the hash function to each array element.
3573          */
3574         InitFunctionCallInfoData(locfcinfo, &typentry->hash_proc_finfo, 1,
3575                                                          InvalidOid, NULL, NULL);
3576
3577         /* Loop over source data */
3578         nitems = ArrayGetNItems(ndims, dims);
3579         ptr = ARR_DATA_PTR(array);
3580         bitmap = ARR_NULLBITMAP(array);
3581         bitmask = 1;
3582
3583         for (i = 0; i < nitems; i++)
3584         {
3585                 uint32          elthash;
3586
3587                 /* Get element, checking for NULL */
3588                 if (bitmap && (*bitmap & bitmask) == 0)
3589                 {
3590                         /* Treat nulls as having hashvalue 0 */
3591                         elthash = 0;
3592                 }
3593                 else
3594                 {
3595                         Datum           elt;
3596
3597                         elt = fetch_att(ptr, typbyval, typlen);
3598                         ptr = att_addlength_pointer(ptr, typlen, ptr);
3599                         ptr = (char *) att_align_nominal(ptr, typalign);
3600
3601                         /* Apply the hash function */
3602                         locfcinfo.arg[0] = elt;
3603                         locfcinfo.argnull[0] = false;
3604                         locfcinfo.isnull = false;
3605                         elthash = DatumGetUInt32(FunctionCallInvoke(&locfcinfo));
3606                 }
3607
3608                 /* advance bitmap pointer if any */
3609                 if (bitmap)
3610                 {
3611                         bitmask <<= 1;
3612                         if (bitmask == 0x100)
3613                         {
3614                                 bitmap++;
3615                                 bitmask = 1;
3616                         }
3617                 }
3618
3619                 /*
3620                  * Combine hash values of successive elements by multiplying the
3621                  * current value by 31 and adding on the new element's hash value.
3622                  *
3623                  * The result is a sum in which each element's hash value is
3624                  * multiplied by a different power of 31. This is modulo 2^32
3625                  * arithmetic, and the powers of 31 modulo 2^32 form a cyclic group of
3626                  * order 2^27. So for arrays of up to 2^27 elements, each element's
3627                  * hash value is multiplied by a different (odd) number, resulting in
3628                  * a good mixing of all the elements' hash values.
3629                  */
3630                 result = (result << 5) - result + elthash;
3631         }
3632
3633         /* Avoid leaking memory when handed toasted input. */
3634         PG_FREE_IF_COPY(array, 0);
3635
3636         PG_RETURN_UINT32(result);
3637 }
3638
3639
3640 /*-----------------------------------------------------------------------------
3641  * array overlap/containment comparisons
3642  *              These use the same methods of comparing array elements as array_eq.
3643  *              We consider only the elements of the arrays, ignoring dimensionality.
3644  *----------------------------------------------------------------------------
3645  */
3646
3647 /*
3648  * array_contain_compare :
3649  *                compares two arrays for overlap/containment
3650  *
3651  * When matchall is true, return true if all members of array1 are in array2.
3652  * When matchall is false, return true if any members of array1 are in array2.
3653  */
3654 static bool
3655 array_contain_compare(ArrayType *array1, ArrayType *array2, Oid collation,
3656                                           bool matchall, void **fn_extra)
3657 {
3658         bool            result = matchall;
3659         Oid                     element_type = ARR_ELEMTYPE(array1);
3660         TypeCacheEntry *typentry;
3661         int                     nelems1;
3662         Datum      *values2;
3663         bool       *nulls2;
3664         int                     nelems2;
3665         int                     typlen;
3666         bool            typbyval;
3667         char            typalign;
3668         char       *ptr1;
3669         bits8      *bitmap1;
3670         int                     bitmask;
3671         int                     i;
3672         int                     j;
3673         FunctionCallInfoData locfcinfo;
3674
3675         if (element_type != ARR_ELEMTYPE(array2))
3676                 ereport(ERROR,
3677                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
3678                                  errmsg("cannot compare arrays of different element types")));
3679
3680         /*
3681          * We arrange to look up the equality function only once per series of
3682          * calls, assuming the element type doesn't change underneath us.  The
3683          * typcache is used so that we have no memory leakage when being used as
3684          * an index support function.
3685          */
3686         typentry = (TypeCacheEntry *) *fn_extra;
3687         if (typentry == NULL ||
3688                 typentry->type_id != element_type)
3689         {
3690                 typentry = lookup_type_cache(element_type,
3691                                                                          TYPECACHE_EQ_OPR_FINFO);
3692                 if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
3693                         ereport(ERROR,
3694                                         (errcode(ERRCODE_UNDEFINED_FUNCTION),
3695                                 errmsg("could not identify an equality operator for type %s",
3696                                            format_type_be(element_type))));
3697                 *fn_extra = (void *) typentry;
3698         }
3699         typlen = typentry->typlen;
3700         typbyval = typentry->typbyval;
3701         typalign = typentry->typalign;
3702
3703         /*
3704          * Since we probably will need to scan array2 multiple times, it's
3705          * worthwhile to use deconstruct_array on it.  We scan array1 the hard way
3706          * however, since we very likely won't need to look at all of it.
3707          */
3708         deconstruct_array(array2, element_type, typlen, typbyval, typalign,
3709                                           &values2, &nulls2, &nelems2);
3710
3711         /*
3712          * Apply the comparison operator to each pair of array elements.
3713          */
3714         InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
3715                                                          collation, NULL, NULL);
3716
3717         /* Loop over source data */
3718         nelems1 = ArrayGetNItems(ARR_NDIM(array1), ARR_DIMS(array1));
3719         ptr1 = ARR_DATA_PTR(array1);
3720         bitmap1 = ARR_NULLBITMAP(array1);
3721         bitmask = 1;
3722
3723         for (i = 0; i < nelems1; i++)
3724         {
3725                 Datum           elt1;
3726                 bool            isnull1;
3727
3728                 /* Get element, checking for NULL */
3729                 if (bitmap1 && (*bitmap1 & bitmask) == 0)
3730                 {
3731                         isnull1 = true;
3732                         elt1 = (Datum) 0;
3733                 }
3734                 else
3735                 {
3736                         isnull1 = false;
3737                         elt1 = fetch_att(ptr1, typbyval, typlen);
3738                         ptr1 = att_addlength_pointer(ptr1, typlen, ptr1);
3739                         ptr1 = (char *) att_align_nominal(ptr1, typalign);
3740                 }
3741
3742                 /* advance bitmap pointer if any */
3743                 bitmask <<= 1;
3744                 if (bitmask == 0x100)
3745                 {
3746                         if (bitmap1)
3747                                 bitmap1++;
3748                         bitmask = 1;
3749                 }
3750
3751                 /*
3752                  * We assume that the comparison operator is strict, so a NULL can't
3753                  * match anything.      XXX this diverges from the "NULL=NULL" behavior of
3754                  * array_eq, should we act like that?
3755                  */
3756                 if (isnull1)
3757                 {
3758                         if (matchall)
3759                         {
3760                                 result = false;
3761                                 break;
3762                         }
3763                         continue;
3764                 }
3765
3766                 for (j = 0; j < nelems2; j++)
3767                 {
3768                         Datum           elt2 = values2[j];
3769                         bool            isnull2 = nulls2[j];
3770                         bool            oprresult;
3771
3772                         if (isnull2)
3773                                 continue;               /* can't match */
3774
3775                         /*
3776                          * Apply the operator to the element pair
3777                          */
3778                         locfcinfo.arg[0] = elt1;
3779                         locfcinfo.arg[1] = elt2;
3780                         locfcinfo.argnull[0] = false;
3781                         locfcinfo.argnull[1] = false;
3782                         locfcinfo.isnull = false;
3783                         oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
3784                         if (oprresult)
3785                                 break;
3786                 }
3787
3788                 if (j < nelems2)
3789                 {
3790                         /* found a match for elt1 */
3791                         if (!matchall)
3792                         {
3793                                 result = true;
3794                                 break;
3795                         }
3796                 }
3797                 else
3798                 {
3799                         /* no match for elt1 */
3800                         if (matchall)
3801                         {
3802                                 result = false;
3803                                 break;
3804                         }
3805                 }
3806         }
3807
3808         pfree(values2);
3809         pfree(nulls2);
3810
3811         return result;
3812 }
3813
3814 Datum
3815 arrayoverlap(PG_FUNCTION_ARGS)
3816 {
3817         ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
3818         ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
3819         Oid                     collation = PG_GET_COLLATION();
3820         bool            result;
3821
3822         result = array_contain_compare(array1, array2, collation, false,
3823                                                                    &fcinfo->flinfo->fn_extra);
3824
3825         /* Avoid leaking memory when handed toasted input. */
3826         PG_FREE_IF_COPY(array1, 0);
3827         PG_FREE_IF_COPY(array2, 1);
3828
3829         PG_RETURN_BOOL(result);
3830 }
3831
3832 Datum
3833 arraycontains(PG_FUNCTION_ARGS)
3834 {
3835         ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
3836         ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
3837         Oid                     collation = PG_GET_COLLATION();
3838         bool            result;
3839
3840         result = array_contain_compare(array2, array1, collation, true,
3841                                                                    &fcinfo->flinfo->fn_extra);
3842
3843         /* Avoid leaking memory when handed toasted input. */
3844         PG_FREE_IF_COPY(array1, 0);
3845         PG_FREE_IF_COPY(array2, 1);
3846
3847         PG_RETURN_BOOL(result);
3848 }
3849
3850 Datum
3851 arraycontained(PG_FUNCTION_ARGS)
3852 {
3853         ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
3854         ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
3855         Oid                     collation = PG_GET_COLLATION();
3856         bool            result;
3857
3858         result = array_contain_compare(array1, array2, collation, true,
3859                                                                    &fcinfo->flinfo->fn_extra);
3860
3861         /* Avoid leaking memory when handed toasted input. */
3862         PG_FREE_IF_COPY(array1, 0);
3863         PG_FREE_IF_COPY(array2, 1);
3864
3865         PG_RETURN_BOOL(result);
3866 }
3867
3868
3869 /*-----------------------------------------------------------------------------
3870  * Array iteration functions
3871  *              These functions are used to iterate efficiently through arrays
3872  *-----------------------------------------------------------------------------
3873  */
3874
3875 /*
3876  * array_create_iterator --- set up to iterate through an array
3877  *
3878  * If slice_ndim is zero, we will iterate element-by-element; the returned
3879  * datums are of the array's element type.
3880  *
3881  * If slice_ndim is 1..ARR_NDIM(arr), we will iterate by slices: the
3882  * returned datums are of the same array type as 'arr', but of size
3883  * equal to the rightmost N dimensions of 'arr'.
3884  *
3885  * The passed-in array must remain valid for the lifetime of the iterator.
3886  */
3887 ArrayIterator
3888 array_create_iterator(ArrayType *arr, int slice_ndim)
3889 {
3890         ArrayIterator iterator = palloc0(sizeof(ArrayIteratorData));
3891
3892         /*
3893          * Sanity-check inputs --- caller should have got this right already
3894          */
3895         Assert(PointerIsValid(arr));
3896         if (slice_ndim < 0 || slice_ndim > ARR_NDIM(arr))
3897                 elog(ERROR, "invalid arguments to array_create_iterator");
3898
3899         /*
3900          * Remember basic info about the array and its element type
3901          */
3902         iterator->arr = arr;
3903         iterator->nullbitmap = ARR_NULLBITMAP(arr);
3904         iterator->nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
3905         get_typlenbyvalalign(ARR_ELEMTYPE(arr),
3906                                                  &iterator->typlen,
3907                                                  &iterator->typbyval,
3908                                                  &iterator->typalign);
3909
3910         /*
3911          * Remember the slicing parameters.
3912          */
3913         iterator->slice_ndim = slice_ndim;
3914
3915         if (slice_ndim > 0)
3916         {
3917                 /*
3918                  * Get pointers into the array's dims and lbound arrays to represent
3919                  * the dims/lbound arrays of a slice.  These are the same as the
3920                  * rightmost N dimensions of the array.
3921                  */
3922                 iterator->slice_dims = ARR_DIMS(arr) + ARR_NDIM(arr) - slice_ndim;
3923                 iterator->slice_lbound = ARR_LBOUND(arr) + ARR_NDIM(arr) - slice_ndim;
3924
3925                 /*
3926                  * Compute number of elements in a slice.
3927                  */
3928                 iterator->slice_len = ArrayGetNItems(slice_ndim,
3929                                                                                          iterator->slice_dims);
3930
3931                 /*
3932                  * Create workspace for building sub-arrays.
3933                  */
3934                 iterator->slice_values = (Datum *)
3935                         palloc(iterator->slice_len * sizeof(Datum));
3936                 iterator->slice_nulls = (bool *)
3937                         palloc(iterator->slice_len * sizeof(bool));
3938         }
3939
3940         /*
3941          * Initialize our data pointer and linear element number.  These will
3942          * advance through the array during array_iterate().
3943          */
3944         iterator->data_ptr = ARR_DATA_PTR(arr);
3945         iterator->current_item = 0;
3946
3947         return iterator;
3948 }
3949
3950 /*
3951  * Iterate through the array referenced by 'iterator'.
3952  *
3953  * As long as there is another element (or slice), return it into
3954  * *value / *isnull, and return true.  Return false when no more data.
3955  */
3956 bool
3957 array_iterate(ArrayIterator iterator, Datum *value, bool *isnull)
3958 {
3959         /* Done if we have reached the end of the array */
3960         if (iterator->current_item >= iterator->nitems)
3961                 return false;
3962
3963         if (iterator->slice_ndim == 0)
3964         {
3965                 /*
3966                  * Scalar case: return one element.
3967                  */
3968                 if (array_get_isnull(iterator->nullbitmap, iterator->current_item++))
3969                 {
3970                         *isnull = true;
3971                         *value = (Datum) 0;
3972                 }
3973                 else
3974                 {
3975                         /* non-NULL, so fetch the individual Datum to return */
3976                         char       *p = iterator->data_ptr;
3977
3978                         *isnull = false;
3979                         *value = fetch_att(p, iterator->typbyval, iterator->typlen);
3980
3981                         /* Move our data pointer forward to the next element */
3982                         p = att_addlength_pointer(p, iterator->typlen, p);
3983                         p = (char *) att_align_nominal(p, iterator->typalign);
3984                         iterator->data_ptr = p;
3985                 }
3986         }
3987         else
3988         {
3989                 /*
3990                  * Slice case: build and return an array of the requested size.
3991                  */
3992                 ArrayType  *result;
3993                 Datum      *values = iterator->slice_values;
3994                 bool       *nulls = iterator->slice_nulls;
3995                 char       *p = iterator->data_ptr;
3996                 int                     i;
3997
3998                 for (i = 0; i < iterator->slice_len; i++)
3999                 {
4000                         if (array_get_isnull(iterator->nullbitmap,
4001                                                                  iterator->current_item++))
4002                         {
4003                                 nulls[i] = true;
4004                                 values[i] = (Datum) 0;
4005                         }
4006                         else
4007                         {
4008                                 nulls[i] = false;
4009                                 values[i] = fetch_att(p, iterator->typbyval, iterator->typlen);
4010
4011                                 /* Move our data pointer forward to the next element */
4012                                 p = att_addlength_pointer(p, iterator->typlen, p);
4013                                 p = (char *) att_align_nominal(p, iterator->typalign);
4014                         }
4015                 }
4016
4017                 iterator->data_ptr = p;
4018
4019                 result = construct_md_array(values,
4020                                                                         nulls,
4021                                                                         iterator->slice_ndim,
4022                                                                         iterator->slice_dims,
4023                                                                         iterator->slice_lbound,
4024                                                                         ARR_ELEMTYPE(iterator->arr),
4025                                                                         iterator->typlen,
4026                                                                         iterator->typbyval,
4027                                                                         iterator->typalign);
4028
4029                 *isnull = false;
4030                 *value = PointerGetDatum(result);
4031         }
4032
4033         return true;
4034 }
4035
4036 /*
4037  * Release an ArrayIterator data structure
4038  */
4039 void
4040 array_free_iterator(ArrayIterator iterator)
4041 {
4042         if (iterator->slice_ndim > 0)
4043         {
4044                 pfree(iterator->slice_values);
4045                 pfree(iterator->slice_nulls);
4046         }
4047         pfree(iterator);
4048 }
4049
4050
4051 /***************************************************************************/
4052 /******************|              Support  Routines                       |*****************/
4053 /***************************************************************************/
4054
4055 /*
4056  * Check whether a specific array element is NULL
4057  *
4058  * nullbitmap: pointer to array's null bitmap (NULL if none)
4059  * offset: 0-based linear element number of array element
4060  */
4061 static bool
4062 array_get_isnull(const bits8 *nullbitmap, int offset)
4063 {
4064         if (nullbitmap == NULL)
4065                 return false;                   /* assume not null */
4066         if (nullbitmap[offset / 8] & (1 << (offset % 8)))
4067                 return false;                   /* not null */
4068         return true;
4069 }
4070
4071 /*
4072  * Set a specific array element's null-bitmap entry
4073  *
4074  * nullbitmap: pointer to array's null bitmap (mustn't be NULL)
4075  * offset: 0-based linear element number of array element
4076  * isNull: null status to set
4077  */
4078 static void
4079 array_set_isnull(bits8 *nullbitmap, int offset, bool isNull)
4080 {
4081         int                     bitmask;
4082
4083         nullbitmap += offset / 8;
4084         bitmask = 1 << (offset % 8);
4085         if (isNull)
4086                 *nullbitmap &= ~bitmask;
4087         else
4088                 *nullbitmap |= bitmask;
4089 }
4090
4091 /*
4092  * Fetch array element at pointer, converted correctly to a Datum
4093  *
4094  * Caller must have handled case of NULL element
4095  */
4096 static Datum
4097 ArrayCast(char *value, bool byval, int len)
4098 {
4099         return fetch_att(value, byval, len);
4100 }
4101
4102 /*
4103  * Copy datum to *dest and return total space used (including align padding)
4104  *
4105  * Caller must have handled case of NULL element
4106  */
4107 static int
4108 ArrayCastAndSet(Datum src,
4109                                 int typlen,
4110                                 bool typbyval,
4111                                 char typalign,
4112                                 char *dest)
4113 {
4114         int                     inc;
4115
4116         if (typlen > 0)
4117         {
4118                 if (typbyval)
4119                         store_att_byval(dest, src, typlen);
4120                 else
4121                         memmove(dest, DatumGetPointer(src), typlen);
4122                 inc = att_align_nominal(typlen, typalign);
4123         }
4124         else
4125         {
4126                 Assert(!typbyval);
4127                 inc = att_addlength_datum(0, typlen, src);
4128                 memmove(dest, DatumGetPointer(src), inc);
4129                 inc = att_align_nominal(inc, typalign);
4130         }
4131
4132         return inc;
4133 }
4134
4135 /*
4136  * Advance ptr over nitems array elements
4137  *
4138  * ptr: starting location in array
4139  * offset: 0-based linear element number of first element (the one at *ptr)
4140  * nullbitmap: start of array's null bitmap, or NULL if none
4141  * nitems: number of array elements to advance over (>= 0)
4142  * typlen, typbyval, typalign: storage parameters of array element datatype
4143  *
4144  * It is caller's responsibility to ensure that nitems is within range
4145  */
4146 static char *
4147 array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
4148                    int typlen, bool typbyval, char typalign)
4149 {
4150         int                     bitmask;
4151         int                     i;
4152
4153         /* easy if fixed-size elements and no NULLs */
4154         if (typlen > 0 && !nullbitmap)
4155                 return ptr + nitems * ((Size) att_align_nominal(typlen, typalign));
4156
4157         /* seems worth having separate loops for NULL and no-NULLs cases */
4158         if (nullbitmap)
4159         {
4160                 nullbitmap += offset / 8;
4161                 bitmask = 1 << (offset % 8);
4162
4163                 for (i = 0; i < nitems; i++)
4164                 {
4165                         if (*nullbitmap & bitmask)
4166                         {
4167                                 ptr = att_addlength_pointer(ptr, typlen, ptr);
4168                                 ptr = (char *) att_align_nominal(ptr, typalign);
4169                         }
4170                         bitmask <<= 1;
4171                         if (bitmask == 0x100)
4172                         {
4173                                 nullbitmap++;
4174                                 bitmask = 1;
4175                         }
4176                 }
4177         }
4178         else
4179         {
4180                 for (i = 0; i < nitems; i++)
4181                 {
4182                         ptr = att_addlength_pointer(ptr, typlen, ptr);
4183                         ptr = (char *) att_align_nominal(ptr, typalign);
4184                 }
4185         }
4186         return ptr;
4187 }
4188
4189 /*
4190  * Compute total size of the nitems array elements starting at *ptr
4191  *
4192  * Parameters same as for array_seek
4193  */
4194 static int
4195 array_nelems_size(char *ptr, int offset, bits8 *nullbitmap, int nitems,
4196                                   int typlen, bool typbyval, char typalign)
4197 {
4198         return array_seek(ptr, offset, nullbitmap, nitems,
4199                                           typlen, typbyval, typalign) - ptr;
4200 }
4201
4202 /*
4203  * Copy nitems array elements from srcptr to destptr
4204  *
4205  * destptr: starting destination location (must be enough room!)
4206  * nitems: number of array elements to copy (>= 0)
4207  * srcptr: starting location in source array
4208  * offset: 0-based linear element number of first element (the one at *srcptr)
4209  * nullbitmap: start of source array's null bitmap, or NULL if none
4210  * typlen, typbyval, typalign: storage parameters of array element datatype
4211  *
4212  * Returns number of bytes copied
4213  *
4214  * NB: this does not take care of setting up the destination's null bitmap!
4215  */
4216 static int
4217 array_copy(char *destptr, int nitems,
4218                    char *srcptr, int offset, bits8 *nullbitmap,
4219                    int typlen, bool typbyval, char typalign)
4220 {
4221         int                     numbytes;
4222
4223         numbytes = array_nelems_size(srcptr, offset, nullbitmap, nitems,
4224                                                                  typlen, typbyval, typalign);
4225         memcpy(destptr, srcptr, numbytes);
4226         return numbytes;
4227 }
4228
4229 /*
4230  * Copy nitems null-bitmap bits from source to destination
4231  *
4232  * destbitmap: start of destination array's null bitmap (mustn't be NULL)
4233  * destoffset: 0-based linear element number of first dest element
4234  * srcbitmap: start of source array's null bitmap, or NULL if none
4235  * srcoffset: 0-based linear element number of first source element
4236  * nitems: number of bits to copy (>= 0)
4237  *
4238  * If srcbitmap is NULL then we assume the source is all-non-NULL and
4239  * fill 1's into the destination bitmap.  Note that only the specified
4240  * bits in the destination map are changed, not any before or after.
4241  *
4242  * Note: this could certainly be optimized using standard bitblt methods.
4243  * However, it's not clear that the typical Postgres array has enough elements
4244  * to make it worth worrying too much.  For the moment, KISS.
4245  */
4246 void
4247 array_bitmap_copy(bits8 *destbitmap, int destoffset,
4248                                   const bits8 *srcbitmap, int srcoffset,
4249                                   int nitems)
4250 {
4251         int                     destbitmask,
4252                                 destbitval,
4253                                 srcbitmask,
4254                                 srcbitval;
4255
4256         Assert(destbitmap);
4257         if (nitems <= 0)
4258                 return;                                 /* don't risk fetch off end of memory */
4259         destbitmap += destoffset / 8;
4260         destbitmask = 1 << (destoffset % 8);
4261         destbitval = *destbitmap;
4262         if (srcbitmap)
4263         {
4264                 srcbitmap += srcoffset / 8;
4265                 srcbitmask = 1 << (srcoffset % 8);
4266                 srcbitval = *srcbitmap;
4267                 while (nitems-- > 0)
4268                 {
4269                         if (srcbitval & srcbitmask)
4270                                 destbitval |= destbitmask;
4271                         else
4272                                 destbitval &= ~destbitmask;
4273                         destbitmask <<= 1;
4274                         if (destbitmask == 0x100)
4275                         {
4276                                 *destbitmap++ = destbitval;
4277                                 destbitmask = 1;
4278                                 if (nitems > 0)
4279                                         destbitval = *destbitmap;
4280                         }
4281                         srcbitmask <<= 1;
4282                         if (srcbitmask == 0x100)
4283                         {
4284                                 srcbitmap++;
4285                                 srcbitmask = 1;
4286                                 if (nitems > 0)
4287                                         srcbitval = *srcbitmap;
4288                         }
4289                 }
4290                 if (destbitmask != 1)
4291                         *destbitmap = destbitval;
4292         }
4293         else
4294         {
4295                 while (nitems-- > 0)
4296                 {
4297                         destbitval |= destbitmask;
4298                         destbitmask <<= 1;
4299                         if (destbitmask == 0x100)
4300                         {
4301                                 *destbitmap++ = destbitval;
4302                                 destbitmask = 1;
4303                                 if (nitems > 0)
4304                                         destbitval = *destbitmap;
4305                         }
4306                 }
4307                 if (destbitmask != 1)
4308                         *destbitmap = destbitval;
4309         }
4310 }
4311
4312 /*
4313  * Compute space needed for a slice of an array
4314  *
4315  * We assume the caller has verified that the slice coordinates are valid.
4316  */
4317 static int
4318 array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
4319                                  int ndim, int *dim, int *lb,
4320                                  int *st, int *endp,
4321                                  int typlen, bool typbyval, char typalign)
4322 {
4323         int                     src_offset,
4324                                 span[MAXDIM],
4325                                 prod[MAXDIM],
4326                                 dist[MAXDIM],
4327                                 indx[MAXDIM];
4328         char       *ptr;
4329         int                     i,
4330                                 j,
4331                                 inc;
4332         int                     count = 0;
4333
4334         mda_get_range(ndim, span, st, endp);
4335
4336         /* Pretty easy for fixed element length without nulls ... */
4337         if (typlen > 0 && !arraynullsptr)
4338                 return ArrayGetNItems(ndim, span) * att_align_nominal(typlen, typalign);
4339
4340         /* Else gotta do it the hard way */
4341         src_offset = ArrayGetOffset(ndim, dim, lb, st);
4342         ptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
4343                                          typlen, typbyval, typalign);
4344         mda_get_prod(ndim, dim, prod);
4345         mda_get_offset_values(ndim, dist, prod, span);
4346         for (i = 0; i < ndim; i++)
4347                 indx[i] = 0;
4348         j = ndim - 1;
4349         do
4350         {
4351                 if (dist[j])
4352                 {
4353                         ptr = array_seek(ptr, src_offset, arraynullsptr, dist[j],
4354                                                          typlen, typbyval, typalign);
4355                         src_offset += dist[j];
4356                 }
4357                 if (!array_get_isnull(arraynullsptr, src_offset))
4358                 {
4359                         inc = att_addlength_pointer(0, typlen, ptr);
4360                         inc = att_align_nominal(inc, typalign);
4361                         ptr += inc;
4362                         count += inc;
4363                 }
4364                 src_offset++;
4365         } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
4366         return count;
4367 }
4368
4369 /*
4370  * Extract a slice of an array into consecutive elements in the destination
4371  * array.
4372  *
4373  * We assume the caller has verified that the slice coordinates are valid,
4374  * allocated enough storage for the result, and initialized the header
4375  * of the new array.
4376  */
4377 static void
4378 array_extract_slice(ArrayType *newarray,
4379                                         int ndim,
4380                                         int *dim,
4381                                         int *lb,
4382                                         char *arraydataptr,
4383                                         bits8 *arraynullsptr,
4384                                         int *st,
4385                                         int *endp,
4386                                         int typlen,
4387                                         bool typbyval,
4388                                         char typalign)
4389 {
4390         char       *destdataptr = ARR_DATA_PTR(newarray);
4391         bits8      *destnullsptr = ARR_NULLBITMAP(newarray);
4392         char       *srcdataptr;
4393         int                     src_offset,
4394                                 dest_offset,
4395                                 prod[MAXDIM],
4396                                 span[MAXDIM],
4397                                 dist[MAXDIM],
4398                                 indx[MAXDIM];
4399         int                     i,
4400                                 j,
4401                                 inc;
4402
4403         src_offset = ArrayGetOffset(ndim, dim, lb, st);
4404         srcdataptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
4405                                                         typlen, typbyval, typalign);
4406         mda_get_prod(ndim, dim, prod);
4407         mda_get_range(ndim, span, st, endp);
4408         mda_get_offset_values(ndim, dist, prod, span);
4409         for (i = 0; i < ndim; i++)
4410                 indx[i] = 0;
4411         dest_offset = 0;
4412         j = ndim - 1;
4413         do
4414         {
4415                 if (dist[j])
4416                 {
4417                         /* skip unwanted elements */
4418                         srcdataptr = array_seek(srcdataptr, src_offset, arraynullsptr,
4419                                                                         dist[j],
4420                                                                         typlen, typbyval, typalign);
4421                         src_offset += dist[j];
4422                 }
4423                 inc = array_copy(destdataptr, 1,
4424                                                  srcdataptr, src_offset, arraynullsptr,
4425                                                  typlen, typbyval, typalign);
4426                 if (destnullsptr)
4427                         array_bitmap_copy(destnullsptr, dest_offset,
4428                                                           arraynullsptr, src_offset,
4429                                                           1);
4430                 destdataptr += inc;
4431                 srcdataptr += inc;
4432                 src_offset++;
4433                 dest_offset++;
4434         } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
4435 }
4436
4437 /*
4438  * Insert a slice into an array.
4439  *
4440  * ndim/dim[]/lb[] are dimensions of the original array.  A new array with
4441  * those same dimensions is to be constructed.  destArray must already
4442  * have been allocated and its header initialized.
4443  *
4444  * st[]/endp[] identify the slice to be replaced.  Elements within the slice
4445  * volume are taken from consecutive elements of the srcArray; elements
4446  * outside it are copied from origArray.
4447  *
4448  * We assume the caller has verified that the slice coordinates are valid.
4449  */
4450 static void
4451 array_insert_slice(ArrayType *destArray,
4452                                    ArrayType *origArray,
4453                                    ArrayType *srcArray,
4454                                    int ndim,
4455                                    int *dim,
4456                                    int *lb,
4457                                    int *st,
4458                                    int *endp,
4459                                    int typlen,
4460                                    bool typbyval,
4461                                    char typalign)
4462 {
4463         char       *destPtr = ARR_DATA_PTR(destArray);
4464         char       *origPtr = ARR_DATA_PTR(origArray);
4465         char       *srcPtr = ARR_DATA_PTR(srcArray);
4466         bits8      *destBitmap = ARR_NULLBITMAP(destArray);
4467         bits8      *origBitmap = ARR_NULLBITMAP(origArray);
4468         bits8      *srcBitmap = ARR_NULLBITMAP(srcArray);
4469         int                     orignitems = ArrayGetNItems(ARR_NDIM(origArray),
4470                                                                                         ARR_DIMS(origArray));
4471         int                     dest_offset,
4472                                 orig_offset,
4473                                 src_offset,
4474                                 prod[MAXDIM],
4475                                 span[MAXDIM],
4476                                 dist[MAXDIM],
4477                                 indx[MAXDIM];
4478         int                     i,
4479                                 j,
4480                                 inc;
4481
4482         dest_offset = ArrayGetOffset(ndim, dim, lb, st);
4483         /* copy items before the slice start */
4484         inc = array_copy(destPtr, dest_offset,
4485                                          origPtr, 0, origBitmap,
4486                                          typlen, typbyval, typalign);
4487         destPtr += inc;
4488         origPtr += inc;
4489         if (destBitmap)
4490                 array_bitmap_copy(destBitmap, 0, origBitmap, 0, dest_offset);
4491         orig_offset = dest_offset;
4492         mda_get_prod(ndim, dim, prod);
4493         mda_get_range(ndim, span, st, endp);
4494         mda_get_offset_values(ndim, dist, prod, span);
4495         for (i = 0; i < ndim; i++)
4496                 indx[i] = 0;
4497         src_offset = 0;
4498         j = ndim - 1;
4499         do
4500         {
4501                 /* Copy/advance over elements between here and next part of slice */
4502                 if (dist[j])
4503                 {
4504                         inc = array_copy(destPtr, dist[j],
4505                                                          origPtr, orig_offset, origBitmap,
4506                                                          typlen, typbyval, typalign);
4507                         destPtr += inc;
4508                         origPtr += inc;
4509                         if (destBitmap)
4510                                 array_bitmap_copy(destBitmap, dest_offset,
4511                                                                   origBitmap, orig_offset,
4512                                                                   dist[j]);
4513                         dest_offset += dist[j];
4514                         orig_offset += dist[j];
4515                 }
4516                 /* Copy new element at this slice position */
4517                 inc = array_copy(destPtr, 1,
4518                                                  srcPtr, src_offset, srcBitmap,
4519                                                  typlen, typbyval, typalign);
4520                 if (destBitmap)
4521                         array_bitmap_copy(destBitmap, dest_offset,
4522                                                           srcBitmap, src_offset,
4523                                                           1);
4524                 destPtr += inc;
4525                 srcPtr += inc;
4526                 dest_offset++;
4527                 src_offset++;
4528                 /* Advance over old element at this slice position */
4529                 origPtr = array_seek(origPtr, orig_offset, origBitmap, 1,
4530                                                          typlen, typbyval, typalign);
4531                 orig_offset++;
4532         } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
4533
4534         /* don't miss any data at the end */
4535         array_copy(destPtr, orignitems - orig_offset,
4536                            origPtr, orig_offset, origBitmap,
4537                            typlen, typbyval, typalign);
4538         if (destBitmap)
4539                 array_bitmap_copy(destBitmap, dest_offset,
4540                                                   origBitmap, orig_offset,
4541                                                   orignitems - orig_offset);
4542 }
4543
4544 /*
4545  * accumArrayResult - accumulate one (more) Datum for an array result
4546  *
4547  *      astate is working state (NULL on first call)
4548  *      rcontext is where to keep working state
4549  */
4550 ArrayBuildState *
4551 accumArrayResult(ArrayBuildState *astate,
4552                                  Datum dvalue, bool disnull,
4553                                  Oid element_type,
4554                                  MemoryContext rcontext)
4555 {
4556         MemoryContext arr_context,
4557                                 oldcontext;
4558
4559         if (astate == NULL)
4560         {
4561                 /* First time through --- initialize */
4562
4563                 /* Make a temporary context to hold all the junk */
4564                 arr_context = AllocSetContextCreate(rcontext,
4565                                                                                         "accumArrayResult",
4566                                                                                         ALLOCSET_DEFAULT_MINSIZE,
4567                                                                                         ALLOCSET_DEFAULT_INITSIZE,
4568                                                                                         ALLOCSET_DEFAULT_MAXSIZE);
4569                 oldcontext = MemoryContextSwitchTo(arr_context);
4570                 astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
4571                 astate->mcontext = arr_context;
4572                 astate->alen = 64;              /* arbitrary starting array size */
4573                 astate->dvalues = (Datum *) palloc(astate->alen * sizeof(Datum));
4574                 astate->dnulls = (bool *) palloc(astate->alen * sizeof(bool));
4575                 astate->nelems = 0;
4576                 astate->element_type = element_type;
4577                 get_typlenbyvalalign(element_type,
4578                                                          &astate->typlen,
4579                                                          &astate->typbyval,
4580                                                          &astate->typalign);
4581         }
4582         else
4583         {
4584                 oldcontext = MemoryContextSwitchTo(astate->mcontext);
4585                 Assert(astate->element_type == element_type);
4586                 /* enlarge dvalues[]/dnulls[] if needed */
4587                 if (astate->nelems >= astate->alen)
4588                 {
4589                         astate->alen *= 2;
4590                         astate->dvalues = (Datum *)
4591                                 repalloc(astate->dvalues, astate->alen * sizeof(Datum));
4592                         astate->dnulls = (bool *)
4593                                 repalloc(astate->dnulls, astate->alen * sizeof(bool));
4594                 }
4595         }
4596
4597         /*
4598          * Ensure pass-by-ref stuff is copied into mcontext; and detoast it too if
4599          * it's varlena.  (You might think that detoasting is not needed here
4600          * because construct_md_array can detoast the array elements later.
4601          * However, we must not let construct_md_array modify the ArrayBuildState
4602          * because that would mean array_agg_finalfn damages its input, which is
4603          * verboten.  Also, this way frequently saves one copying step.)
4604          */
4605         if (!disnull && !astate->typbyval)
4606         {
4607                 if (astate->typlen == -1)
4608                         dvalue = PointerGetDatum(PG_DETOAST_DATUM_COPY(dvalue));
4609                 else
4610                         dvalue = datumCopy(dvalue, astate->typbyval, astate->typlen);
4611         }
4612
4613         astate->dvalues[astate->nelems] = dvalue;
4614         astate->dnulls[astate->nelems] = disnull;
4615         astate->nelems++;
4616
4617         MemoryContextSwitchTo(oldcontext);
4618
4619         return astate;
4620 }
4621
4622 /*
4623  * makeArrayResult - produce 1-D final result of accumArrayResult
4624  *
4625  *      astate is working state (not NULL)
4626  *      rcontext is where to construct result
4627  */
4628 Datum
4629 makeArrayResult(ArrayBuildState *astate,
4630                                 MemoryContext rcontext)
4631 {
4632         int                     dims[1];
4633         int                     lbs[1];
4634
4635         dims[0] = astate->nelems;
4636         lbs[0] = 1;
4637
4638         return makeMdArrayResult(astate, 1, dims, lbs, rcontext, true);
4639 }
4640
4641 /*
4642  * makeMdArrayResult - produce multi-D final result of accumArrayResult
4643  *
4644  * beware: no check that specified dimensions match the number of values
4645  * accumulated.
4646  *
4647  *      astate is working state (not NULL)
4648  *      rcontext is where to construct result
4649  *      release is true if okay to release working state
4650  */
4651 Datum
4652 makeMdArrayResult(ArrayBuildState *astate,
4653                                   int ndims,
4654                                   int *dims,
4655                                   int *lbs,
4656                                   MemoryContext rcontext,
4657                                   bool release)
4658 {
4659         ArrayType  *result;
4660         MemoryContext oldcontext;
4661
4662         /* Build the final array result in rcontext */
4663         oldcontext = MemoryContextSwitchTo(rcontext);
4664
4665         result = construct_md_array(astate->dvalues,
4666                                                                 astate->dnulls,
4667                                                                 ndims,
4668                                                                 dims,
4669                                                                 lbs,
4670                                                                 astate->element_type,
4671                                                                 astate->typlen,
4672                                                                 astate->typbyval,
4673                                                                 astate->typalign);
4674
4675         MemoryContextSwitchTo(oldcontext);
4676
4677         /* Clean up all the junk */
4678         if (release)
4679                 MemoryContextDelete(astate->mcontext);
4680
4681         return PointerGetDatum(result);
4682 }
4683
4684 Datum
4685 array_larger(PG_FUNCTION_ARGS)
4686 {
4687         ArrayType  *v1,
4688                            *v2,
4689                            *result;
4690
4691         v1 = PG_GETARG_ARRAYTYPE_P(0);
4692         v2 = PG_GETARG_ARRAYTYPE_P(1);
4693
4694         result = ((array_cmp(fcinfo) > 0) ? v1 : v2);
4695
4696         PG_RETURN_ARRAYTYPE_P(result);
4697 }
4698
4699 Datum
4700 array_smaller(PG_FUNCTION_ARGS)
4701 {
4702         ArrayType  *v1,
4703                            *v2,
4704                            *result;
4705
4706         v1 = PG_GETARG_ARRAYTYPE_P(0);
4707         v2 = PG_GETARG_ARRAYTYPE_P(1);
4708
4709         result = ((array_cmp(fcinfo) < 0) ? v1 : v2);
4710
4711         PG_RETURN_ARRAYTYPE_P(result);
4712 }
4713
4714
4715 typedef struct generate_subscripts_fctx
4716 {
4717         int4            lower;
4718         int4            upper;
4719         bool            reverse;
4720 } generate_subscripts_fctx;
4721
4722 /*
4723  * generate_subscripts(array anyarray, dim int [, reverse bool])
4724  *              Returns all subscripts of the array for any dimension
4725  */
4726 Datum
4727 generate_subscripts(PG_FUNCTION_ARGS)
4728 {
4729         FuncCallContext *funcctx;
4730         MemoryContext oldcontext;
4731         generate_subscripts_fctx *fctx;
4732
4733         /* stuff done only on the first call of the function */
4734         if (SRF_IS_FIRSTCALL())
4735         {
4736                 ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
4737                 int                     reqdim = PG_GETARG_INT32(1);
4738                 int                *lb,
4739                                    *dimv;
4740
4741                 /* create a function context for cross-call persistence */
4742                 funcctx = SRF_FIRSTCALL_INIT();
4743
4744                 /* Sanity check: does it look like an array at all? */
4745                 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
4746                         SRF_RETURN_DONE(funcctx);
4747
4748                 /* Sanity check: was the requested dim valid */
4749                 if (reqdim <= 0 || reqdim > ARR_NDIM(v))
4750                         SRF_RETURN_DONE(funcctx);
4751
4752                 /*
4753                  * switch to memory context appropriate for multiple function calls
4754                  */
4755                 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
4756                 fctx = (generate_subscripts_fctx *) palloc(sizeof(generate_subscripts_fctx));
4757
4758                 lb = ARR_LBOUND(v);
4759                 dimv = ARR_DIMS(v);
4760
4761                 fctx->lower = lb[reqdim - 1];
4762                 fctx->upper = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
4763                 fctx->reverse = (PG_NARGS() < 3) ? false : PG_GETARG_BOOL(2);
4764
4765                 funcctx->user_fctx = fctx;
4766
4767                 MemoryContextSwitchTo(oldcontext);
4768         }
4769
4770         funcctx = SRF_PERCALL_SETUP();
4771
4772         fctx = funcctx->user_fctx;
4773
4774         if (fctx->lower <= fctx->upper)
4775         {
4776                 if (!fctx->reverse)
4777                         SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->lower++));
4778                 else
4779                         SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->upper--));
4780         }
4781         else
4782                 /* done when there are no more elements left */
4783                 SRF_RETURN_DONE(funcctx);
4784 }
4785
4786 /*
4787  * generate_subscripts_nodir
4788  *              Implements the 2-argument version of generate_subscripts
4789  */
4790 Datum
4791 generate_subscripts_nodir(PG_FUNCTION_ARGS)
4792 {
4793         /* just call the other one -- it can handle both cases */
4794         return generate_subscripts(fcinfo);
4795 }
4796
4797 /*
4798  * array_fill_with_lower_bounds
4799  *              Create and fill array with defined lower bounds.
4800  */
4801 Datum
4802 array_fill_with_lower_bounds(PG_FUNCTION_ARGS)
4803 {
4804         ArrayType  *dims;
4805         ArrayType  *lbs;
4806         ArrayType  *result;
4807         Oid                     elmtype;
4808         Datum           value;
4809         bool            isnull;
4810
4811         if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
4812                 ereport(ERROR,
4813                                 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
4814                            errmsg("dimension array or low bound array cannot be null")));
4815
4816         dims = PG_GETARG_ARRAYTYPE_P(1);
4817         lbs = PG_GETARG_ARRAYTYPE_P(2);
4818
4819         if (!PG_ARGISNULL(0))
4820         {
4821                 value = PG_GETARG_DATUM(0);
4822                 isnull = false;
4823         }
4824         else
4825         {
4826                 value = 0;
4827                 isnull = true;
4828         }
4829
4830         elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
4831         if (!OidIsValid(elmtype))
4832                 elog(ERROR, "could not determine data type of input");
4833
4834         result = array_fill_internal(dims, lbs, value, isnull, elmtype, fcinfo);
4835         PG_RETURN_ARRAYTYPE_P(result);
4836 }
4837
4838 /*
4839  * array_fill
4840  *              Create and fill array with default lower bounds.
4841  */
4842 Datum
4843 array_fill(PG_FUNCTION_ARGS)
4844 {
4845         ArrayType  *dims;
4846         ArrayType  *result;
4847         Oid                     elmtype;
4848         Datum           value;
4849         bool            isnull;
4850
4851         if (PG_ARGISNULL(1))
4852                 ereport(ERROR,
4853                                 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
4854                            errmsg("dimension array or low bound array cannot be null")));
4855
4856         dims = PG_GETARG_ARRAYTYPE_P(1);
4857
4858         if (!PG_ARGISNULL(0))
4859         {
4860                 value = PG_GETARG_DATUM(0);
4861                 isnull = false;
4862         }
4863         else
4864         {
4865                 value = 0;
4866                 isnull = true;
4867         }
4868
4869         elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
4870         if (!OidIsValid(elmtype))
4871                 elog(ERROR, "could not determine data type of input");
4872
4873         result = array_fill_internal(dims, NULL, value, isnull, elmtype, fcinfo);
4874         PG_RETURN_ARRAYTYPE_P(result);
4875 }
4876
4877 static ArrayType *
4878 create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
4879                                           Oid elmtype, int dataoffset)
4880 {
4881         ArrayType  *result;
4882
4883         result = (ArrayType *) palloc0(nbytes);
4884         SET_VARSIZE(result, nbytes);
4885         result->ndim = ndims;
4886         result->dataoffset = dataoffset;
4887         result->elemtype = elmtype;
4888         memcpy(ARR_DIMS(result), dimv, ndims * sizeof(int));
4889         memcpy(ARR_LBOUND(result), lbsv, ndims * sizeof(int));
4890
4891         return result;
4892 }
4893
4894 static ArrayType *
4895 array_fill_internal(ArrayType *dims, ArrayType *lbs,
4896                                         Datum value, bool isnull, Oid elmtype,
4897                                         FunctionCallInfo fcinfo)
4898 {
4899         ArrayType  *result;
4900         int                *dimv;
4901         int                *lbsv;
4902         int                     ndims;
4903         int                     nitems;
4904         int                     deflbs[MAXDIM];
4905         int16           elmlen;
4906         bool            elmbyval;
4907         char            elmalign;
4908         ArrayMetaState *my_extra;
4909
4910         /*
4911          * Params checks
4912          */
4913         if (ARR_NDIM(dims) != 1)
4914                 ereport(ERROR,
4915                                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4916                                  errmsg("wrong number of array subscripts"),
4917                                  errdetail("Dimension array must be one dimensional.")));
4918
4919         if (ARR_LBOUND(dims)[0] != 1)
4920                 ereport(ERROR,
4921                                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4922                                  errmsg("wrong range of array subscripts"),
4923                                  errdetail("Lower bound of dimension array must be one.")));
4924
4925         if (array_contains_nulls(dims))
4926                 ereport(ERROR,
4927                                 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
4928                                  errmsg("dimension values cannot be null")));
4929
4930         dimv = (int *) ARR_DATA_PTR(dims);
4931         ndims = ARR_DIMS(dims)[0];
4932
4933         if (ndims < 0)                          /* we do allow zero-dimension arrays */
4934                 ereport(ERROR,
4935                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4936                                  errmsg("invalid number of dimensions: %d", ndims)));
4937         if (ndims > MAXDIM)
4938                 ereport(ERROR,
4939                                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
4940                                  errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
4941                                                 ndims, MAXDIM)));
4942
4943         if (lbs != NULL)
4944         {
4945                 if (ARR_NDIM(lbs) != 1)
4946                         ereport(ERROR,
4947                                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4948                                          errmsg("wrong number of array subscripts"),
4949                                          errdetail("Dimension array must be one dimensional.")));
4950
4951                 if (ARR_LBOUND(lbs)[0] != 1)
4952                         ereport(ERROR,
4953                                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4954                                          errmsg("wrong range of array subscripts"),
4955                                   errdetail("Lower bound of dimension array must be one.")));
4956
4957                 if (array_contains_nulls(lbs))
4958                         ereport(ERROR,
4959                                         (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
4960                                          errmsg("dimension values cannot be null")));
4961
4962                 if (ARR_DIMS(lbs)[0] != ndims)
4963                         ereport(ERROR,
4964                                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4965                                          errmsg("wrong number of array subscripts"),
4966                                          errdetail("Low bound array has different size than dimensions array.")));
4967
4968                 lbsv = (int *) ARR_DATA_PTR(lbs);
4969         }
4970         else
4971         {
4972                 int                     i;
4973
4974                 for (i = 0; i < MAXDIM; i++)
4975                         deflbs[i] = 1;
4976
4977                 lbsv = deflbs;
4978         }
4979
4980         /* fast track for empty array */
4981         if (ndims == 0)
4982                 return construct_empty_array(elmtype);
4983
4984         nitems = ArrayGetNItems(ndims, dimv);
4985
4986         /*
4987          * We arrange to look up info about element type only once per series of
4988          * calls, assuming the element type doesn't change underneath us.
4989          */
4990         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
4991         if (my_extra == NULL)
4992         {
4993                 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
4994                                                                                                           sizeof(ArrayMetaState));
4995                 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
4996                 my_extra->element_type = InvalidOid;
4997         }
4998
4999         if (my_extra->element_type != elmtype)
5000         {
5001                 /* Get info about element type */
5002                 get_typlenbyvalalign(elmtype,
5003                                                          &my_extra->typlen,
5004                                                          &my_extra->typbyval,
5005                                                          &my_extra->typalign);
5006                 my_extra->element_type = elmtype;
5007         }
5008
5009         elmlen = my_extra->typlen;
5010         elmbyval = my_extra->typbyval;
5011         elmalign = my_extra->typalign;
5012
5013         /* compute required space */
5014         if (!isnull)
5015         {
5016                 int                     i;
5017                 char       *p;
5018                 int                     nbytes;
5019                 int                     totbytes;
5020
5021                 /* make sure data is not toasted */
5022                 if (elmlen == -1)
5023                         value = PointerGetDatum(PG_DETOAST_DATUM(value));
5024
5025                 nbytes = att_addlength_datum(0, elmlen, value);
5026                 nbytes = att_align_nominal(nbytes, elmalign);
5027                 Assert(nbytes > 0);
5028
5029                 totbytes = nbytes * nitems;
5030
5031                 /* check for overflow of multiplication or total request */
5032                 if (totbytes / nbytes != nitems ||
5033                         !AllocSizeIsValid(totbytes))
5034                         ereport(ERROR,
5035                                         (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
5036                                          errmsg("array size exceeds the maximum allowed (%d)",
5037                                                         (int) MaxAllocSize)));
5038
5039                 /*
5040                  * This addition can't overflow, but it might cause us to go past
5041                  * MaxAllocSize.  We leave it to palloc to complain in that case.
5042                  */
5043                 totbytes += ARR_OVERHEAD_NONULLS(ndims);
5044
5045                 result = create_array_envelope(ndims, dimv, lbsv, totbytes,
5046                                                                            elmtype, 0);
5047
5048                 p = ARR_DATA_PTR(result);
5049                 for (i = 0; i < nitems; i++)
5050                         p += ArrayCastAndSet(value, elmlen, elmbyval, elmalign, p);
5051         }
5052         else
5053         {
5054                 int                     nbytes;
5055                 int                     dataoffset;
5056
5057                 dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
5058                 nbytes = dataoffset;
5059
5060                 result = create_array_envelope(ndims, dimv, lbsv, nbytes,
5061                                                                            elmtype, dataoffset);
5062
5063                 /* create_array_envelope already zeroed the bitmap, so we're done */
5064         }
5065
5066         return result;
5067 }
5068
5069
5070 /*
5071  * UNNEST
5072  */
5073 Datum
5074 array_unnest(PG_FUNCTION_ARGS)
5075 {
5076         typedef struct
5077         {
5078                 ArrayType  *arr;
5079                 int                     nextelem;
5080                 int                     numelems;
5081                 char       *elemdataptr;        /* this moves with nextelem */
5082                 bits8      *arraynullsptr;              /* this does not */
5083                 int16           elmlen;
5084                 bool            elmbyval;
5085                 char            elmalign;
5086         } array_unnest_fctx;
5087
5088         FuncCallContext *funcctx;
5089         array_unnest_fctx *fctx;
5090         MemoryContext oldcontext;
5091
5092         /* stuff done only on the first call of the function */
5093         if (SRF_IS_FIRSTCALL())
5094         {
5095                 ArrayType  *arr;
5096
5097                 /* create a function context for cross-call persistence */
5098                 funcctx = SRF_FIRSTCALL_INIT();
5099
5100                 /*
5101                  * switch to memory context appropriate for multiple function calls
5102                  */
5103                 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
5104
5105                 /*
5106                  * Get the array value and detoast if needed.  We can't do this
5107                  * earlier because if we have to detoast, we want the detoasted copy
5108                  * to be in multi_call_memory_ctx, so it will go away when we're done
5109                  * and not before.      (If no detoast happens, we assume the originally
5110                  * passed array will stick around till then.)
5111                  */
5112                 arr = PG_GETARG_ARRAYTYPE_P(0);
5113
5114                 /* allocate memory for user context */
5115                 fctx = (array_unnest_fctx *) palloc(sizeof(array_unnest_fctx));
5116
5117                 /* initialize state */
5118                 fctx->arr = arr;
5119                 fctx->nextelem = 0;
5120                 fctx->numelems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
5121
5122                 fctx->elemdataptr = ARR_DATA_PTR(arr);
5123                 fctx->arraynullsptr = ARR_NULLBITMAP(arr);
5124
5125                 get_typlenbyvalalign(ARR_ELEMTYPE(arr),
5126                                                          &fctx->elmlen,
5127                                                          &fctx->elmbyval,
5128                                                          &fctx->elmalign);
5129
5130                 funcctx->user_fctx = fctx;
5131                 MemoryContextSwitchTo(oldcontext);
5132         }
5133
5134         /* stuff done on every call of the function */
5135         funcctx = SRF_PERCALL_SETUP();
5136         fctx = funcctx->user_fctx;
5137
5138         if (fctx->nextelem < fctx->numelems)
5139         {
5140                 int                     offset = fctx->nextelem++;
5141                 Datum           elem;
5142
5143                 /*
5144                  * Check for NULL array element
5145                  */
5146                 if (array_get_isnull(fctx->arraynullsptr, offset))
5147                 {
5148                         fcinfo->isnull = true;
5149                         elem = (Datum) 0;
5150                         /* elemdataptr does not move */
5151                 }
5152                 else
5153                 {
5154                         /*
5155                          * OK, get the element
5156                          */
5157                         char       *ptr = fctx->elemdataptr;
5158
5159                         fcinfo->isnull = false;
5160                         elem = ArrayCast(ptr, fctx->elmbyval, fctx->elmlen);
5161
5162                         /*
5163                          * Advance elemdataptr over it
5164                          */
5165                         ptr = att_addlength_pointer(ptr, fctx->elmlen, ptr);
5166                         ptr = (char *) att_align_nominal(ptr, fctx->elmalign);
5167                         fctx->elemdataptr = ptr;
5168                 }
5169
5170                 SRF_RETURN_NEXT(funcctx, elem);
5171         }
5172         else
5173         {
5174                 /* do when there is no more left */
5175                 SRF_RETURN_DONE(funcctx);
5176         }
5177 }