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