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