]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/arrayfuncs.c
Add array_contains_nulls() function in arrayfuncs.c.
[postgresql] / src / backend / utils / adt / arrayfuncs.c
1 /*-------------------------------------------------------------------------
2  *
3  * arrayfuncs.c
4  *        Support functions for arrays.
5  *
6  * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        src/backend/utils/adt/arrayfuncs.c
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16
17 #include <ctype.h>
18
19 #include "funcapi.h"
20 #include "libpq/pqformat.h"
21 #include "parser/parse_coerce.h"
22 #include "utils/array.h"
23 #include "utils/builtins.h"
24 #include "utils/datum.h"
25 #include "utils/lsyscache.h"
26 #include "utils/memutils.h"
27 #include "utils/typcache.h"
28
29
30 /*
31  * GUC parameter
32  */
33 bool            Array_nulls = true;
34
35 /*
36  * Local definitions
37  */
38 #define ASSGN    "="
39
40 typedef enum
41 {
42         ARRAY_NO_LEVEL,
43         ARRAY_LEVEL_STARTED,
44         ARRAY_ELEM_STARTED,
45         ARRAY_ELEM_COMPLETED,
46         ARRAY_QUOTED_ELEM_STARTED,
47         ARRAY_QUOTED_ELEM_COMPLETED,
48         ARRAY_ELEM_DELIMITED,
49         ARRAY_LEVEL_COMPLETED,
50         ARRAY_LEVEL_DELIMITED
51 } ArrayParseState;
52
53 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 *) palloc0(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                         p = att_addlength_pointer(p, elmlen, p);
3027                         p = (char *) att_align_nominal(p, elmalign);
3028                 }
3029
3030                 /* advance bitmap pointer if any */
3031                 if (bitmap)
3032                 {
3033                         bitmask <<= 1;
3034                         if (bitmask == 0x100)
3035                         {
3036                                 bitmap++;
3037                                 bitmask = 1;
3038                         }
3039                 }
3040         }
3041 }
3042
3043 /*
3044  * array_contains_nulls --- detect whether an array has any null elements
3045  *
3046  * This gives an accurate answer, whereas testing ARR_HASNULL only tells
3047  * if the array *might* contain a null.
3048  */
3049 bool
3050 array_contains_nulls(ArrayType *array)
3051 {
3052         int                     nelems;
3053         bits8      *bitmap;
3054         int                     bitmask;
3055
3056         /* Easy answer if there's no null bitmap */
3057         if (!ARR_HASNULL(array))
3058                 return false;
3059
3060         nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
3061
3062         bitmap = ARR_NULLBITMAP(array);
3063
3064         /* check whole bytes of the bitmap byte-at-a-time */
3065         while (nelems >= 8)
3066         {
3067                 if (*bitmap != 0xFF)
3068                         return true;
3069                 bitmap++;
3070                 nelems -= 8;
3071         }
3072
3073         /* check last partial byte */
3074         bitmask = 1;
3075         while (nelems > 0)
3076         {
3077                 if ((*bitmap & bitmask) == 0)
3078                         return true;
3079                 bitmask <<= 1;
3080                 nelems--;
3081         }
3082
3083         return false;
3084 }
3085
3086
3087 /*
3088  * array_eq :
3089  *                compares two arrays for equality
3090  * result :
3091  *                returns true if the arrays are equal, false otherwise.
3092  *
3093  * Note: we do not use array_cmp here, since equality may be meaningful in
3094  * datatypes that don't have a total ordering (and hence no btree support).
3095  */
3096 Datum
3097 array_eq(PG_FUNCTION_ARGS)
3098 {
3099         ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
3100         ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
3101         int                     ndims1 = ARR_NDIM(array1);
3102         int                     ndims2 = ARR_NDIM(array2);
3103         int                *dims1 = ARR_DIMS(array1);
3104         int                *dims2 = ARR_DIMS(array2);
3105         Oid                     element_type = ARR_ELEMTYPE(array1);
3106         bool            result = true;
3107         int                     nitems;
3108         TypeCacheEntry *typentry;
3109         int                     typlen;
3110         bool            typbyval;
3111         char            typalign;
3112         char       *ptr1;
3113         char       *ptr2;
3114         bits8      *bitmap1;
3115         bits8      *bitmap2;
3116         int                     bitmask;
3117         int                     i;
3118         FunctionCallInfoData locfcinfo;
3119
3120         if (element_type != ARR_ELEMTYPE(array2))
3121                 ereport(ERROR,
3122                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
3123                                  errmsg("cannot compare arrays of different element types")));
3124
3125         /* fast path if the arrays do not have the same dimensionality */
3126         if (ndims1 != ndims2 ||
3127                 memcmp(dims1, dims2, 2 * ndims1 * sizeof(int)) != 0)
3128                 result = false;
3129         else
3130         {
3131                 /*
3132                  * We arrange to look up the equality function only once per series of
3133                  * calls, assuming the element type doesn't change underneath us.  The
3134                  * typcache is used so that we have no memory leakage when being used
3135                  * as an index support function.
3136                  */
3137                 typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3138                 if (typentry == NULL ||
3139                         typentry->type_id != element_type)
3140                 {
3141                         typentry = lookup_type_cache(element_type,
3142                                                                                  TYPECACHE_EQ_OPR_FINFO);
3143                         if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
3144                                 ereport(ERROR,
3145                                                 (errcode(ERRCODE_UNDEFINED_FUNCTION),
3146                                 errmsg("could not identify an equality operator for type %s",
3147                                            format_type_be(element_type))));
3148                         fcinfo->flinfo->fn_extra = (void *) typentry;
3149                 }
3150                 typlen = typentry->typlen;
3151                 typbyval = typentry->typbyval;
3152                 typalign = typentry->typalign;
3153
3154                 /*
3155                  * apply the operator to each pair of array elements.
3156                  */
3157                 InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
3158                                                                  NULL, NULL);
3159
3160                 /* Loop over source data */
3161                 nitems = ArrayGetNItems(ndims1, dims1);
3162                 ptr1 = ARR_DATA_PTR(array1);
3163                 ptr2 = ARR_DATA_PTR(array2);
3164                 bitmap1 = ARR_NULLBITMAP(array1);
3165                 bitmap2 = ARR_NULLBITMAP(array2);
3166                 bitmask = 1;                    /* use same bitmask for both arrays */
3167
3168                 for (i = 0; i < nitems; i++)
3169                 {
3170                         Datum           elt1;
3171                         Datum           elt2;
3172                         bool            isnull1;
3173                         bool            isnull2;
3174                         bool            oprresult;
3175
3176                         /* Get elements, checking for NULL */
3177                         if (bitmap1 && (*bitmap1 & bitmask) == 0)
3178                         {
3179                                 isnull1 = true;
3180                                 elt1 = (Datum) 0;
3181                         }
3182                         else
3183                         {
3184                                 isnull1 = false;
3185                                 elt1 = fetch_att(ptr1, typbyval, typlen);
3186                                 ptr1 = att_addlength_pointer(ptr1, typlen, ptr1);
3187                                 ptr1 = (char *) att_align_nominal(ptr1, typalign);
3188                         }
3189
3190                         if (bitmap2 && (*bitmap2 & bitmask) == 0)
3191                         {
3192                                 isnull2 = true;
3193                                 elt2 = (Datum) 0;
3194                         }
3195                         else
3196                         {
3197                                 isnull2 = false;
3198                                 elt2 = fetch_att(ptr2, typbyval, typlen);
3199                                 ptr2 = att_addlength_pointer(ptr2, typlen, ptr2);
3200                                 ptr2 = (char *) att_align_nominal(ptr2, typalign);
3201                         }
3202
3203                         /* advance bitmap pointers if any */
3204                         bitmask <<= 1;
3205                         if (bitmask == 0x100)
3206                         {
3207                                 if (bitmap1)
3208                                         bitmap1++;
3209                                 if (bitmap2)
3210                                         bitmap2++;
3211                                 bitmask = 1;
3212                         }
3213
3214                         /*
3215                          * We consider two NULLs equal; NULL and not-NULL are unequal.
3216                          */
3217                         if (isnull1 && isnull2)
3218                                 continue;
3219                         if (isnull1 || isnull2)
3220                         {
3221                                 result = false;
3222                                 break;
3223                         }
3224
3225                         /*
3226                          * Apply the operator to the element pair
3227                          */
3228                         locfcinfo.arg[0] = elt1;
3229                         locfcinfo.arg[1] = elt2;
3230                         locfcinfo.argnull[0] = false;
3231                         locfcinfo.argnull[1] = false;
3232                         locfcinfo.isnull = false;
3233                         oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
3234                         if (!oprresult)
3235                         {
3236                                 result = false;
3237                                 break;
3238                         }
3239                 }
3240         }
3241
3242         /* Avoid leaking memory when handed toasted input. */
3243         PG_FREE_IF_COPY(array1, 0);
3244         PG_FREE_IF_COPY(array2, 1);
3245
3246         PG_RETURN_BOOL(result);
3247 }
3248
3249
3250 /*-----------------------------------------------------------------------------
3251  * array-array bool operators:
3252  *              Given two arrays, iterate comparison operators
3253  *              over the array. Uses logic similar to text comparison
3254  *              functions, except element-by-element instead of
3255  *              character-by-character.
3256  *----------------------------------------------------------------------------
3257  */
3258
3259 Datum
3260 array_ne(PG_FUNCTION_ARGS)
3261 {
3262         PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo)));
3263 }
3264
3265 Datum
3266 array_lt(PG_FUNCTION_ARGS)
3267 {
3268         PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
3269 }
3270
3271 Datum
3272 array_gt(PG_FUNCTION_ARGS)
3273 {
3274         PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
3275 }
3276
3277 Datum
3278 array_le(PG_FUNCTION_ARGS)
3279 {
3280         PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
3281 }
3282
3283 Datum
3284 array_ge(PG_FUNCTION_ARGS)
3285 {
3286         PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
3287 }
3288
3289 Datum
3290 btarraycmp(PG_FUNCTION_ARGS)
3291 {
3292         PG_RETURN_INT32(array_cmp(fcinfo));
3293 }
3294
3295 /*
3296  * array_cmp()
3297  * Internal comparison function for arrays.
3298  *
3299  * Returns -1, 0 or 1
3300  */
3301 static int
3302 array_cmp(FunctionCallInfo fcinfo)
3303 {
3304         ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
3305         ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
3306         int                     ndims1 = ARR_NDIM(array1);
3307         int                     ndims2 = ARR_NDIM(array2);
3308         int                *dims1 = ARR_DIMS(array1);
3309         int                *dims2 = ARR_DIMS(array2);
3310         int                     nitems1 = ArrayGetNItems(ndims1, dims1);
3311         int                     nitems2 = ArrayGetNItems(ndims2, dims2);
3312         Oid                     element_type = ARR_ELEMTYPE(array1);
3313         int                     result = 0;
3314         TypeCacheEntry *typentry;
3315         int                     typlen;
3316         bool            typbyval;
3317         char            typalign;
3318         int                     min_nitems;
3319         char       *ptr1;
3320         char       *ptr2;
3321         bits8      *bitmap1;
3322         bits8      *bitmap2;
3323         int                     bitmask;
3324         int                     i;
3325         FunctionCallInfoData locfcinfo;
3326
3327         if (element_type != ARR_ELEMTYPE(array2))
3328                 ereport(ERROR,
3329                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
3330                                  errmsg("cannot compare arrays of different element types")));
3331
3332         /*
3333          * We arrange to look up the comparison function only once per series of
3334          * calls, assuming the element type doesn't change underneath us. The
3335          * typcache is used so that we have no memory leakage when being used as
3336          * an index support function.
3337          */
3338         typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3339         if (typentry == NULL ||
3340                 typentry->type_id != element_type)
3341         {
3342                 typentry = lookup_type_cache(element_type,
3343                                                                          TYPECACHE_CMP_PROC_FINFO);
3344                 if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
3345                         ereport(ERROR,
3346                                         (errcode(ERRCODE_UNDEFINED_FUNCTION),
3347                            errmsg("could not identify a comparison function for type %s",
3348                                           format_type_be(element_type))));
3349                 fcinfo->flinfo->fn_extra = (void *) typentry;
3350         }
3351         typlen = typentry->typlen;
3352         typbyval = typentry->typbyval;
3353         typalign = typentry->typalign;
3354
3355         /*
3356          * apply the operator to each pair of array elements.
3357          */
3358         InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2,
3359                                                          NULL, NULL);
3360
3361         /* Loop over source data */
3362         min_nitems = Min(nitems1, nitems2);
3363         ptr1 = ARR_DATA_PTR(array1);
3364         ptr2 = ARR_DATA_PTR(array2);
3365         bitmap1 = ARR_NULLBITMAP(array1);
3366         bitmap2 = ARR_NULLBITMAP(array2);
3367         bitmask = 1;                            /* use same bitmask for both arrays */
3368
3369         for (i = 0; i < min_nitems; i++)
3370         {
3371                 Datum           elt1;
3372                 Datum           elt2;
3373                 bool            isnull1;
3374                 bool            isnull2;
3375                 int32           cmpresult;
3376
3377                 /* Get elements, checking for NULL */
3378                 if (bitmap1 && (*bitmap1 & bitmask) == 0)
3379                 {
3380                         isnull1 = true;
3381                         elt1 = (Datum) 0;
3382                 }
3383                 else
3384                 {
3385                         isnull1 = false;
3386                         elt1 = fetch_att(ptr1, typbyval, typlen);
3387                         ptr1 = att_addlength_pointer(ptr1, typlen, ptr1);
3388                         ptr1 = (char *) att_align_nominal(ptr1, typalign);
3389                 }
3390
3391                 if (bitmap2 && (*bitmap2 & bitmask) == 0)
3392                 {
3393                         isnull2 = true;
3394                         elt2 = (Datum) 0;
3395                 }
3396                 else
3397                 {
3398                         isnull2 = false;
3399                         elt2 = fetch_att(ptr2, typbyval, typlen);
3400                         ptr2 = att_addlength_pointer(ptr2, typlen, ptr2);
3401                         ptr2 = (char *) att_align_nominal(ptr2, typalign);
3402                 }
3403
3404                 /* advance bitmap pointers if any */
3405                 bitmask <<= 1;
3406                 if (bitmask == 0x100)
3407                 {
3408                         if (bitmap1)
3409                                 bitmap1++;
3410                         if (bitmap2)
3411                                 bitmap2++;
3412                         bitmask = 1;
3413                 }
3414
3415                 /*
3416                  * We consider two NULLs equal; NULL > not-NULL.
3417                  */
3418                 if (isnull1 && isnull2)
3419                         continue;
3420                 if (isnull1)
3421                 {
3422                         /* arg1 is greater than arg2 */
3423                         result = 1;
3424                         break;
3425                 }
3426                 if (isnull2)
3427                 {
3428                         /* arg1 is less than arg2 */
3429                         result = -1;
3430                         break;
3431                 }
3432
3433                 /* Compare the pair of elements */
3434                 locfcinfo.arg[0] = elt1;
3435                 locfcinfo.arg[1] = elt2;
3436                 locfcinfo.argnull[0] = false;
3437                 locfcinfo.argnull[1] = false;
3438                 locfcinfo.isnull = false;
3439                 cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
3440
3441                 if (cmpresult == 0)
3442                         continue;                       /* equal */
3443
3444                 if (cmpresult < 0)
3445                 {
3446                         /* arg1 is less than arg2 */
3447                         result = -1;
3448                         break;
3449                 }
3450                 else
3451                 {
3452                         /* arg1 is greater than arg2 */
3453                         result = 1;
3454                         break;
3455                 }
3456         }
3457
3458         /*
3459          * If arrays contain same data (up to end of shorter one), apply
3460          * additional rules to sort by dimensionality.  The relative significance
3461          * of the different bits of information is historical; mainly we just care
3462          * that we don't say "equal" for arrays of different dimensionality.
3463          */
3464         if (result == 0)
3465         {
3466                 if (nitems1 != nitems2)
3467                         result = (nitems1 < nitems2) ? -1 : 1;
3468                 else if (ndims1 != ndims2)
3469                         result = (ndims1 < ndims2) ? -1 : 1;
3470                 else
3471                 {
3472                         /* this relies on LB array immediately following DIMS array */
3473                         for (i = 0; i < ndims1 * 2; i++)
3474                         {
3475                                 if (dims1[i] != dims2[i])
3476                                 {
3477                                         result = (dims1[i] < dims2[i]) ? -1 : 1;
3478                                         break;
3479                                 }
3480                         }
3481                 }
3482         }
3483
3484         /* Avoid leaking memory when handed toasted input. */
3485         PG_FREE_IF_COPY(array1, 0);
3486         PG_FREE_IF_COPY(array2, 1);
3487
3488         return result;
3489 }
3490
3491
3492 /*-----------------------------------------------------------------------------
3493  * array hashing
3494  *              Hash the elements and combine the results.
3495  *----------------------------------------------------------------------------
3496  */
3497
3498 Datum
3499 hash_array(PG_FUNCTION_ARGS)
3500 {
3501         ArrayType  *array = PG_GETARG_ARRAYTYPE_P(0);
3502         int                     ndims = ARR_NDIM(array);
3503         int                *dims = ARR_DIMS(array);
3504         Oid                     element_type = ARR_ELEMTYPE(array);
3505         uint32          result = 0;
3506         int                     nitems;
3507         TypeCacheEntry *typentry;
3508         int                     typlen;
3509         bool            typbyval;
3510         char            typalign;
3511         char       *ptr;
3512         bits8      *bitmap;
3513         int                     bitmask;
3514         int                     i;
3515         FunctionCallInfoData locfcinfo;
3516
3517         /*
3518          * We arrange to look up the hash function only once per series of calls,
3519          * assuming the element type doesn't change underneath us.  The typcache
3520          * is used so that we have no memory leakage when being used as an index
3521          * support function.
3522          */
3523         typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3524         if (typentry == NULL ||
3525                 typentry->type_id != element_type)
3526         {
3527                 typentry = lookup_type_cache(element_type,
3528                                                                          TYPECACHE_HASH_PROC_FINFO);
3529                 if (!OidIsValid(typentry->hash_proc_finfo.fn_oid))
3530                         ereport(ERROR,
3531                                         (errcode(ERRCODE_UNDEFINED_FUNCTION),
3532                                          errmsg("could not identify a hash function for type %s",
3533                                                         format_type_be(element_type))));
3534                 fcinfo->flinfo->fn_extra = (void *) typentry;
3535         }
3536         typlen = typentry->typlen;
3537         typbyval = typentry->typbyval;
3538         typalign = typentry->typalign;
3539
3540         /*
3541          * apply the hash function to each array element.
3542          */
3543         InitFunctionCallInfoData(locfcinfo, &typentry->hash_proc_finfo, 1,
3544                                                          NULL, NULL);
3545
3546         /* Loop over source data */
3547         nitems = ArrayGetNItems(ndims, dims);
3548         ptr = ARR_DATA_PTR(array);
3549         bitmap = ARR_NULLBITMAP(array);
3550         bitmask = 1;
3551
3552         for (i = 0; i < nitems; i++)
3553         {
3554                 uint32          elthash;
3555
3556                 /* Get element, checking for NULL */
3557                 if (bitmap && (*bitmap & bitmask) == 0)
3558                 {
3559                         /* Treat nulls as having hashvalue 0 */
3560                         elthash = 0;
3561                 }
3562                 else
3563                 {
3564                         Datum           elt;
3565
3566                         elt = fetch_att(ptr, typbyval, typlen);
3567                         ptr = att_addlength_pointer(ptr, typlen, ptr);
3568                         ptr = (char *) att_align_nominal(ptr, typalign);
3569
3570                         /* Apply the hash function */
3571                         locfcinfo.arg[0] = elt;
3572                         locfcinfo.argnull[0] = false;
3573                         locfcinfo.isnull = false;
3574                         elthash = DatumGetUInt32(FunctionCallInvoke(&locfcinfo));
3575                 }
3576
3577                 /* advance bitmap pointer if any */
3578                 if (bitmap)
3579                 {
3580                         bitmask <<= 1;
3581                         if (bitmask == 0x100)
3582                         {
3583                                 bitmap++;
3584                                 bitmask = 1;
3585                         }
3586                 }
3587
3588                 /*
3589                  * Combine hash values of successive elements by rotating the previous
3590                  * value left 1 bit, then XOR'ing in the new element's hash value.
3591                  */
3592                 result = (result << 1) | (result >> 31);
3593                 result ^= elthash;
3594         }
3595
3596         /* Avoid leaking memory when handed toasted input. */
3597         PG_FREE_IF_COPY(array, 0);
3598
3599         PG_RETURN_UINT32(result);
3600 }
3601
3602
3603 /*-----------------------------------------------------------------------------
3604  * array overlap/containment comparisons
3605  *              These use the same methods of comparing array elements as array_eq.
3606  *              We consider only the elements of the arrays, ignoring dimensionality.
3607  *----------------------------------------------------------------------------
3608  */
3609
3610 /*
3611  * array_contain_compare :
3612  *                compares two arrays for overlap/containment
3613  *
3614  * When matchall is true, return true if all members of array1 are in array2.
3615  * When matchall is false, return true if any members of array1 are in array2.
3616  */
3617 static bool
3618 array_contain_compare(ArrayType *array1, ArrayType *array2, bool matchall,
3619                                           void **fn_extra)
3620 {
3621         bool            result = matchall;
3622         Oid                     element_type = ARR_ELEMTYPE(array1);
3623         TypeCacheEntry *typentry;
3624         int                     nelems1;
3625         Datum      *values2;
3626         bool       *nulls2;
3627         int                     nelems2;
3628         int                     typlen;
3629         bool            typbyval;
3630         char            typalign;
3631         char       *ptr1;
3632         bits8      *bitmap1;
3633         int                     bitmask;
3634         int                     i;
3635         int                     j;
3636         FunctionCallInfoData locfcinfo;
3637
3638         if (element_type != ARR_ELEMTYPE(array2))
3639                 ereport(ERROR,
3640                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
3641                                  errmsg("cannot compare arrays of different element types")));
3642
3643         /*
3644          * We arrange to look up the equality function only once per series of
3645          * calls, assuming the element type doesn't change underneath us.  The
3646          * typcache is used so that we have no memory leakage when being used as
3647          * an index support function.
3648          */
3649         typentry = (TypeCacheEntry *) *fn_extra;
3650         if (typentry == NULL ||
3651                 typentry->type_id != element_type)
3652         {
3653                 typentry = lookup_type_cache(element_type,
3654                                                                          TYPECACHE_EQ_OPR_FINFO);
3655                 if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
3656                         ereport(ERROR,
3657                                         (errcode(ERRCODE_UNDEFINED_FUNCTION),
3658                                 errmsg("could not identify an equality operator for type %s",
3659                                            format_type_be(element_type))));
3660                 *fn_extra = (void *) typentry;
3661         }
3662         typlen = typentry->typlen;
3663         typbyval = typentry->typbyval;
3664         typalign = typentry->typalign;
3665
3666         /*
3667          * Since we probably will need to scan array2 multiple times, it's
3668          * worthwhile to use deconstruct_array on it.  We scan array1 the hard way
3669          * however, since we very likely won't need to look at all of it.
3670          */
3671         deconstruct_array(array2, element_type, typlen, typbyval, typalign,
3672                                           &values2, &nulls2, &nelems2);
3673
3674         /*
3675          * Apply the comparison operator to each pair of array elements.
3676          */
3677         InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
3678                                                          NULL, NULL);
3679
3680         /* Loop over source data */
3681         nelems1 = ArrayGetNItems(ARR_NDIM(array1), ARR_DIMS(array1));
3682         ptr1 = ARR_DATA_PTR(array1);
3683         bitmap1 = ARR_NULLBITMAP(array1);
3684         bitmask = 1;
3685
3686         for (i = 0; i < nelems1; i++)
3687         {
3688                 Datum           elt1;
3689                 bool            isnull1;
3690
3691                 /* Get element, checking for NULL */
3692                 if (bitmap1 && (*bitmap1 & bitmask) == 0)
3693                 {
3694                         isnull1 = true;
3695                         elt1 = (Datum) 0;
3696                 }
3697                 else
3698                 {
3699                         isnull1 = false;
3700                         elt1 = fetch_att(ptr1, typbyval, typlen);
3701                         ptr1 = att_addlength_pointer(ptr1, typlen, ptr1);
3702                         ptr1 = (char *) att_align_nominal(ptr1, typalign);
3703                 }
3704
3705                 /* advance bitmap pointer if any */
3706                 bitmask <<= 1;
3707                 if (bitmask == 0x100)
3708                 {
3709                         if (bitmap1)
3710                                 bitmap1++;
3711                         bitmask = 1;
3712                 }
3713
3714                 /*
3715                  * We assume that the comparison operator is strict, so a NULL can't
3716                  * match anything.      XXX this diverges from the "NULL=NULL" behavior of
3717                  * array_eq, should we act like that?
3718                  */
3719                 if (isnull1)
3720                 {
3721                         if (matchall)
3722                         {
3723                                 result = false;
3724                                 break;
3725                         }
3726                         continue;
3727                 }
3728
3729                 for (j = 0; j < nelems2; j++)
3730                 {
3731                         Datum           elt2 = values2[j];
3732                         bool            isnull2 = nulls2[j];
3733                         bool            oprresult;
3734
3735                         if (isnull2)
3736                                 continue;               /* can't match */
3737
3738                         /*
3739                          * Apply the operator to the element pair
3740                          */
3741                         locfcinfo.arg[0] = elt1;
3742                         locfcinfo.arg[1] = elt2;
3743                         locfcinfo.argnull[0] = false;
3744                         locfcinfo.argnull[1] = false;
3745                         locfcinfo.isnull = false;
3746                         oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
3747                         if (oprresult)
3748                                 break;
3749                 }
3750
3751                 if (j < nelems2)
3752                 {
3753                         /* found a match for elt1 */
3754                         if (!matchall)
3755                         {
3756                                 result = true;
3757                                 break;
3758                         }
3759                 }
3760                 else
3761                 {
3762                         /* no match for elt1 */
3763                         if (matchall)
3764                         {
3765                                 result = false;
3766                                 break;
3767                         }
3768                 }
3769         }
3770
3771         pfree(values2);
3772         pfree(nulls2);
3773
3774         return result;
3775 }
3776
3777 Datum
3778 arrayoverlap(PG_FUNCTION_ARGS)
3779 {
3780         ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
3781         ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
3782         bool            result;
3783
3784         result = array_contain_compare(array1, array2, false,
3785                                                                    &fcinfo->flinfo->fn_extra);
3786
3787         /* Avoid leaking memory when handed toasted input. */
3788         PG_FREE_IF_COPY(array1, 0);
3789         PG_FREE_IF_COPY(array2, 1);
3790
3791         PG_RETURN_BOOL(result);
3792 }
3793
3794 Datum
3795 arraycontains(PG_FUNCTION_ARGS)
3796 {
3797         ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
3798         ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
3799         bool            result;
3800
3801         result = array_contain_compare(array2, array1, true,
3802                                                                    &fcinfo->flinfo->fn_extra);
3803
3804         /* Avoid leaking memory when handed toasted input. */
3805         PG_FREE_IF_COPY(array1, 0);
3806         PG_FREE_IF_COPY(array2, 1);
3807
3808         PG_RETURN_BOOL(result);
3809 }
3810
3811 Datum
3812 arraycontained(PG_FUNCTION_ARGS)
3813 {
3814         ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
3815         ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
3816         bool            result;
3817
3818         result = array_contain_compare(array1, array2, true,
3819                                                                    &fcinfo->flinfo->fn_extra);
3820
3821         /* Avoid leaking memory when handed toasted input. */
3822         PG_FREE_IF_COPY(array1, 0);
3823         PG_FREE_IF_COPY(array2, 1);
3824
3825         PG_RETURN_BOOL(result);
3826 }
3827
3828
3829 /***************************************************************************/
3830 /******************|              Support  Routines                       |*****************/
3831 /***************************************************************************/
3832
3833 /*
3834  * Check whether a specific array element is NULL
3835  *
3836  * nullbitmap: pointer to array's null bitmap (NULL if none)
3837  * offset: 0-based linear element number of array element
3838  */
3839 static bool
3840 array_get_isnull(const bits8 *nullbitmap, int offset)
3841 {
3842         if (nullbitmap == NULL)
3843                 return false;                   /* assume not null */
3844         if (nullbitmap[offset / 8] & (1 << (offset % 8)))
3845                 return false;                   /* not null */
3846         return true;
3847 }
3848
3849 /*
3850  * Set a specific array element's null-bitmap entry
3851  *
3852  * nullbitmap: pointer to array's null bitmap (mustn't be NULL)
3853  * offset: 0-based linear element number of array element
3854  * isNull: null status to set
3855  */
3856 static void
3857 array_set_isnull(bits8 *nullbitmap, int offset, bool isNull)
3858 {
3859         int                     bitmask;
3860
3861         nullbitmap += offset / 8;
3862         bitmask = 1 << (offset % 8);
3863         if (isNull)
3864                 *nullbitmap &= ~bitmask;
3865         else
3866                 *nullbitmap |= bitmask;
3867 }
3868
3869 /*
3870  * Fetch array element at pointer, converted correctly to a Datum
3871  *
3872  * Caller must have handled case of NULL element
3873  */
3874 static Datum
3875 ArrayCast(char *value, bool byval, int len)
3876 {
3877         return fetch_att(value, byval, len);
3878 }
3879
3880 /*
3881  * Copy datum to *dest and return total space used (including align padding)
3882  *
3883  * Caller must have handled case of NULL element
3884  */
3885 static int
3886 ArrayCastAndSet(Datum src,
3887                                 int typlen,
3888                                 bool typbyval,
3889                                 char typalign,
3890                                 char *dest)
3891 {
3892         int                     inc;
3893
3894         if (typlen > 0)
3895         {
3896                 if (typbyval)
3897                         store_att_byval(dest, src, typlen);
3898                 else
3899                         memmove(dest, DatumGetPointer(src), typlen);
3900                 inc = att_align_nominal(typlen, typalign);
3901         }
3902         else
3903         {
3904                 Assert(!typbyval);
3905                 inc = att_addlength_datum(0, typlen, src);
3906                 memmove(dest, DatumGetPointer(src), inc);
3907                 inc = att_align_nominal(inc, typalign);
3908         }
3909
3910         return inc;
3911 }
3912
3913 /*
3914  * Advance ptr over nitems array elements
3915  *
3916  * ptr: starting location in array
3917  * offset: 0-based linear element number of first element (the one at *ptr)
3918  * nullbitmap: start of array's null bitmap, or NULL if none
3919  * nitems: number of array elements to advance over (>= 0)
3920  * typlen, typbyval, typalign: storage parameters of array element datatype
3921  *
3922  * It is caller's responsibility to ensure that nitems is within range
3923  */
3924 static char *
3925 array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
3926                    int typlen, bool typbyval, char typalign)
3927 {
3928         int                     bitmask;
3929         int                     i;
3930
3931         /* easy if fixed-size elements and no NULLs */
3932         if (typlen > 0 && !nullbitmap)
3933                 return ptr + nitems * ((Size) att_align_nominal(typlen, typalign));
3934
3935         /* seems worth having separate loops for NULL and no-NULLs cases */
3936         if (nullbitmap)
3937         {
3938                 nullbitmap += offset / 8;
3939                 bitmask = 1 << (offset % 8);
3940
3941                 for (i = 0; i < nitems; i++)
3942                 {
3943                         if (*nullbitmap & bitmask)
3944                         {
3945                                 ptr = att_addlength_pointer(ptr, typlen, ptr);
3946                                 ptr = (char *) att_align_nominal(ptr, typalign);
3947                         }
3948                         bitmask <<= 1;
3949                         if (bitmask == 0x100)
3950                         {
3951                                 nullbitmap++;
3952                                 bitmask = 1;
3953                         }
3954                 }
3955         }
3956         else
3957         {
3958                 for (i = 0; i < nitems; i++)
3959                 {
3960                         ptr = att_addlength_pointer(ptr, typlen, ptr);
3961                         ptr = (char *) att_align_nominal(ptr, typalign);
3962                 }
3963         }
3964         return ptr;
3965 }
3966
3967 /*
3968  * Compute total size of the nitems array elements starting at *ptr
3969  *
3970  * Parameters same as for array_seek
3971  */
3972 static int
3973 array_nelems_size(char *ptr, int offset, bits8 *nullbitmap, int nitems,
3974                                   int typlen, bool typbyval, char typalign)
3975 {
3976         return array_seek(ptr, offset, nullbitmap, nitems,
3977                                           typlen, typbyval, typalign) - ptr;
3978 }
3979
3980 /*
3981  * Copy nitems array elements from srcptr to destptr
3982  *
3983  * destptr: starting destination location (must be enough room!)
3984  * nitems: number of array elements to copy (>= 0)
3985  * srcptr: starting location in source array
3986  * offset: 0-based linear element number of first element (the one at *srcptr)
3987  * nullbitmap: start of source array's null bitmap, or NULL if none
3988  * typlen, typbyval, typalign: storage parameters of array element datatype
3989  *
3990  * Returns number of bytes copied
3991  *
3992  * NB: this does not take care of setting up the destination's null bitmap!
3993  */
3994 static int
3995 array_copy(char *destptr, int nitems,
3996                    char *srcptr, int offset, bits8 *nullbitmap,
3997                    int typlen, bool typbyval, char typalign)
3998 {
3999         int                     numbytes;
4000
4001         numbytes = array_nelems_size(srcptr, offset, nullbitmap, nitems,
4002                                                                  typlen, typbyval, typalign);
4003         memcpy(destptr, srcptr, numbytes);
4004         return numbytes;
4005 }
4006
4007 /*
4008  * Copy nitems null-bitmap bits from source to destination
4009  *
4010  * destbitmap: start of destination array's null bitmap (mustn't be NULL)
4011  * destoffset: 0-based linear element number of first dest element
4012  * srcbitmap: start of source array's null bitmap, or NULL if none
4013  * srcoffset: 0-based linear element number of first source element
4014  * nitems: number of bits to copy (>= 0)
4015  *
4016  * If srcbitmap is NULL then we assume the source is all-non-NULL and
4017  * fill 1's into the destination bitmap.  Note that only the specified
4018  * bits in the destination map are changed, not any before or after.
4019  *
4020  * Note: this could certainly be optimized using standard bitblt methods.
4021  * However, it's not clear that the typical Postgres array has enough elements
4022  * to make it worth worrying too much.  For the moment, KISS.
4023  */
4024 void
4025 array_bitmap_copy(bits8 *destbitmap, int destoffset,
4026                                   const bits8 *srcbitmap, int srcoffset,
4027                                   int nitems)
4028 {
4029         int                     destbitmask,
4030                                 destbitval,
4031                                 srcbitmask,
4032                                 srcbitval;
4033
4034         Assert(destbitmap);
4035         if (nitems <= 0)
4036                 return;                                 /* don't risk fetch off end of memory */
4037         destbitmap += destoffset / 8;
4038         destbitmask = 1 << (destoffset % 8);
4039         destbitval = *destbitmap;
4040         if (srcbitmap)
4041         {
4042                 srcbitmap += srcoffset / 8;
4043                 srcbitmask = 1 << (srcoffset % 8);
4044                 srcbitval = *srcbitmap;
4045                 while (nitems-- > 0)
4046                 {
4047                         if (srcbitval & srcbitmask)
4048                                 destbitval |= destbitmask;
4049                         else
4050                                 destbitval &= ~destbitmask;
4051                         destbitmask <<= 1;
4052                         if (destbitmask == 0x100)
4053                         {
4054                                 *destbitmap++ = destbitval;
4055                                 destbitmask = 1;
4056                                 if (nitems > 0)
4057                                         destbitval = *destbitmap;
4058                         }
4059                         srcbitmask <<= 1;
4060                         if (srcbitmask == 0x100)
4061                         {
4062                                 srcbitmap++;
4063                                 srcbitmask = 1;
4064                                 if (nitems > 0)
4065                                         srcbitval = *srcbitmap;
4066                         }
4067                 }
4068                 if (destbitmask != 1)
4069                         *destbitmap = destbitval;
4070         }
4071         else
4072         {
4073                 while (nitems-- > 0)
4074                 {
4075                         destbitval |= destbitmask;
4076                         destbitmask <<= 1;
4077                         if (destbitmask == 0x100)
4078                         {
4079                                 *destbitmap++ = destbitval;
4080                                 destbitmask = 1;
4081                                 if (nitems > 0)
4082                                         destbitval = *destbitmap;
4083                         }
4084                 }
4085                 if (destbitmask != 1)
4086                         *destbitmap = destbitval;
4087         }
4088 }
4089
4090 /*
4091  * Compute space needed for a slice of an array
4092  *
4093  * We assume the caller has verified that the slice coordinates are valid.
4094  */
4095 static int
4096 array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
4097                                  int ndim, int *dim, int *lb,
4098                                  int *st, int *endp,
4099                                  int typlen, bool typbyval, char typalign)
4100 {
4101         int                     src_offset,
4102                                 span[MAXDIM],
4103                                 prod[MAXDIM],
4104                                 dist[MAXDIM],
4105                                 indx[MAXDIM];
4106         char       *ptr;
4107         int                     i,
4108                                 j,
4109                                 inc;
4110         int                     count = 0;
4111
4112         mda_get_range(ndim, span, st, endp);
4113
4114         /* Pretty easy for fixed element length without nulls ... */
4115         if (typlen > 0 && !arraynullsptr)
4116                 return ArrayGetNItems(ndim, span) * att_align_nominal(typlen, typalign);
4117
4118         /* Else gotta do it the hard way */
4119         src_offset = ArrayGetOffset(ndim, dim, lb, st);
4120         ptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
4121                                          typlen, typbyval, typalign);
4122         mda_get_prod(ndim, dim, prod);
4123         mda_get_offset_values(ndim, dist, prod, span);
4124         for (i = 0; i < ndim; i++)
4125                 indx[i] = 0;
4126         j = ndim - 1;
4127         do
4128         {
4129                 if (dist[j])
4130                 {
4131                         ptr = array_seek(ptr, src_offset, arraynullsptr, dist[j],
4132                                                          typlen, typbyval, typalign);
4133                         src_offset += dist[j];
4134                 }
4135                 if (!array_get_isnull(arraynullsptr, src_offset))
4136                 {
4137                         inc = att_addlength_pointer(0, typlen, ptr);
4138                         inc = att_align_nominal(inc, typalign);
4139                         ptr += inc;
4140                         count += inc;
4141                 }
4142                 src_offset++;
4143         } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
4144         return count;
4145 }
4146
4147 /*
4148  * Extract a slice of an array into consecutive elements in the destination
4149  * array.
4150  *
4151  * We assume the caller has verified that the slice coordinates are valid,
4152  * allocated enough storage for the result, and initialized the header
4153  * of the new array.
4154  */
4155 static void
4156 array_extract_slice(ArrayType *newarray,
4157                                         int ndim,
4158                                         int *dim,
4159                                         int *lb,
4160                                         char *arraydataptr,
4161                                         bits8 *arraynullsptr,
4162                                         int *st,
4163                                         int *endp,
4164                                         int typlen,
4165                                         bool typbyval,
4166                                         char typalign)
4167 {
4168         char       *destdataptr = ARR_DATA_PTR(newarray);
4169         bits8      *destnullsptr = ARR_NULLBITMAP(newarray);
4170         char       *srcdataptr;
4171         int                     src_offset,
4172                                 dest_offset,
4173                                 prod[MAXDIM],
4174                                 span[MAXDIM],
4175                                 dist[MAXDIM],
4176                                 indx[MAXDIM];
4177         int                     i,
4178                                 j,
4179                                 inc;
4180
4181         src_offset = ArrayGetOffset(ndim, dim, lb, st);
4182         srcdataptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
4183                                                         typlen, typbyval, typalign);
4184         mda_get_prod(ndim, dim, prod);
4185         mda_get_range(ndim, span, st, endp);
4186         mda_get_offset_values(ndim, dist, prod, span);
4187         for (i = 0; i < ndim; i++)
4188                 indx[i] = 0;
4189         dest_offset = 0;
4190         j = ndim - 1;
4191         do
4192         {
4193                 if (dist[j])
4194                 {
4195                         /* skip unwanted elements */
4196                         srcdataptr = array_seek(srcdataptr, src_offset, arraynullsptr,
4197                                                                         dist[j],
4198                                                                         typlen, typbyval, typalign);
4199                         src_offset += dist[j];
4200                 }
4201                 inc = array_copy(destdataptr, 1,
4202                                                  srcdataptr, src_offset, arraynullsptr,
4203                                                  typlen, typbyval, typalign);
4204                 if (destnullsptr)
4205                         array_bitmap_copy(destnullsptr, dest_offset,
4206                                                           arraynullsptr, src_offset,
4207                                                           1);
4208                 destdataptr += inc;
4209                 srcdataptr += inc;
4210                 src_offset++;
4211                 dest_offset++;
4212         } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
4213 }
4214
4215 /*
4216  * Insert a slice into an array.
4217  *
4218  * ndim/dim[]/lb[] are dimensions of the original array.  A new array with
4219  * those same dimensions is to be constructed.  destArray must already
4220  * have been allocated and its header initialized.
4221  *
4222  * st[]/endp[] identify the slice to be replaced.  Elements within the slice
4223  * volume are taken from consecutive elements of the srcArray; elements
4224  * outside it are copied from origArray.
4225  *
4226  * We assume the caller has verified that the slice coordinates are valid.
4227  */
4228 static void
4229 array_insert_slice(ArrayType *destArray,
4230                                    ArrayType *origArray,
4231                                    ArrayType *srcArray,
4232                                    int ndim,
4233                                    int *dim,
4234                                    int *lb,
4235                                    int *st,
4236                                    int *endp,
4237                                    int typlen,
4238                                    bool typbyval,
4239                                    char typalign)
4240 {
4241         char       *destPtr = ARR_DATA_PTR(destArray);
4242         char       *origPtr = ARR_DATA_PTR(origArray);
4243         char       *srcPtr = ARR_DATA_PTR(srcArray);
4244         bits8      *destBitmap = ARR_NULLBITMAP(destArray);
4245         bits8      *origBitmap = ARR_NULLBITMAP(origArray);
4246         bits8      *srcBitmap = ARR_NULLBITMAP(srcArray);
4247         int                     orignitems = ArrayGetNItems(ARR_NDIM(origArray),
4248                                                                                         ARR_DIMS(origArray));
4249         int                     dest_offset,
4250                                 orig_offset,
4251                                 src_offset,
4252                                 prod[MAXDIM],
4253                                 span[MAXDIM],
4254                                 dist[MAXDIM],
4255                                 indx[MAXDIM];
4256         int                     i,
4257                                 j,
4258                                 inc;
4259
4260         dest_offset = ArrayGetOffset(ndim, dim, lb, st);
4261         /* copy items before the slice start */
4262         inc = array_copy(destPtr, dest_offset,
4263                                          origPtr, 0, origBitmap,
4264                                          typlen, typbyval, typalign);
4265         destPtr += inc;
4266         origPtr += inc;
4267         if (destBitmap)
4268                 array_bitmap_copy(destBitmap, 0, origBitmap, 0, dest_offset);
4269         orig_offset = dest_offset;
4270         mda_get_prod(ndim, dim, prod);
4271         mda_get_range(ndim, span, st, endp);
4272         mda_get_offset_values(ndim, dist, prod, span);
4273         for (i = 0; i < ndim; i++)
4274                 indx[i] = 0;
4275         src_offset = 0;
4276         j = ndim - 1;
4277         do
4278         {
4279                 /* Copy/advance over elements between here and next part of slice */
4280                 if (dist[j])
4281                 {
4282                         inc = array_copy(destPtr, dist[j],
4283                                                          origPtr, orig_offset, origBitmap,
4284                                                          typlen, typbyval, typalign);
4285                         destPtr += inc;
4286                         origPtr += inc;
4287                         if (destBitmap)
4288                                 array_bitmap_copy(destBitmap, dest_offset,
4289                                                                   origBitmap, orig_offset,
4290                                                                   dist[j]);
4291                         dest_offset += dist[j];
4292                         orig_offset += dist[j];
4293                 }
4294                 /* Copy new element at this slice position */
4295                 inc = array_copy(destPtr, 1,
4296                                                  srcPtr, src_offset, srcBitmap,
4297                                                  typlen, typbyval, typalign);
4298                 if (destBitmap)
4299                         array_bitmap_copy(destBitmap, dest_offset,
4300                                                           srcBitmap, src_offset,
4301                                                           1);
4302                 destPtr += inc;
4303                 srcPtr += inc;
4304                 dest_offset++;
4305                 src_offset++;
4306                 /* Advance over old element at this slice position */
4307                 origPtr = array_seek(origPtr, orig_offset, origBitmap, 1,
4308                                                          typlen, typbyval, typalign);
4309                 orig_offset++;
4310         } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
4311
4312         /* don't miss any data at the end */
4313         array_copy(destPtr, orignitems - orig_offset,
4314                            origPtr, orig_offset, origBitmap,
4315                            typlen, typbyval, typalign);
4316         if (destBitmap)
4317                 array_bitmap_copy(destBitmap, dest_offset,
4318                                                   origBitmap, orig_offset,
4319                                                   orignitems - orig_offset);
4320 }
4321
4322 /*
4323  * accumArrayResult - accumulate one (more) Datum for an array result
4324  *
4325  *      astate is working state (NULL on first call)
4326  *      rcontext is where to keep working state
4327  */
4328 ArrayBuildState *
4329 accumArrayResult(ArrayBuildState *astate,
4330                                  Datum dvalue, bool disnull,
4331                                  Oid element_type,
4332                                  MemoryContext rcontext)
4333 {
4334         MemoryContext arr_context,
4335                                 oldcontext;
4336
4337         if (astate == NULL)
4338         {
4339                 /* First time through --- initialize */
4340
4341                 /* Make a temporary context to hold all the junk */
4342                 arr_context = AllocSetContextCreate(rcontext,
4343                                                                                         "accumArrayResult",
4344                                                                                         ALLOCSET_DEFAULT_MINSIZE,
4345                                                                                         ALLOCSET_DEFAULT_INITSIZE,
4346                                                                                         ALLOCSET_DEFAULT_MAXSIZE);
4347                 oldcontext = MemoryContextSwitchTo(arr_context);
4348                 astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
4349                 astate->mcontext = arr_context;
4350                 astate->alen = 64;              /* arbitrary starting array size */
4351                 astate->dvalues = (Datum *) palloc(astate->alen * sizeof(Datum));
4352                 astate->dnulls = (bool *) palloc(astate->alen * sizeof(bool));
4353                 astate->nelems = 0;
4354                 astate->element_type = element_type;
4355                 get_typlenbyvalalign(element_type,
4356                                                          &astate->typlen,
4357                                                          &astate->typbyval,
4358                                                          &astate->typalign);
4359         }
4360         else
4361         {
4362                 oldcontext = MemoryContextSwitchTo(astate->mcontext);
4363                 Assert(astate->element_type == element_type);
4364                 /* enlarge dvalues[]/dnulls[] if needed */
4365                 if (astate->nelems >= astate->alen)
4366                 {
4367                         astate->alen *= 2;
4368                         astate->dvalues = (Datum *)
4369                                 repalloc(astate->dvalues, astate->alen * sizeof(Datum));
4370                         astate->dnulls = (bool *)
4371                                 repalloc(astate->dnulls, astate->alen * sizeof(bool));
4372                 }
4373         }
4374
4375         /*
4376          * Ensure pass-by-ref stuff is copied into mcontext; and detoast it too if
4377          * it's varlena.  (You might think that detoasting is not needed here
4378          * because construct_md_array can detoast the array elements later.
4379          * However, we must not let construct_md_array modify the ArrayBuildState
4380          * because that would mean array_agg_finalfn damages its input, which is
4381          * verboten.  Also, this way frequently saves one copying step.)
4382          */
4383         if (!disnull && !astate->typbyval)
4384         {
4385                 if (astate->typlen == -1)
4386                         dvalue = PointerGetDatum(PG_DETOAST_DATUM_COPY(dvalue));
4387                 else
4388                         dvalue = datumCopy(dvalue, astate->typbyval, astate->typlen);
4389         }
4390
4391         astate->dvalues[astate->nelems] = dvalue;
4392         astate->dnulls[astate->nelems] = disnull;
4393         astate->nelems++;
4394
4395         MemoryContextSwitchTo(oldcontext);
4396
4397         return astate;
4398 }
4399
4400 /*
4401  * makeArrayResult - produce 1-D final result of accumArrayResult
4402  *
4403  *      astate is working state (not NULL)
4404  *      rcontext is where to construct result
4405  */
4406 Datum
4407 makeArrayResult(ArrayBuildState *astate,
4408                                 MemoryContext rcontext)
4409 {
4410         int                     dims[1];
4411         int                     lbs[1];
4412
4413         dims[0] = astate->nelems;
4414         lbs[0] = 1;
4415
4416         return makeMdArrayResult(astate, 1, dims, lbs, rcontext, true);
4417 }
4418
4419 /*
4420  * makeMdArrayResult - produce multi-D final result of accumArrayResult
4421  *
4422  * beware: no check that specified dimensions match the number of values
4423  * accumulated.
4424  *
4425  *      astate is working state (not NULL)
4426  *      rcontext is where to construct result
4427  *      release is true if okay to release working state
4428  */
4429 Datum
4430 makeMdArrayResult(ArrayBuildState *astate,
4431                                   int ndims,
4432                                   int *dims,
4433                                   int *lbs,
4434                                   MemoryContext rcontext,
4435                                   bool release)
4436 {
4437         ArrayType  *result;
4438         MemoryContext oldcontext;
4439
4440         /* Build the final array result in rcontext */
4441         oldcontext = MemoryContextSwitchTo(rcontext);
4442
4443         result = construct_md_array(astate->dvalues,
4444                                                                 astate->dnulls,
4445                                                                 ndims,
4446                                                                 dims,
4447                                                                 lbs,
4448                                                                 astate->element_type,
4449                                                                 astate->typlen,
4450                                                                 astate->typbyval,
4451                                                                 astate->typalign);
4452
4453         MemoryContextSwitchTo(oldcontext);
4454
4455         /* Clean up all the junk */
4456         if (release)
4457                 MemoryContextDelete(astate->mcontext);
4458
4459         return PointerGetDatum(result);
4460 }
4461
4462 Datum
4463 array_larger(PG_FUNCTION_ARGS)
4464 {
4465         ArrayType  *v1,
4466                            *v2,
4467                            *result;
4468
4469         v1 = PG_GETARG_ARRAYTYPE_P(0);
4470         v2 = PG_GETARG_ARRAYTYPE_P(1);
4471
4472         result = ((array_cmp(fcinfo) > 0) ? v1 : v2);
4473
4474         PG_RETURN_ARRAYTYPE_P(result);
4475 }
4476
4477 Datum
4478 array_smaller(PG_FUNCTION_ARGS)
4479 {
4480         ArrayType  *v1,
4481                            *v2,
4482                            *result;
4483
4484         v1 = PG_GETARG_ARRAYTYPE_P(0);
4485         v2 = PG_GETARG_ARRAYTYPE_P(1);
4486
4487         result = ((array_cmp(fcinfo) < 0) ? v1 : v2);
4488
4489         PG_RETURN_ARRAYTYPE_P(result);
4490 }
4491
4492
4493 typedef struct generate_subscripts_fctx
4494 {
4495         int4            lower;
4496         int4            upper;
4497         bool            reverse;
4498 } generate_subscripts_fctx;
4499
4500 /*
4501  * generate_subscripts(array anyarray, dim int [, reverse bool])
4502  *              Returns all subscripts of the array for any dimension
4503  */
4504 Datum
4505 generate_subscripts(PG_FUNCTION_ARGS)
4506 {
4507         FuncCallContext *funcctx;
4508         MemoryContext oldcontext;
4509         generate_subscripts_fctx *fctx;
4510
4511         /* stuff done only on the first call of the function */
4512         if (SRF_IS_FIRSTCALL())
4513         {
4514                 ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
4515                 int                     reqdim = PG_GETARG_INT32(1);
4516                 int                *lb,
4517                                    *dimv;
4518
4519                 /* create a function context for cross-call persistence */
4520                 funcctx = SRF_FIRSTCALL_INIT();
4521
4522                 /* Sanity check: does it look like an array at all? */
4523                 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
4524                         SRF_RETURN_DONE(funcctx);
4525
4526                 /* Sanity check: was the requested dim valid */
4527                 if (reqdim <= 0 || reqdim > ARR_NDIM(v))
4528                         SRF_RETURN_DONE(funcctx);
4529
4530                 /*
4531                  * switch to memory context appropriate for multiple function calls
4532                  */
4533                 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
4534                 fctx = (generate_subscripts_fctx *) palloc(sizeof(generate_subscripts_fctx));
4535
4536                 lb = ARR_LBOUND(v);
4537                 dimv = ARR_DIMS(v);
4538
4539                 fctx->lower = lb[reqdim - 1];
4540                 fctx->upper = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
4541                 fctx->reverse = (PG_NARGS() < 3) ? false : PG_GETARG_BOOL(2);
4542
4543                 funcctx->user_fctx = fctx;
4544
4545                 MemoryContextSwitchTo(oldcontext);
4546         }
4547
4548         funcctx = SRF_PERCALL_SETUP();
4549
4550         fctx = funcctx->user_fctx;
4551
4552         if (fctx->lower <= fctx->upper)
4553         {
4554                 if (!fctx->reverse)
4555                         SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->lower++));
4556                 else
4557                         SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->upper--));
4558         }
4559         else
4560                 /* done when there are no more elements left */
4561                 SRF_RETURN_DONE(funcctx);
4562 }
4563
4564 /*
4565  * generate_subscripts_nodir
4566  *              Implements the 2-argument version of generate_subscripts
4567  */
4568 Datum
4569 generate_subscripts_nodir(PG_FUNCTION_ARGS)
4570 {
4571         /* just call the other one -- it can handle both cases */
4572         return generate_subscripts(fcinfo);
4573 }
4574
4575 /*
4576  * array_fill_with_lower_bounds
4577  *              Create and fill array with defined lower bounds.
4578  */
4579 Datum
4580 array_fill_with_lower_bounds(PG_FUNCTION_ARGS)
4581 {
4582         ArrayType  *dims;
4583         ArrayType  *lbs;
4584         ArrayType  *result;
4585         Oid                     elmtype;
4586         Datum           value;
4587         bool            isnull;
4588
4589         if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
4590                 ereport(ERROR,
4591                                 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
4592                            errmsg("dimension array or low bound array cannot be NULL")));
4593
4594         dims = PG_GETARG_ARRAYTYPE_P(1);
4595         lbs = PG_GETARG_ARRAYTYPE_P(2);
4596
4597         if (!PG_ARGISNULL(0))
4598         {
4599                 value = PG_GETARG_DATUM(0);
4600                 isnull = false;
4601         }
4602         else
4603         {
4604                 value = 0;
4605                 isnull = true;
4606         }
4607
4608         elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
4609         if (!OidIsValid(elmtype))
4610                 elog(ERROR, "could not determine data type of input");
4611
4612         result = array_fill_internal(dims, lbs, value, isnull, elmtype, fcinfo);
4613         PG_RETURN_ARRAYTYPE_P(result);
4614 }
4615
4616 /*
4617  * array_fill
4618  *              Create and fill array with default lower bounds.
4619  */
4620 Datum
4621 array_fill(PG_FUNCTION_ARGS)
4622 {
4623         ArrayType  *dims;
4624         ArrayType  *result;
4625         Oid                     elmtype;
4626         Datum           value;
4627         bool            isnull;
4628
4629         if (PG_ARGISNULL(1))
4630                 ereport(ERROR,
4631                                 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
4632                            errmsg("dimension array or low bound array cannot be NULL")));
4633
4634         dims = PG_GETARG_ARRAYTYPE_P(1);
4635
4636         if (!PG_ARGISNULL(0))
4637         {
4638                 value = PG_GETARG_DATUM(0);
4639                 isnull = false;
4640         }
4641         else
4642         {
4643                 value = 0;
4644                 isnull = true;
4645         }
4646
4647         elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
4648         if (!OidIsValid(elmtype))
4649                 elog(ERROR, "could not determine data type of input");
4650
4651         result = array_fill_internal(dims, NULL, value, isnull, elmtype, fcinfo);
4652         PG_RETURN_ARRAYTYPE_P(result);
4653 }
4654
4655 static ArrayType *
4656 create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
4657                                           Oid elmtype, int dataoffset)
4658 {
4659         ArrayType  *result;
4660
4661         result = (ArrayType *) palloc0(nbytes);
4662         SET_VARSIZE(result, nbytes);
4663         result->ndim = ndims;
4664         result->dataoffset = dataoffset;
4665         result->elemtype = elmtype;
4666         memcpy(ARR_DIMS(result), dimv, ndims * sizeof(int));
4667         memcpy(ARR_LBOUND(result), lbsv, ndims * sizeof(int));
4668
4669         return result;
4670 }
4671
4672 static ArrayType *
4673 array_fill_internal(ArrayType *dims, ArrayType *lbs,
4674                                         Datum value, bool isnull, Oid elmtype,
4675                                         FunctionCallInfo fcinfo)
4676 {
4677         ArrayType  *result;
4678         int                *dimv;
4679         int                *lbsv;
4680         int                     ndims;
4681         int                     nitems;
4682         int                     deflbs[MAXDIM];
4683         int16           elmlen;
4684         bool            elmbyval;
4685         char            elmalign;
4686         ArrayMetaState *my_extra;
4687
4688         /*
4689          * Params checks
4690          */
4691         if (ARR_NDIM(dims) != 1)
4692                 ereport(ERROR,
4693                                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4694                                  errmsg("wrong number of array subscripts"),
4695                                  errdetail("Dimension array must be one dimensional.")));
4696
4697         if (ARR_LBOUND(dims)[0] != 1)
4698                 ereport(ERROR,
4699                                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4700                                  errmsg("wrong range of array subscripts"),
4701                                  errdetail("Lower bound of dimension array must be one.")));
4702
4703         if (ARR_HASNULL(dims))
4704                 ereport(ERROR,
4705                                 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
4706                                  errmsg("dimension values cannot be null")));
4707
4708         dimv = (int *) ARR_DATA_PTR(dims);
4709         ndims = ARR_DIMS(dims)[0];
4710
4711         if (ndims < 0)                          /* we do allow zero-dimension arrays */
4712                 ereport(ERROR,
4713                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4714                                  errmsg("invalid number of dimensions: %d", ndims)));
4715         if (ndims > MAXDIM)
4716                 ereport(ERROR,
4717                                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
4718                                  errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
4719                                                 ndims, MAXDIM)));
4720
4721         if (lbs != NULL)
4722         {
4723                 if (ARR_NDIM(lbs) != 1)
4724                         ereport(ERROR,
4725                                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4726                                          errmsg("wrong number of array subscripts"),
4727                                          errdetail("Dimension array must be one dimensional.")));
4728
4729                 if (ARR_LBOUND(lbs)[0] != 1)
4730                         ereport(ERROR,
4731                                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4732                                          errmsg("wrong range of array subscripts"),
4733                                   errdetail("Lower bound of dimension array must be one.")));
4734
4735                 if (ARR_HASNULL(lbs))
4736                         ereport(ERROR,
4737                                         (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
4738                                          errmsg("dimension values cannot be null")));
4739
4740                 if (ARR_DIMS(lbs)[0] != ndims)
4741                         ereport(ERROR,
4742                                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4743                                          errmsg("wrong number of array subscripts"),
4744                                          errdetail("Low bound array has different size than dimensions array.")));
4745
4746                 lbsv = (int *) ARR_DATA_PTR(lbs);
4747         }
4748         else
4749         {
4750                 int                     i;
4751
4752                 for (i = 0; i < MAXDIM; i++)
4753                         deflbs[i] = 1;
4754
4755                 lbsv = deflbs;
4756         }
4757
4758         /* fast track for empty array */
4759         if (ndims == 0)
4760                 return construct_empty_array(elmtype);
4761
4762         nitems = ArrayGetNItems(ndims, dimv);
4763
4764         /*
4765          * We arrange to look up info about element type only once per series of
4766          * calls, assuming the element type doesn't change underneath us.
4767          */
4768         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
4769         if (my_extra == NULL)
4770         {
4771                 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
4772                                                                                                           sizeof(ArrayMetaState));
4773                 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
4774                 my_extra->element_type = InvalidOid;
4775         }
4776
4777         if (my_extra->element_type != elmtype)
4778         {
4779                 /* Get info about element type */
4780                 get_typlenbyvalalign(elmtype,
4781                                                          &my_extra->typlen,
4782                                                          &my_extra->typbyval,
4783                                                          &my_extra->typalign);
4784                 my_extra->element_type = elmtype;
4785         }
4786
4787         elmlen = my_extra->typlen;
4788         elmbyval = my_extra->typbyval;
4789         elmalign = my_extra->typalign;
4790
4791         /* compute required space */
4792         if (!isnull)
4793         {
4794                 int                     i;
4795                 char       *p;
4796                 int                     nbytes;
4797                 int                     totbytes;
4798
4799                 /* make sure data is not toasted */
4800                 if (elmlen == -1)
4801                         value = PointerGetDatum(PG_DETOAST_DATUM(value));
4802
4803                 nbytes = att_addlength_datum(0, elmlen, value);
4804                 nbytes = att_align_nominal(nbytes, elmalign);
4805                 Assert(nbytes > 0);
4806
4807                 totbytes = nbytes * nitems;
4808
4809                 /* check for overflow of multiplication or total request */
4810                 if (totbytes / nbytes != nitems ||
4811                         !AllocSizeIsValid(totbytes))
4812                         ereport(ERROR,
4813                                         (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
4814                                          errmsg("array size exceeds the maximum allowed (%d)",
4815                                                         (int) MaxAllocSize)));
4816
4817                 /*
4818                  * This addition can't overflow, but it might cause us to go past
4819                  * MaxAllocSize.  We leave it to palloc to complain in that case.
4820                  */
4821                 totbytes += ARR_OVERHEAD_NONULLS(ndims);
4822
4823                 result = create_array_envelope(ndims, dimv, lbsv, totbytes,
4824                                                                            elmtype, 0);
4825
4826                 p = ARR_DATA_PTR(result);
4827                 for (i = 0; i < nitems; i++)
4828                         p += ArrayCastAndSet(value, elmlen, elmbyval, elmalign, p);
4829         }
4830         else
4831         {
4832                 int                     nbytes;
4833                 int                     dataoffset;
4834
4835                 dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
4836                 nbytes = dataoffset;
4837
4838                 result = create_array_envelope(ndims, dimv, lbsv, nbytes,
4839                                                                            elmtype, dataoffset);
4840
4841                 /* create_array_envelope already zeroed the bitmap, so we're done */
4842         }
4843
4844         return result;
4845 }
4846
4847
4848 /*
4849  * UNNEST
4850  */
4851 Datum
4852 array_unnest(PG_FUNCTION_ARGS)
4853 {
4854         typedef struct
4855         {
4856                 ArrayType  *arr;
4857                 int                     nextelem;
4858                 int                     numelems;
4859                 char       *elemdataptr;        /* this moves with nextelem */
4860                 bits8      *arraynullsptr;              /* this does not */
4861                 int16           elmlen;
4862                 bool            elmbyval;
4863                 char            elmalign;
4864         } array_unnest_fctx;
4865
4866         FuncCallContext *funcctx;
4867         array_unnest_fctx *fctx;
4868         MemoryContext oldcontext;
4869
4870         /* stuff done only on the first call of the function */
4871         if (SRF_IS_FIRSTCALL())
4872         {
4873                 ArrayType  *arr;
4874
4875                 /* create a function context for cross-call persistence */
4876                 funcctx = SRF_FIRSTCALL_INIT();
4877
4878                 /*
4879                  * switch to memory context appropriate for multiple function calls
4880                  */
4881                 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
4882
4883                 /*
4884                  * Get the array value and detoast if needed.  We can't do this
4885                  * earlier because if we have to detoast, we want the detoasted copy
4886                  * to be in multi_call_memory_ctx, so it will go away when we're done
4887                  * and not before.      (If no detoast happens, we assume the originally
4888                  * passed array will stick around till then.)
4889                  */
4890                 arr = PG_GETARG_ARRAYTYPE_P(0);
4891
4892                 /* allocate memory for user context */
4893                 fctx = (array_unnest_fctx *) palloc(sizeof(array_unnest_fctx));
4894
4895                 /* initialize state */
4896                 fctx->arr = arr;
4897                 fctx->nextelem = 0;
4898                 fctx->numelems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
4899
4900                 fctx->elemdataptr = ARR_DATA_PTR(arr);
4901                 fctx->arraynullsptr = ARR_NULLBITMAP(arr);
4902
4903                 get_typlenbyvalalign(ARR_ELEMTYPE(arr),
4904                                                          &fctx->elmlen,
4905                                                          &fctx->elmbyval,
4906                                                          &fctx->elmalign);
4907
4908                 funcctx->user_fctx = fctx;
4909                 MemoryContextSwitchTo(oldcontext);
4910         }
4911
4912         /* stuff done on every call of the function */
4913         funcctx = SRF_PERCALL_SETUP();
4914         fctx = funcctx->user_fctx;
4915
4916         if (fctx->nextelem < fctx->numelems)
4917         {
4918                 int                     offset = fctx->nextelem++;
4919                 Datum           elem;
4920
4921                 /*
4922                  * Check for NULL array element
4923                  */
4924                 if (array_get_isnull(fctx->arraynullsptr, offset))
4925                 {
4926                         fcinfo->isnull = true;
4927                         elem = (Datum) 0;
4928                         /* elemdataptr does not move */
4929                 }
4930                 else
4931                 {
4932                         /*
4933                          * OK, get the element
4934                          */
4935                         char       *ptr = fctx->elemdataptr;
4936
4937                         fcinfo->isnull = false;
4938                         elem = ArrayCast(ptr, fctx->elmbyval, fctx->elmlen);
4939
4940                         /*
4941                          * Advance elemdataptr over it
4942                          */
4943                         ptr = att_addlength_pointer(ptr, fctx->elmlen, ptr);
4944                         ptr = (char *) att_align_nominal(ptr, fctx->elmalign);
4945                         fctx->elemdataptr = ptr;
4946                 }
4947
4948                 SRF_RETURN_NEXT(funcctx, elem);
4949         }
4950         else
4951         {
4952                 /* do when there is no more left */
4953                 SRF_RETURN_DONE(funcctx);
4954         }
4955 }