OSDN Git Service

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