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