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