PostgreSQL Source Code  git master
arrayfuncs.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * arrayfuncs.c
4  * Support functions for arrays.
5  *
6  * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  * src/backend/utils/adt/arrayfuncs.c
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16 
17 #include <ctype.h>
18 #include <math.h>
19 
20 #include "access/htup_details.h"
21 #include "catalog/pg_type.h"
22 #include "funcapi.h"
23 #include "libpq/pqformat.h"
24 #include "nodes/nodeFuncs.h"
25 #include "nodes/supportnodes.h"
26 #include "optimizer/optimizer.h"
27 #include "utils/array.h"
28 #include "utils/arrayaccess.h"
29 #include "utils/builtins.h"
30 #include "utils/datum.h"
31 #include "utils/lsyscache.h"
32 #include "utils/memutils.h"
33 #include "utils/selfuncs.h"
34 #include "utils/typcache.h"
35 
36 
37 /*
38  * GUC parameter
39  */
40 bool Array_nulls = true;
41 
42 /*
43  * Local definitions
44  */
45 #define ASSGN "="
46 
47 #define AARR_FREE_IF_COPY(array,n) \
48  do { \
49  if (!VARATT_IS_EXPANDED_HEADER(array)) \
50  PG_FREE_IF_COPY(array, n); \
51  } while (0)
52 
53 typedef enum
54 {
65 
66 /* Working state for array_iterate() */
67 typedef struct ArrayIteratorData
68 {
69  /* basic info about the array, set up during array_create_iterator() */
70  ArrayType *arr; /* array we're iterating through */
71  bits8 *nullbitmap; /* its null bitmap, if any */
72  int nitems; /* total number of elements in array */
73  int16 typlen; /* element type's length */
74  bool typbyval; /* element type's byval property */
75  char typalign; /* element type's align property */
76 
77  /* information about the requested slice size */
78  int slice_ndim; /* slice dimension, or 0 if not slicing */
79  int slice_len; /* number of elements per slice */
80  int *slice_dims; /* slice dims array */
81  int *slice_lbound; /* slice lbound array */
82  Datum *slice_values; /* workspace of length slice_len */
83  bool *slice_nulls; /* workspace of length slice_len */
84 
85  /* current position information, updated on each iteration */
86  char *data_ptr; /* our current position in the array */
87  int current_item; /* the item # we're at in the array */
89 
90 static bool array_isspace(char ch);
91 static int ArrayCount(const char *str, int *dim, char typdelim);
92 static void ReadArrayStr(char *arrayStr, const char *origStr,
93  int nitems, int ndim, int *dim,
94  FmgrInfo *inputproc, Oid typioparam, int32 typmod,
95  char typdelim,
96  int typlen, bool typbyval, char typalign,
97  Datum *values, bool *nulls,
98  bool *hasnulls, int32 *nbytes);
99 static void ReadArrayBinary(StringInfo buf, int nitems,
100  FmgrInfo *receiveproc, Oid typioparam, int32 typmod,
101  int typlen, bool typbyval, char typalign,
102  Datum *values, bool *nulls,
103  bool *hasnulls, int32 *nbytes);
104 static Datum array_get_element_expanded(Datum arraydatum,
105  int nSubscripts, int *indx,
106  int arraytyplen,
107  int elmlen, bool elmbyval, char elmalign,
108  bool *isNull);
109 static Datum array_set_element_expanded(Datum arraydatum,
110  int nSubscripts, int *indx,
111  Datum dataValue, bool isNull,
112  int arraytyplen,
113  int elmlen, bool elmbyval, char elmalign);
114 static bool array_get_isnull(const bits8 *nullbitmap, int offset);
115 static void array_set_isnull(bits8 *nullbitmap, int offset, bool isNull);
116 static Datum ArrayCast(char *value, bool byval, int len);
117 static int ArrayCastAndSet(Datum src,
118  int typlen, bool typbyval, char typalign,
119  char *dest);
120 static char *array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
121  int typlen, bool typbyval, char typalign);
122 static int array_nelems_size(char *ptr, int offset, bits8 *nullbitmap,
123  int nitems, int typlen, bool typbyval, char typalign);
124 static int array_copy(char *destptr, int nitems,
125  char *srcptr, int offset, bits8 *nullbitmap,
126  int typlen, bool typbyval, char typalign);
127 static int array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
128  int ndim, int *dim, int *lb,
129  int *st, int *endp,
130  int typlen, bool typbyval, char typalign);
131 static void array_extract_slice(ArrayType *newarray,
132  int ndim, int *dim, int *lb,
133  char *arraydataptr, bits8 *arraynullsptr,
134  int *st, int *endp,
135  int typlen, bool typbyval, char typalign);
136 static void array_insert_slice(ArrayType *destArray, ArrayType *origArray,
137  ArrayType *srcArray,
138  int ndim, int *dim, int *lb,
139  int *st, int *endp,
140  int typlen, bool typbyval, char typalign);
141 static int array_cmp(FunctionCallInfo fcinfo);
142 static ArrayType *create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
143  Oid elmtype, int dataoffset);
145  Datum value, bool isnull, Oid elmtype,
146  FunctionCallInfo fcinfo);
148  Datum search, bool search_isnull,
149  Datum replace, bool replace_isnull,
150  bool remove, Oid collation,
151  FunctionCallInfo fcinfo);
152 static int width_bucket_array_float8(Datum operand, ArrayType *thresholds);
153 static int width_bucket_array_fixed(Datum operand,
154  ArrayType *thresholds,
155  Oid collation,
156  TypeCacheEntry *typentry);
157 static int width_bucket_array_variable(Datum operand,
158  ArrayType *thresholds,
159  Oid collation,
160  TypeCacheEntry *typentry);
161 
162 
163 /*
164  * array_in :
165  * converts an array from the external format in "string" to
166  * its internal format.
167  *
168  * return value :
169  * the internal representation of the input array
170  */
171 Datum
173 {
174  char *string = PG_GETARG_CSTRING(0); /* external form */
175  Oid element_type = PG_GETARG_OID(1); /* type of an array
176  * element */
177  int32 typmod = PG_GETARG_INT32(2); /* typmod for array elements */
178  int typlen;
179  bool typbyval;
180  char typalign;
181  char typdelim;
182  Oid typioparam;
183  char *string_save,
184  *p;
185  int i,
186  nitems;
187  Datum *dataPtr;
188  bool *nullsPtr;
189  bool hasnulls;
190  int32 nbytes;
191  int32 dataoffset;
192  ArrayType *retval;
193  int ndim,
194  dim[MAXDIM],
195  lBound[MAXDIM];
196  ArrayMetaState *my_extra;
197 
198  /*
199  * We arrange to look up info about element type, including its input
200  * conversion proc, only once per series of calls, assuming the element
201  * type doesn't change underneath us.
202  */
203  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
204  if (my_extra == NULL)
205  {
206  fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
207  sizeof(ArrayMetaState));
208  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
209  my_extra->element_type = ~element_type;
210  }
211 
212  if (my_extra->element_type != element_type)
213  {
214  /*
215  * Get info about element type, including its input conversion proc
216  */
217  get_type_io_data(element_type, IOFunc_input,
218  &my_extra->typlen, &my_extra->typbyval,
219  &my_extra->typalign, &my_extra->typdelim,
220  &my_extra->typioparam, &my_extra->typiofunc);
221  fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
222  fcinfo->flinfo->fn_mcxt);
223  my_extra->element_type = element_type;
224  }
225  typlen = my_extra->typlen;
226  typbyval = my_extra->typbyval;
227  typalign = my_extra->typalign;
228  typdelim = my_extra->typdelim;
229  typioparam = my_extra->typioparam;
230 
231  /* Make a modifiable copy of the input */
232  string_save = pstrdup(string);
233 
234  /*
235  * If the input string starts with dimension info, read and use that.
236  * Otherwise, we require the input to be in curly-brace style, and we
237  * prescan the input to determine dimensions.
238  *
239  * Dimension info takes the form of one or more [n] or [m:n] items. The
240  * outer loop iterates once per dimension item.
241  */
242  p = string_save;
243  ndim = 0;
244  for (;;)
245  {
246  char *q;
247  int ub;
248 
249  /*
250  * Note: we currently allow whitespace between, but not within,
251  * dimension items.
252  */
253  while (array_isspace(*p))
254  p++;
255  if (*p != '[')
256  break; /* no more dimension items */
257  p++;
258  if (ndim >= MAXDIM)
259  ereport(ERROR,
260  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
261  errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
262  ndim + 1, MAXDIM)));
263 
264  for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++)
265  /* skip */ ;
266  if (q == p) /* no digits? */
267  ereport(ERROR,
268  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
269  errmsg("malformed array literal: \"%s\"", string),
270  errdetail("\"[\" must introduce explicitly-specified array dimensions.")));
271 
272  if (*q == ':')
273  {
274  /* [m:n] format */
275  *q = '\0';
276  lBound[ndim] = atoi(p);
277  p = q + 1;
278  for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++)
279  /* skip */ ;
280  if (q == p) /* no digits? */
281  ereport(ERROR,
282  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
283  errmsg("malformed array literal: \"%s\"", string),
284  errdetail("Missing array dimension value.")));
285  }
286  else
287  {
288  /* [n] format */
289  lBound[ndim] = 1;
290  }
291  if (*q != ']')
292  ereport(ERROR,
293  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
294  errmsg("malformed array literal: \"%s\"", string),
295  errdetail("Missing \"%s\" after array dimensions.",
296  "]")));
297 
298  *q = '\0';
299  ub = atoi(p);
300  p = q + 1;
301  if (ub < lBound[ndim])
302  ereport(ERROR,
303  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
304  errmsg("upper bound cannot be less than lower bound")));
305 
306  dim[ndim] = ub - lBound[ndim] + 1;
307  ndim++;
308  }
309 
310  if (ndim == 0)
311  {
312  /* No array dimensions, so intuit dimensions from brace structure */
313  if (*p != '{')
314  ereport(ERROR,
315  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
316  errmsg("malformed array literal: \"%s\"", string),
317  errdetail("Array value must start with \"{\" or dimension information.")));
318  ndim = ArrayCount(p, dim, typdelim);
319  for (i = 0; i < ndim; i++)
320  lBound[i] = 1;
321  }
322  else
323  {
324  int ndim_braces,
325  dim_braces[MAXDIM];
326 
327  /* If array dimensions are given, expect '=' operator */
328  if (strncmp(p, ASSGN, strlen(ASSGN)) != 0)
329  ereport(ERROR,
330  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
331  errmsg("malformed array literal: \"%s\"", string),
332  errdetail("Missing \"%s\" after array dimensions.",
333  ASSGN)));
334  p += strlen(ASSGN);
335  while (array_isspace(*p))
336  p++;
337 
338  /*
339  * intuit dimensions from brace structure -- it better match what we
340  * were given
341  */
342  if (*p != '{')
343  ereport(ERROR,
344  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
345  errmsg("malformed array literal: \"%s\"", string),
346  errdetail("Array contents must start with \"{\".")));
347  ndim_braces = ArrayCount(p, dim_braces, typdelim);
348  if (ndim_braces != ndim)
349  ereport(ERROR,
350  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
351  errmsg("malformed array literal: \"%s\"", string),
352  errdetail("Specified array dimensions do not match array contents.")));
353  for (i = 0; i < ndim; ++i)
354  {
355  if (dim[i] != dim_braces[i])
356  ereport(ERROR,
357  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
358  errmsg("malformed array literal: \"%s\"", string),
359  errdetail("Specified array dimensions do not match array contents.")));
360  }
361  }
362 
363 #ifdef ARRAYDEBUG
364  printf("array_in- ndim %d (", ndim);
365  for (i = 0; i < ndim; i++)
366  {
367  printf(" %d", dim[i]);
368  };
369  printf(") for %s\n", string);
370 #endif
371 
372  /* This checks for overflow of the array dimensions */
373  nitems = ArrayGetNItems(ndim, dim);
374  /* Empty array? */
375  if (nitems == 0)
377 
378  dataPtr = (Datum *) palloc(nitems * sizeof(Datum));
379  nullsPtr = (bool *) palloc(nitems * sizeof(bool));
380  ReadArrayStr(p, string,
381  nitems, ndim, dim,
382  &my_extra->proc, typioparam, typmod,
383  typdelim,
384  typlen, typbyval, typalign,
385  dataPtr, nullsPtr,
386  &hasnulls, &nbytes);
387  if (hasnulls)
388  {
389  dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
390  nbytes += dataoffset;
391  }
392  else
393  {
394  dataoffset = 0; /* marker for no null bitmap */
395  nbytes += ARR_OVERHEAD_NONULLS(ndim);
396  }
397  retval = (ArrayType *) palloc0(nbytes);
398  SET_VARSIZE(retval, nbytes);
399  retval->ndim = ndim;
400  retval->dataoffset = dataoffset;
401 
402  /*
403  * This comes from the array's pg_type.typelem (which points to the base
404  * data type's pg_type.oid) and stores system oids in user tables. This
405  * oid must be preserved by binary upgrades.
406  */
407  retval->elemtype = element_type;
408  memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
409  memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
410 
411  CopyArrayEls(retval,
412  dataPtr, nullsPtr, nitems,
413  typlen, typbyval, typalign,
414  true);
415 
416  pfree(dataPtr);
417  pfree(nullsPtr);
418  pfree(string_save);
419 
420  PG_RETURN_ARRAYTYPE_P(retval);
421 }
422 
423 /*
424  * array_isspace() --- a non-locale-dependent isspace()
425  *
426  * We used to use isspace() for parsing array values, but that has
427  * undesirable results: an array value might be silently interpreted
428  * differently depending on the locale setting. Now we just hard-wire
429  * the traditional ASCII definition of isspace().
430  */
431 static bool
433 {
434  if (ch == ' ' ||
435  ch == '\t' ||
436  ch == '\n' ||
437  ch == '\r' ||
438  ch == '\v' ||
439  ch == '\f')
440  return true;
441  return false;
442 }
443 
444 /*
445  * ArrayCount
446  * Determines the dimensions for an array string.
447  *
448  * Returns number of dimensions as function result. The axis lengths are
449  * returned in dim[], which must be of size MAXDIM.
450  */
451 static int
452 ArrayCount(const char *str, int *dim, char typdelim)
453 {
454  int nest_level = 0,
455  i;
456  int ndim = 1,
457  temp[MAXDIM],
458  nelems[MAXDIM],
459  nelems_last[MAXDIM];
460  bool in_quotes = false;
461  bool eoArray = false;
462  bool empty_array = true;
463  const char *ptr;
464  ArrayParseState parse_state = ARRAY_NO_LEVEL;
465 
466  for (i = 0; i < MAXDIM; ++i)
467  {
468  temp[i] = dim[i] = nelems_last[i] = 0;
469  nelems[i] = 1;
470  }
471 
472  ptr = str;
473  while (!eoArray)
474  {
475  bool itemdone = false;
476 
477  while (!itemdone)
478  {
479  if (parse_state == ARRAY_ELEM_STARTED ||
480  parse_state == ARRAY_QUOTED_ELEM_STARTED)
481  empty_array = false;
482 
483  switch (*ptr)
484  {
485  case '\0':
486  /* Signal a premature end of the string */
487  ereport(ERROR,
488  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
489  errmsg("malformed array literal: \"%s\"", str),
490  errdetail("Unexpected end of input.")));
491  break;
492  case '\\':
493 
494  /*
495  * An escape must be after a level start, after an element
496  * start, or after an element delimiter. In any case we
497  * now must be past an element start.
498  */
499  if (parse_state != ARRAY_LEVEL_STARTED &&
500  parse_state != ARRAY_ELEM_STARTED &&
501  parse_state != ARRAY_QUOTED_ELEM_STARTED &&
502  parse_state != ARRAY_ELEM_DELIMITED)
503  ereport(ERROR,
504  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
505  errmsg("malformed array literal: \"%s\"", str),
506  errdetail("Unexpected \"%c\" character.",
507  '\\')));
508  if (parse_state != ARRAY_QUOTED_ELEM_STARTED)
509  parse_state = ARRAY_ELEM_STARTED;
510  /* skip the escaped character */
511  if (*(ptr + 1))
512  ptr++;
513  else
514  ereport(ERROR,
515  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
516  errmsg("malformed array literal: \"%s\"", str),
517  errdetail("Unexpected end of input.")));
518  break;
519  case '"':
520 
521  /*
522  * A quote must be after a level start, after a quoted
523  * element start, or after an element delimiter. In any
524  * case we now must be past an element start.
525  */
526  if (parse_state != ARRAY_LEVEL_STARTED &&
527  parse_state != ARRAY_QUOTED_ELEM_STARTED &&
528  parse_state != ARRAY_ELEM_DELIMITED)
529  ereport(ERROR,
530  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
531  errmsg("malformed array literal: \"%s\"", str),
532  errdetail("Unexpected array element.")));
533  in_quotes = !in_quotes;
534  if (in_quotes)
535  parse_state = ARRAY_QUOTED_ELEM_STARTED;
536  else
537  parse_state = ARRAY_QUOTED_ELEM_COMPLETED;
538  break;
539  case '{':
540  if (!in_quotes)
541  {
542  /*
543  * A left brace can occur if no nesting has occurred
544  * yet, after a level start, or after a level
545  * delimiter.
546  */
547  if (parse_state != ARRAY_NO_LEVEL &&
548  parse_state != ARRAY_LEVEL_STARTED &&
549  parse_state != ARRAY_LEVEL_DELIMITED)
550  ereport(ERROR,
551  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
552  errmsg("malformed array literal: \"%s\"", str),
553  errdetail("Unexpected \"%c\" character.",
554  '{')));
555  parse_state = ARRAY_LEVEL_STARTED;
556  if (nest_level >= MAXDIM)
557  ereport(ERROR,
558  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
559  errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
560  nest_level + 1, MAXDIM)));
561  temp[nest_level] = 0;
562  nest_level++;
563  if (ndim < nest_level)
564  ndim = nest_level;
565  }
566  break;
567  case '}':
568  if (!in_quotes)
569  {
570  /*
571  * A right brace can occur after an element start, an
572  * element completion, a quoted element completion, or
573  * a level completion.
574  */
575  if (parse_state != ARRAY_ELEM_STARTED &&
576  parse_state != ARRAY_ELEM_COMPLETED &&
577  parse_state != ARRAY_QUOTED_ELEM_COMPLETED &&
578  parse_state != ARRAY_LEVEL_COMPLETED &&
579  !(nest_level == 1 && parse_state == ARRAY_LEVEL_STARTED))
580  ereport(ERROR,
581  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
582  errmsg("malformed array literal: \"%s\"", str),
583  errdetail("Unexpected \"%c\" character.",
584  '}')));
585  parse_state = ARRAY_LEVEL_COMPLETED;
586  if (nest_level == 0)
587  ereport(ERROR,
588  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
589  errmsg("malformed array literal: \"%s\"", str),
590  errdetail("Unmatched \"%c\" character.", '}')));
591  nest_level--;
592 
593  if (nelems_last[nest_level] != 0 &&
594  nelems[nest_level] != nelems_last[nest_level])
595  ereport(ERROR,
596  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
597  errmsg("malformed array literal: \"%s\"", str),
598  errdetail("Multidimensional arrays must have "
599  "sub-arrays with matching "
600  "dimensions.")));
601  nelems_last[nest_level] = nelems[nest_level];
602  nelems[nest_level] = 1;
603  if (nest_level == 0)
604  eoArray = itemdone = true;
605  else
606  {
607  /*
608  * We don't set itemdone here; see comments in
609  * ReadArrayStr
610  */
611  temp[nest_level - 1]++;
612  }
613  }
614  break;
615  default:
616  if (!in_quotes)
617  {
618  if (*ptr == typdelim)
619  {
620  /*
621  * Delimiters can occur after an element start, an
622  * element completion, a quoted element
623  * completion, or a level completion.
624  */
625  if (parse_state != ARRAY_ELEM_STARTED &&
626  parse_state != ARRAY_ELEM_COMPLETED &&
627  parse_state != ARRAY_QUOTED_ELEM_COMPLETED &&
628  parse_state != ARRAY_LEVEL_COMPLETED)
629  ereport(ERROR,
630  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
631  errmsg("malformed array literal: \"%s\"", str),
632  errdetail("Unexpected \"%c\" character.",
633  typdelim)));
634  if (parse_state == ARRAY_LEVEL_COMPLETED)
635  parse_state = ARRAY_LEVEL_DELIMITED;
636  else
637  parse_state = ARRAY_ELEM_DELIMITED;
638  itemdone = true;
639  nelems[nest_level - 1]++;
640  }
641  else if (!array_isspace(*ptr))
642  {
643  /*
644  * Other non-space characters must be after a
645  * level start, after an element start, or after
646  * an element delimiter. In any case we now must
647  * be past an element start.
648  */
649  if (parse_state != ARRAY_LEVEL_STARTED &&
650  parse_state != ARRAY_ELEM_STARTED &&
651  parse_state != ARRAY_ELEM_DELIMITED)
652  ereport(ERROR,
653  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
654  errmsg("malformed array literal: \"%s\"", str),
655  errdetail("Unexpected array element.")));
656  parse_state = ARRAY_ELEM_STARTED;
657  }
658  }
659  break;
660  }
661  if (!itemdone)
662  ptr++;
663  }
664  temp[ndim - 1]++;
665  ptr++;
666  }
667 
668  /* only whitespace is allowed after the closing brace */
669  while (*ptr)
670  {
671  if (!array_isspace(*ptr++))
672  ereport(ERROR,
673  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
674  errmsg("malformed array literal: \"%s\"", str),
675  errdetail("Junk after closing right brace.")));
676  }
677 
678  /* special case for an empty array */
679  if (empty_array)
680  return 0;
681 
682  for (i = 0; i < ndim; ++i)
683  dim[i] = temp[i];
684 
685  return ndim;
686 }
687 
688 /*
689  * ReadArrayStr :
690  * parses the array string pointed to by "arrayStr" and converts the values
691  * to internal format. Unspecified elements are initialized to nulls.
692  * The array dimensions must already have been determined.
693  *
694  * Inputs:
695  * arrayStr: the string to parse.
696  * CAUTION: the contents of "arrayStr" will be modified!
697  * origStr: the unmodified input string, used only in error messages.
698  * nitems: total number of array elements, as already determined.
699  * ndim: number of array dimensions
700  * dim[]: array axis lengths
701  * inputproc: type-specific input procedure for element datatype.
702  * typioparam, typmod: auxiliary values to pass to inputproc.
703  * typdelim: the value delimiter (type-specific).
704  * typlen, typbyval, typalign: storage parameters of element datatype.
705  *
706  * Outputs:
707  * values[]: filled with converted data values.
708  * nulls[]: filled with is-null markers.
709  * *hasnulls: set true iff there are any null elements.
710  * *nbytes: set to total size of data area needed (including alignment
711  * padding but not including array header overhead).
712  *
713  * Note that values[] and nulls[] are allocated by the caller, and must have
714  * nitems elements.
715  */
716 static void
717 ReadArrayStr(char *arrayStr,
718  const char *origStr,
719  int nitems,
720  int ndim,
721  int *dim,
722  FmgrInfo *inputproc,
723  Oid typioparam,
724  int32 typmod,
725  char typdelim,
726  int typlen,
727  bool typbyval,
728  char typalign,
729  Datum *values,
730  bool *nulls,
731  bool *hasnulls,
732  int32 *nbytes)
733 {
734  int i,
735  nest_level = 0;
736  char *srcptr;
737  bool in_quotes = false;
738  bool eoArray = false;
739  bool hasnull;
740  int32 totbytes;
741  int indx[MAXDIM],
742  prod[MAXDIM];
743 
744  mda_get_prod(ndim, dim, prod);
745  MemSet(indx, 0, sizeof(indx));
746 
747  /* Initialize is-null markers to true */
748  memset(nulls, true, nitems * sizeof(bool));
749 
750  /*
751  * We have to remove " and \ characters to create a clean item value to
752  * pass to the datatype input routine. We overwrite each item value
753  * in-place within arrayStr to do this. srcptr is the current scan point,
754  * and dstptr is where we are copying to.
755  *
756  * We also want to suppress leading and trailing unquoted whitespace. We
757  * use the leadingspace flag to suppress leading space. Trailing space is
758  * tracked by using dstendptr to point to the last significant output
759  * character.
760  *
761  * The error checking in this routine is mostly pro-forma, since we expect
762  * that ArrayCount() already validated the string. So we don't bother
763  * with errdetail messages.
764  */
765  srcptr = arrayStr;
766  while (!eoArray)
767  {
768  bool itemdone = false;
769  bool leadingspace = true;
770  bool hasquoting = false;
771  char *itemstart;
772  char *dstptr;
773  char *dstendptr;
774 
775  i = -1;
776  itemstart = dstptr = dstendptr = srcptr;
777 
778  while (!itemdone)
779  {
780  switch (*srcptr)
781  {
782  case '\0':
783  /* Signal a premature end of the string */
784  ereport(ERROR,
785  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
786  errmsg("malformed array literal: \"%s\"",
787  origStr)));
788  break;
789  case '\\':
790  /* Skip backslash, copy next character as-is. */
791  srcptr++;
792  if (*srcptr == '\0')
793  ereport(ERROR,
794  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
795  errmsg("malformed array literal: \"%s\"",
796  origStr)));
797  *dstptr++ = *srcptr++;
798  /* Treat the escaped character as non-whitespace */
799  leadingspace = false;
800  dstendptr = dstptr;
801  hasquoting = true; /* can't be a NULL marker */
802  break;
803  case '"':
804  in_quotes = !in_quotes;
805  if (in_quotes)
806  leadingspace = false;
807  else
808  {
809  /*
810  * Advance dstendptr when we exit in_quotes; this
811  * saves having to do it in all the other in_quotes
812  * cases.
813  */
814  dstendptr = dstptr;
815  }
816  hasquoting = true; /* can't be a NULL marker */
817  srcptr++;
818  break;
819  case '{':
820  if (!in_quotes)
821  {
822  if (nest_level >= ndim)
823  ereport(ERROR,
824  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
825  errmsg("malformed array literal: \"%s\"",
826  origStr)));
827  nest_level++;
828  indx[nest_level - 1] = 0;
829  srcptr++;
830  }
831  else
832  *dstptr++ = *srcptr++;
833  break;
834  case '}':
835  if (!in_quotes)
836  {
837  if (nest_level == 0)
838  ereport(ERROR,
839  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
840  errmsg("malformed array literal: \"%s\"",
841  origStr)));
842  if (i == -1)
843  i = ArrayGetOffset0(ndim, indx, prod);
844  indx[nest_level - 1] = 0;
845  nest_level--;
846  if (nest_level == 0)
847  eoArray = itemdone = true;
848  else
849  indx[nest_level - 1]++;
850  srcptr++;
851  }
852  else
853  *dstptr++ = *srcptr++;
854  break;
855  default:
856  if (in_quotes)
857  *dstptr++ = *srcptr++;
858  else if (*srcptr == typdelim)
859  {
860  if (i == -1)
861  i = ArrayGetOffset0(ndim, indx, prod);
862  itemdone = true;
863  indx[ndim - 1]++;
864  srcptr++;
865  }
866  else if (array_isspace(*srcptr))
867  {
868  /*
869  * If leading space, drop it immediately. Else, copy
870  * but don't advance dstendptr.
871  */
872  if (leadingspace)
873  srcptr++;
874  else
875  *dstptr++ = *srcptr++;
876  }
877  else
878  {
879  *dstptr++ = *srcptr++;
880  leadingspace = false;
881  dstendptr = dstptr;
882  }
883  break;
884  }
885  }
886 
887  Assert(dstptr < srcptr);
888  *dstendptr = '\0';
889 
890  if (i < 0 || i >= nitems)
891  ereport(ERROR,
892  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
893  errmsg("malformed array literal: \"%s\"",
894  origStr)));
895 
896  if (Array_nulls && !hasquoting &&
897  pg_strcasecmp(itemstart, "NULL") == 0)
898  {
899  /* it's a NULL item */
900  values[i] = InputFunctionCall(inputproc, NULL,
901  typioparam, typmod);
902  nulls[i] = true;
903  }
904  else
905  {
906  values[i] = InputFunctionCall(inputproc, itemstart,
907  typioparam, typmod);
908  nulls[i] = false;
909  }
910  }
911 
912  /*
913  * Check for nulls, compute total data space needed
914  */
915  hasnull = false;
916  totbytes = 0;
917  for (i = 0; i < nitems; i++)
918  {
919  if (nulls[i])
920  hasnull = true;
921  else
922  {
923  /* let's just make sure data is not toasted */
924  if (typlen == -1)
925  values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
926  totbytes = att_addlength_datum(totbytes, typlen, values[i]);
927  totbytes = att_align_nominal(totbytes, typalign);
928  /* check for overflow of total request */
929  if (!AllocSizeIsValid(totbytes))
930  ereport(ERROR,
931  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
932  errmsg("array size exceeds the maximum allowed (%d)",
933  (int) MaxAllocSize)));
934  }
935  }
936  *hasnulls = hasnull;
937  *nbytes = totbytes;
938 }
939 
940 
941 /*
942  * Copy data into an array object from a temporary array of Datums.
943  *
944  * array: array object (with header fields already filled in)
945  * values: array of Datums to be copied
946  * nulls: array of is-null flags (can be NULL if no nulls)
947  * nitems: number of Datums to be copied
948  * typbyval, typlen, typalign: info about element datatype
949  * freedata: if true and element type is pass-by-ref, pfree data values
950  * referenced by Datums after copying them.
951  *
952  * If the input data is of varlena type, the caller must have ensured that
953  * the values are not toasted. (Doing it here doesn't work since the
954  * caller has already allocated space for the array...)
955  */
956 void
958  Datum *values,
959  bool *nulls,
960  int nitems,
961  int typlen,
962  bool typbyval,
963  char typalign,
964  bool freedata)
965 {
966  char *p = ARR_DATA_PTR(array);
967  bits8 *bitmap = ARR_NULLBITMAP(array);
968  int bitval = 0;
969  int bitmask = 1;
970  int i;
971 
972  if (typbyval)
973  freedata = false;
974 
975  for (i = 0; i < nitems; i++)
976  {
977  if (nulls && nulls[i])
978  {
979  if (!bitmap) /* shouldn't happen */
980  elog(ERROR, "null array element where not supported");
981  /* bitmap bit stays 0 */
982  }
983  else
984  {
985  bitval |= bitmask;
986  p += ArrayCastAndSet(values[i], typlen, typbyval, typalign, p);
987  if (freedata)
988  pfree(DatumGetPointer(values[i]));
989  }
990  if (bitmap)
991  {
992  bitmask <<= 1;
993  if (bitmask == 0x100)
994  {
995  *bitmap++ = bitval;
996  bitval = 0;
997  bitmask = 1;
998  }
999  }
1000  }
1001 
1002  if (bitmap && bitmask != 1)
1003  *bitmap = bitval;
1004 }
1005 
1006 /*
1007  * array_out :
1008  * takes the internal representation of an array and returns a string
1009  * containing the array in its external format.
1010  */
1011 Datum
1013 {
1015  Oid element_type = AARR_ELEMTYPE(v);
1016  int typlen;
1017  bool typbyval;
1018  char typalign;
1019  char typdelim;
1020  char *p,
1021  *tmp,
1022  *retval,
1023  **values,
1024  dims_str[(MAXDIM * 33) + 2];
1025 
1026  /*
1027  * 33 per dim since we assume 15 digits per number + ':' +'[]'
1028  *
1029  * +2 allows for assignment operator + trailing null
1030  */
1031  bool *needquotes,
1032  needdims = false;
1033  size_t overall_length;
1034  int nitems,
1035  i,
1036  j,
1037  k,
1038  indx[MAXDIM];
1039  int ndim,
1040  *dims,
1041  *lb;
1042  array_iter iter;
1043  ArrayMetaState *my_extra;
1044 
1045  /*
1046  * We arrange to look up info about element type, including its output
1047  * conversion proc, only once per series of calls, assuming the element
1048  * type doesn't change underneath us.
1049  */
1050  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1051  if (my_extra == NULL)
1052  {
1053  fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1054  sizeof(ArrayMetaState));
1055  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1056  my_extra->element_type = ~element_type;
1057  }
1058 
1059  if (my_extra->element_type != element_type)
1060  {
1061  /*
1062  * Get info about element type, including its output conversion proc
1063  */
1064  get_type_io_data(element_type, IOFunc_output,
1065  &my_extra->typlen, &my_extra->typbyval,
1066  &my_extra->typalign, &my_extra->typdelim,
1067  &my_extra->typioparam, &my_extra->typiofunc);
1068  fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1069  fcinfo->flinfo->fn_mcxt);
1070  my_extra->element_type = element_type;
1071  }
1072  typlen = my_extra->typlen;
1073  typbyval = my_extra->typbyval;
1074  typalign = my_extra->typalign;
1075  typdelim = my_extra->typdelim;
1076 
1077  ndim = AARR_NDIM(v);
1078  dims = AARR_DIMS(v);
1079  lb = AARR_LBOUND(v);
1080  nitems = ArrayGetNItems(ndim, dims);
1081 
1082  if (nitems == 0)
1083  {
1084  retval = pstrdup("{}");
1085  PG_RETURN_CSTRING(retval);
1086  }
1087 
1088  /*
1089  * we will need to add explicit dimensions if any dimension has a lower
1090  * bound other than one
1091  */
1092  for (i = 0; i < ndim; i++)
1093  {
1094  if (lb[i] != 1)
1095  {
1096  needdims = true;
1097  break;
1098  }
1099  }
1100 
1101  /*
1102  * Convert all values to string form, count total space needed (including
1103  * any overhead such as escaping backslashes), and detect whether each
1104  * item needs double quotes.
1105  */
1106  values = (char **) palloc(nitems * sizeof(char *));
1107  needquotes = (bool *) palloc(nitems * sizeof(bool));
1108  overall_length = 0;
1109 
1110  array_iter_setup(&iter, v);
1111 
1112  for (i = 0; i < nitems; i++)
1113  {
1114  Datum itemvalue;
1115  bool isnull;
1116  bool needquote;
1117 
1118  /* Get source element, checking for NULL */
1119  itemvalue = array_iter_next(&iter, &isnull, i,
1120  typlen, typbyval, typalign);
1121 
1122  if (isnull)
1123  {
1124  values[i] = pstrdup("NULL");
1125  overall_length += 4;
1126  needquote = false;
1127  }
1128  else
1129  {
1130  values[i] = OutputFunctionCall(&my_extra->proc, itemvalue);
1131 
1132  /* count data plus backslashes; detect chars needing quotes */
1133  if (values[i][0] == '\0')
1134  needquote = true; /* force quotes for empty string */
1135  else if (pg_strcasecmp(values[i], "NULL") == 0)
1136  needquote = true; /* force quotes for literal NULL */
1137  else
1138  needquote = false;
1139 
1140  for (tmp = values[i]; *tmp != '\0'; tmp++)
1141  {
1142  char ch = *tmp;
1143 
1144  overall_length += 1;
1145  if (ch == '"' || ch == '\\')
1146  {
1147  needquote = true;
1148  overall_length += 1;
1149  }
1150  else if (ch == '{' || ch == '}' || ch == typdelim ||
1151  array_isspace(ch))
1152  needquote = true;
1153  }
1154  }
1155 
1156  needquotes[i] = needquote;
1157 
1158  /* Count the pair of double quotes, if needed */
1159  if (needquote)
1160  overall_length += 2;
1161  /* and the comma (or other typdelim delimiter) */
1162  overall_length += 1;
1163  }
1164 
1165  /*
1166  * The very last array element doesn't have a typdelim delimiter after it,
1167  * but that's OK; that space is needed for the trailing '\0'.
1168  *
1169  * Now count total number of curly brace pairs in output string.
1170  */
1171  for (i = j = 0, k = 1; i < ndim; i++)
1172  {
1173  j += k, k *= dims[i];
1174  }
1175  overall_length += 2 * j;
1176 
1177  /* Format explicit dimensions if required */
1178  dims_str[0] = '\0';
1179  if (needdims)
1180  {
1181  char *ptr = dims_str;
1182 
1183  for (i = 0; i < ndim; i++)
1184  {
1185  sprintf(ptr, "[%d:%d]", lb[i], lb[i] + dims[i] - 1);
1186  ptr += strlen(ptr);
1187  }
1188  *ptr++ = *ASSGN;
1189  *ptr = '\0';
1190  overall_length += ptr - dims_str;
1191  }
1192 
1193  /* Now construct the output string */
1194  retval = (char *) palloc(overall_length);
1195  p = retval;
1196 
1197 #define APPENDSTR(str) (strcpy(p, (str)), p += strlen(p))
1198 #define APPENDCHAR(ch) (*p++ = (ch), *p = '\0')
1199 
1200  if (needdims)
1201  APPENDSTR(dims_str);
1202  APPENDCHAR('{');
1203  for (i = 0; i < ndim; i++)
1204  indx[i] = 0;
1205  j = 0;
1206  k = 0;
1207  do
1208  {
1209  for (i = j; i < ndim - 1; i++)
1210  APPENDCHAR('{');
1211 
1212  if (needquotes[k])
1213  {
1214  APPENDCHAR('"');
1215  for (tmp = values[k]; *tmp; tmp++)
1216  {
1217  char ch = *tmp;
1218 
1219  if (ch == '"' || ch == '\\')
1220  *p++ = '\\';
1221  *p++ = ch;
1222  }
1223  *p = '\0';
1224  APPENDCHAR('"');
1225  }
1226  else
1227  APPENDSTR(values[k]);
1228  pfree(values[k++]);
1229 
1230  for (i = ndim - 1; i >= 0; i--)
1231  {
1232  if (++(indx[i]) < dims[i])
1233  {
1234  APPENDCHAR(typdelim);
1235  break;
1236  }
1237  else
1238  {
1239  indx[i] = 0;
1240  APPENDCHAR('}');
1241  }
1242  }
1243  j = i;
1244  } while (j != -1);
1245 
1246 #undef APPENDSTR
1247 #undef APPENDCHAR
1248 
1249  /* Assert that we calculated the string length accurately */
1250  Assert(overall_length == (p - retval + 1));
1251 
1252  pfree(values);
1253  pfree(needquotes);
1254 
1255  PG_RETURN_CSTRING(retval);
1256 }
1257 
1258 /*
1259  * array_recv :
1260  * converts an array from the external binary format to
1261  * its internal format.
1262  *
1263  * return value :
1264  * the internal representation of the input array
1265  */
1266 Datum
1268 {
1270  Oid spec_element_type = PG_GETARG_OID(1); /* type of an array
1271  * element */
1272  int32 typmod = PG_GETARG_INT32(2); /* typmod for array elements */
1273  Oid element_type;
1274  int typlen;
1275  bool typbyval;
1276  char typalign;
1277  Oid typioparam;
1278  int i,
1279  nitems;
1280  Datum *dataPtr;
1281  bool *nullsPtr;
1282  bool hasnulls;
1283  int32 nbytes;
1284  int32 dataoffset;
1285  ArrayType *retval;
1286  int ndim,
1287  flags,
1288  dim[MAXDIM],
1289  lBound[MAXDIM];
1290  ArrayMetaState *my_extra;
1291 
1292  /* Get the array header information */
1293  ndim = pq_getmsgint(buf, 4);
1294  if (ndim < 0) /* we do allow zero-dimension arrays */
1295  ereport(ERROR,
1296  (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1297  errmsg("invalid number of dimensions: %d", ndim)));
1298  if (ndim > MAXDIM)
1299  ereport(ERROR,
1300  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1301  errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
1302  ndim, MAXDIM)));
1303 
1304  flags = pq_getmsgint(buf, 4);
1305  if (flags != 0 && flags != 1)
1306  ereport(ERROR,
1307  (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1308  errmsg("invalid array flags")));
1309 
1310  element_type = pq_getmsgint(buf, sizeof(Oid));
1311  if (element_type != spec_element_type)
1312  {
1313  /* XXX Can we allow taking the input element type in any cases? */
1314  ereport(ERROR,
1315  (errcode(ERRCODE_DATATYPE_MISMATCH),
1316  errmsg("wrong element type")));
1317  }
1318 
1319  for (i = 0; i < ndim; i++)
1320  {
1321  dim[i] = pq_getmsgint(buf, 4);
1322  lBound[i] = pq_getmsgint(buf, 4);
1323 
1324  /*
1325  * Check overflow of upper bound. (ArrayGetNItems() below checks that
1326  * dim[i] >= 0)
1327  */
1328  if (dim[i] != 0)
1329  {
1330  int ub = lBound[i] + dim[i] - 1;
1331 
1332  if (lBound[i] > ub)
1333  ereport(ERROR,
1334  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
1335  errmsg("integer out of range")));
1336  }
1337  }
1338 
1339  /* This checks for overflow of array dimensions */
1340  nitems = ArrayGetNItems(ndim, dim);
1341 
1342  /*
1343  * We arrange to look up info about element type, including its receive
1344  * conversion proc, only once per series of calls, assuming the element
1345  * type doesn't change underneath us.
1346  */
1347  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1348  if (my_extra == NULL)
1349  {
1350  fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1351  sizeof(ArrayMetaState));
1352  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1353  my_extra->element_type = ~element_type;
1354  }
1355 
1356  if (my_extra->element_type != element_type)
1357  {
1358  /* Get info about element type, including its receive proc */
1359  get_type_io_data(element_type, IOFunc_receive,
1360  &my_extra->typlen, &my_extra->typbyval,
1361  &my_extra->typalign, &my_extra->typdelim,
1362  &my_extra->typioparam, &my_extra->typiofunc);
1363  if (!OidIsValid(my_extra->typiofunc))
1364  ereport(ERROR,
1365  (errcode(ERRCODE_UNDEFINED_FUNCTION),
1366  errmsg("no binary input function available for type %s",
1367  format_type_be(element_type))));
1368  fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1369  fcinfo->flinfo->fn_mcxt);
1370  my_extra->element_type = element_type;
1371  }
1372 
1373  if (nitems == 0)
1374  {
1375  /* Return empty array ... but not till we've validated element_type */
1377  }
1378 
1379  typlen = my_extra->typlen;
1380  typbyval = my_extra->typbyval;
1381  typalign = my_extra->typalign;
1382  typioparam = my_extra->typioparam;
1383 
1384  dataPtr = (Datum *) palloc(nitems * sizeof(Datum));
1385  nullsPtr = (bool *) palloc(nitems * sizeof(bool));
1386  ReadArrayBinary(buf, nitems,
1387  &my_extra->proc, typioparam, typmod,
1388  typlen, typbyval, typalign,
1389  dataPtr, nullsPtr,
1390  &hasnulls, &nbytes);
1391  if (hasnulls)
1392  {
1393  dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
1394  nbytes += dataoffset;
1395  }
1396  else
1397  {
1398  dataoffset = 0; /* marker for no null bitmap */
1399  nbytes += ARR_OVERHEAD_NONULLS(ndim);
1400  }
1401  retval = (ArrayType *) palloc0(nbytes);
1402  SET_VARSIZE(retval, nbytes);
1403  retval->ndim = ndim;
1404  retval->dataoffset = dataoffset;
1405  retval->elemtype = element_type;
1406  memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
1407  memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
1408 
1409  CopyArrayEls(retval,
1410  dataPtr, nullsPtr, nitems,
1411  typlen, typbyval, typalign,
1412  true);
1413 
1414  pfree(dataPtr);
1415  pfree(nullsPtr);
1416 
1417  PG_RETURN_ARRAYTYPE_P(retval);
1418 }
1419 
1420 /*
1421  * ReadArrayBinary:
1422  * collect the data elements of an array being read in binary style.
1423  *
1424  * Inputs:
1425  * buf: the data buffer to read from.
1426  * nitems: total number of array elements (already read).
1427  * receiveproc: type-specific receive procedure for element datatype.
1428  * typioparam, typmod: auxiliary values to pass to receiveproc.
1429  * typlen, typbyval, typalign: storage parameters of element datatype.
1430  *
1431  * Outputs:
1432  * values[]: filled with converted data values.
1433  * nulls[]: filled with is-null markers.
1434  * *hasnulls: set true iff there are any null elements.
1435  * *nbytes: set to total size of data area needed (including alignment
1436  * padding but not including array header overhead).
1437  *
1438  * Note that values[] and nulls[] are allocated by the caller, and must have
1439  * nitems elements.
1440  */
1441 static void
1443  int nitems,
1444  FmgrInfo *receiveproc,
1445  Oid typioparam,
1446  int32 typmod,
1447  int typlen,
1448  bool typbyval,
1449  char typalign,
1450  Datum *values,
1451  bool *nulls,
1452  bool *hasnulls,
1453  int32 *nbytes)
1454 {
1455  int i;
1456  bool hasnull;
1457  int32 totbytes;
1458 
1459  for (i = 0; i < nitems; i++)
1460  {
1461  int itemlen;
1462  StringInfoData elem_buf;
1463  char csave;
1464 
1465  /* Get and check the item length */
1466  itemlen = pq_getmsgint(buf, 4);
1467  if (itemlen < -1 || itemlen > (buf->len - buf->cursor))
1468  ereport(ERROR,
1469  (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1470  errmsg("insufficient data left in message")));
1471 
1472  if (itemlen == -1)
1473  {
1474  /* -1 length means NULL */
1475  values[i] = ReceiveFunctionCall(receiveproc, NULL,
1476  typioparam, typmod);
1477  nulls[i] = true;
1478  continue;
1479  }
1480 
1481  /*
1482  * Rather than copying data around, we just set up a phony StringInfo
1483  * pointing to the correct portion of the input buffer. We assume we
1484  * can scribble on the input buffer so as to maintain the convention
1485  * that StringInfos have a trailing null.
1486  */
1487  elem_buf.data = &buf->data[buf->cursor];
1488  elem_buf.maxlen = itemlen + 1;
1489  elem_buf.len = itemlen;
1490  elem_buf.cursor = 0;
1491 
1492  buf->cursor += itemlen;
1493 
1494  csave = buf->data[buf->cursor];
1495  buf->data[buf->cursor] = '\0';
1496 
1497  /* Now call the element's receiveproc */
1498  values[i] = ReceiveFunctionCall(receiveproc, &elem_buf,
1499  typioparam, typmod);
1500  nulls[i] = false;
1501 
1502  /* Trouble if it didn't eat the whole buffer */
1503  if (elem_buf.cursor != itemlen)
1504  ereport(ERROR,
1505  (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1506  errmsg("improper binary format in array element %d",
1507  i + 1)));
1508 
1509  buf->data[buf->cursor] = csave;
1510  }
1511 
1512  /*
1513  * Check for nulls, compute total data space needed
1514  */
1515  hasnull = false;
1516  totbytes = 0;
1517  for (i = 0; i < nitems; i++)
1518  {
1519  if (nulls[i])
1520  hasnull = true;
1521  else
1522  {
1523  /* let's just make sure data is not toasted */
1524  if (typlen == -1)
1525  values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
1526  totbytes = att_addlength_datum(totbytes, typlen, values[i]);
1527  totbytes = att_align_nominal(totbytes, typalign);
1528  /* check for overflow of total request */
1529  if (!AllocSizeIsValid(totbytes))
1530  ereport(ERROR,
1531  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1532  errmsg("array size exceeds the maximum allowed (%d)",
1533  (int) MaxAllocSize)));
1534  }
1535  }
1536  *hasnulls = hasnull;
1537  *nbytes = totbytes;
1538 }
1539 
1540 
1541 /*
1542  * array_send :
1543  * takes the internal representation of an array and returns a bytea
1544  * containing the array in its external binary format.
1545  */
1546 Datum
1548 {
1550  Oid element_type = AARR_ELEMTYPE(v);
1551  int typlen;
1552  bool typbyval;
1553  char typalign;
1554  int nitems,
1555  i;
1556  int ndim,
1557  *dim,
1558  *lb;
1560  array_iter iter;
1561  ArrayMetaState *my_extra;
1562 
1563  /*
1564  * We arrange to look up info about element type, including its send
1565  * conversion proc, only once per series of calls, assuming the element
1566  * type doesn't change underneath us.
1567  */
1568  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1569  if (my_extra == NULL)
1570  {
1571  fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1572  sizeof(ArrayMetaState));
1573  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1574  my_extra->element_type = ~element_type;
1575  }
1576 
1577  if (my_extra->element_type != element_type)
1578  {
1579  /* Get info about element type, including its send proc */
1580  get_type_io_data(element_type, IOFunc_send,
1581  &my_extra->typlen, &my_extra->typbyval,
1582  &my_extra->typalign, &my_extra->typdelim,
1583  &my_extra->typioparam, &my_extra->typiofunc);
1584  if (!OidIsValid(my_extra->typiofunc))
1585  ereport(ERROR,
1586  (errcode(ERRCODE_UNDEFINED_FUNCTION),
1587  errmsg("no binary output function available for type %s",
1588  format_type_be(element_type))));
1589  fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1590  fcinfo->flinfo->fn_mcxt);
1591  my_extra->element_type = element_type;
1592  }
1593  typlen = my_extra->typlen;
1594  typbyval = my_extra->typbyval;
1595  typalign = my_extra->typalign;
1596 
1597  ndim = AARR_NDIM(v);
1598  dim = AARR_DIMS(v);
1599  lb = AARR_LBOUND(v);
1600  nitems = ArrayGetNItems(ndim, dim);
1601 
1602  pq_begintypsend(&buf);
1603 
1604  /* Send the array header information */
1605  pq_sendint32(&buf, ndim);
1606  pq_sendint32(&buf, AARR_HASNULL(v) ? 1 : 0);
1607  pq_sendint32(&buf, element_type);
1608  for (i = 0; i < ndim; i++)
1609  {
1610  pq_sendint32(&buf, dim[i]);
1611  pq_sendint32(&buf, lb[i]);
1612  }
1613 
1614  /* Send the array elements using the element's own sendproc */
1615  array_iter_setup(&iter, v);
1616 
1617  for (i = 0; i < nitems; i++)
1618  {
1619  Datum itemvalue;
1620  bool isnull;
1621 
1622  /* Get source element, checking for NULL */
1623  itemvalue = array_iter_next(&iter, &isnull, i,
1624  typlen, typbyval, typalign);
1625 
1626  if (isnull)
1627  {
1628  /* -1 length means a NULL */
1629  pq_sendint32(&buf, -1);
1630  }
1631  else
1632  {
1633  bytea *outputbytes;
1634 
1635  outputbytes = SendFunctionCall(&my_extra->proc, itemvalue);
1636  pq_sendint32(&buf, VARSIZE(outputbytes) - VARHDRSZ);
1637  pq_sendbytes(&buf, VARDATA(outputbytes),
1638  VARSIZE(outputbytes) - VARHDRSZ);
1639  pfree(outputbytes);
1640  }
1641  }
1642 
1644 }
1645 
1646 /*
1647  * array_ndims :
1648  * returns the number of dimensions of the array pointed to by "v"
1649  */
1650 Datum
1652 {
1654 
1655  /* Sanity check: does it look like an array at all? */
1656  if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
1657  PG_RETURN_NULL();
1658 
1660 }
1661 
1662 /*
1663  * array_dims :
1664  * returns the dimensions of the array pointed to by "v", as a "text"
1665  */
1666 Datum
1668 {
1670  char *p;
1671  int i;
1672  int *dimv,
1673  *lb;
1674 
1675  /*
1676  * 33 since we assume 15 digits per number + ':' +'[]'
1677  *
1678  * +1 for trailing null
1679  */
1680  char buf[MAXDIM * 33 + 1];
1681 
1682  /* Sanity check: does it look like an array at all? */
1683  if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
1684  PG_RETURN_NULL();
1685 
1686  dimv = AARR_DIMS(v);
1687  lb = AARR_LBOUND(v);
1688 
1689  p = buf;
1690  for (i = 0; i < AARR_NDIM(v); i++)
1691  {
1692  sprintf(p, "[%d:%d]", lb[i], dimv[i] + lb[i] - 1);
1693  p += strlen(p);
1694  }
1695 
1697 }
1698 
1699 /*
1700  * array_lower :
1701  * returns the lower dimension, of the DIM requested, for
1702  * the array pointed to by "v", as an int4
1703  */
1704 Datum
1706 {
1708  int reqdim = PG_GETARG_INT32(1);
1709  int *lb;
1710  int result;
1711 
1712  /* Sanity check: does it look like an array at all? */
1713  if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
1714  PG_RETURN_NULL();
1715 
1716  /* Sanity check: was the requested dim valid */
1717  if (reqdim <= 0 || reqdim > AARR_NDIM(v))
1718  PG_RETURN_NULL();
1719 
1720  lb = AARR_LBOUND(v);
1721  result = lb[reqdim - 1];
1722 
1723  PG_RETURN_INT32(result);
1724 }
1725 
1726 /*
1727  * array_upper :
1728  * returns the upper dimension, of the DIM requested, for
1729  * the array pointed to by "v", as an int4
1730  */
1731 Datum
1733 {
1735  int reqdim = PG_GETARG_INT32(1);
1736  int *dimv,
1737  *lb;
1738  int result;
1739 
1740  /* Sanity check: does it look like an array at all? */
1741  if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
1742  PG_RETURN_NULL();
1743 
1744  /* Sanity check: was the requested dim valid */
1745  if (reqdim <= 0 || reqdim > AARR_NDIM(v))
1746  PG_RETURN_NULL();
1747 
1748  lb = AARR_LBOUND(v);
1749  dimv = AARR_DIMS(v);
1750 
1751  result = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
1752 
1753  PG_RETURN_INT32(result);
1754 }
1755 
1756 /*
1757  * array_length :
1758  * returns the length, of the dimension requested, for
1759  * the array pointed to by "v", as an int4
1760  */
1761 Datum
1763 {
1765  int reqdim = PG_GETARG_INT32(1);
1766  int *dimv;
1767  int result;
1768 
1769  /* Sanity check: does it look like an array at all? */
1770  if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
1771  PG_RETURN_NULL();
1772 
1773  /* Sanity check: was the requested dim valid */
1774  if (reqdim <= 0 || reqdim > AARR_NDIM(v))
1775  PG_RETURN_NULL();
1776 
1777  dimv = AARR_DIMS(v);
1778 
1779  result = dimv[reqdim - 1];
1780 
1781  PG_RETURN_INT32(result);
1782 }
1783 
1784 /*
1785  * array_cardinality:
1786  * returns the total number of elements in an array
1787  */
1788 Datum
1790 {
1792 
1794 }
1795 
1796 
1797 /*
1798  * array_get_element :
1799  * This routine takes an array datum and a subscript array and returns
1800  * the referenced item as a Datum. Note that for a pass-by-reference
1801  * datatype, the returned Datum is a pointer into the array object.
1802  *
1803  * This handles both ordinary varlena arrays and fixed-length arrays.
1804  *
1805  * Inputs:
1806  * arraydatum: the array object (mustn't be NULL)
1807  * nSubscripts: number of subscripts supplied
1808  * indx[]: the subscript values
1809  * arraytyplen: pg_type.typlen for the array type
1810  * elmlen: pg_type.typlen for the array's element type
1811  * elmbyval: pg_type.typbyval for the array's element type
1812  * elmalign: pg_type.typalign for the array's element type
1813  *
1814  * Outputs:
1815  * The return value is the element Datum.
1816  * *isNull is set to indicate whether the element is NULL.
1817  */
1818 Datum
1820  int nSubscripts,
1821  int *indx,
1822  int arraytyplen,
1823  int elmlen,
1824  bool elmbyval,
1825  char elmalign,
1826  bool *isNull)
1827 {
1828  int i,
1829  ndim,
1830  *dim,
1831  *lb,
1832  offset,
1833  fixedDim[1],
1834  fixedLb[1];
1835  char *arraydataptr,
1836  *retptr;
1837  bits8 *arraynullsptr;
1838 
1839  if (arraytyplen > 0)
1840  {
1841  /*
1842  * fixed-length arrays -- these are assumed to be 1-d, 0-based
1843  */
1844  ndim = 1;
1845  fixedDim[0] = arraytyplen / elmlen;
1846  fixedLb[0] = 0;
1847  dim = fixedDim;
1848  lb = fixedLb;
1849  arraydataptr = (char *) DatumGetPointer(arraydatum);
1850  arraynullsptr = NULL;
1851  }
1852  else if (VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(arraydatum)))
1853  {
1854  /* expanded array: let's do this in a separate function */
1855  return array_get_element_expanded(arraydatum,
1856  nSubscripts,
1857  indx,
1858  arraytyplen,
1859  elmlen,
1860  elmbyval,
1861  elmalign,
1862  isNull);
1863  }
1864  else
1865  {
1866  /* detoast array if necessary, producing normal varlena input */
1867  ArrayType *array = DatumGetArrayTypeP(arraydatum);
1868 
1869  ndim = ARR_NDIM(array);
1870  dim = ARR_DIMS(array);
1871  lb = ARR_LBOUND(array);
1872  arraydataptr = ARR_DATA_PTR(array);
1873  arraynullsptr = ARR_NULLBITMAP(array);
1874  }
1875 
1876  /*
1877  * Return NULL for invalid subscript
1878  */
1879  if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
1880  {
1881  *isNull = true;
1882  return (Datum) 0;
1883  }
1884  for (i = 0; i < ndim; i++)
1885  {
1886  if (indx[i] < lb[i] || indx[i] >= (dim[i] + lb[i]))
1887  {
1888  *isNull = true;
1889  return (Datum) 0;
1890  }
1891  }
1892 
1893  /*
1894  * Calculate the element number
1895  */
1896  offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
1897 
1898  /*
1899  * Check for NULL array element
1900  */
1901  if (array_get_isnull(arraynullsptr, offset))
1902  {
1903  *isNull = true;
1904  return (Datum) 0;
1905  }
1906 
1907  /*
1908  * OK, get the element
1909  */
1910  *isNull = false;
1911  retptr = array_seek(arraydataptr, 0, arraynullsptr, offset,
1912  elmlen, elmbyval, elmalign);
1913  return ArrayCast(retptr, elmbyval, elmlen);
1914 }
1915 
1916 /*
1917  * Implementation of array_get_element() for an expanded array
1918  */
1919 static Datum
1921  int nSubscripts, int *indx,
1922  int arraytyplen,
1923  int elmlen, bool elmbyval, char elmalign,
1924  bool *isNull)
1925 {
1926  ExpandedArrayHeader *eah;
1927  int i,
1928  ndim,
1929  *dim,
1930  *lb,
1931  offset;
1932  Datum *dvalues;
1933  bool *dnulls;
1934 
1935  eah = (ExpandedArrayHeader *) DatumGetEOHP(arraydatum);
1936  Assert(eah->ea_magic == EA_MAGIC);
1937 
1938  /* sanity-check caller's info against object */
1939  Assert(arraytyplen == -1);
1940  Assert(elmlen == eah->typlen);
1941  Assert(elmbyval == eah->typbyval);
1942  Assert(elmalign == eah->typalign);
1943 
1944  ndim = eah->ndims;
1945  dim = eah->dims;
1946  lb = eah->lbound;
1947 
1948  /*
1949  * Return NULL for invalid subscript
1950  */
1951  if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
1952  {
1953  *isNull = true;
1954  return (Datum) 0;
1955  }
1956  for (i = 0; i < ndim; i++)
1957  {
1958  if (indx[i] < lb[i] || indx[i] >= (dim[i] + lb[i]))
1959  {
1960  *isNull = true;
1961  return (Datum) 0;
1962  }
1963  }
1964 
1965  /*
1966  * Calculate the element number
1967  */
1968  offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
1969 
1970  /*
1971  * Deconstruct array if we didn't already. Note that we apply this even
1972  * if the input is nominally read-only: it should be safe enough.
1973  */
1975 
1976  dvalues = eah->dvalues;
1977  dnulls = eah->dnulls;
1978 
1979  /*
1980  * Check for NULL array element
1981  */
1982  if (dnulls && dnulls[offset])
1983  {
1984  *isNull = true;
1985  return (Datum) 0;
1986  }
1987 
1988  /*
1989  * OK, get the element. It's OK to return a pass-by-ref value as a
1990  * pointer into the expanded array, for the same reason that regular
1991  * array_get_element can return a pointer into flat arrays: the value is
1992  * assumed not to change for as long as the Datum reference can exist.
1993  */
1994  *isNull = false;
1995  return dvalues[offset];
1996 }
1997 
1998 /*
1999  * array_get_slice :
2000  * This routine takes an array and a range of indices (upperIndx and
2001  * lowerIndx), creates a new array structure for the referred elements
2002  * and returns a pointer to it.
2003  *
2004  * This handles both ordinary varlena arrays and fixed-length arrays.
2005  *
2006  * Inputs:
2007  * arraydatum: the array object (mustn't be NULL)
2008  * nSubscripts: number of subscripts supplied (must be same for upper/lower)
2009  * upperIndx[]: the upper subscript values
2010  * lowerIndx[]: the lower subscript values
2011  * upperProvided[]: true for provided upper subscript values
2012  * lowerProvided[]: true for provided lower subscript values
2013  * arraytyplen: pg_type.typlen for the array type
2014  * elmlen: pg_type.typlen for the array's element type
2015  * elmbyval: pg_type.typbyval for the array's element type
2016  * elmalign: pg_type.typalign for the array's element type
2017  *
2018  * Outputs:
2019  * The return value is the new array Datum (it's never NULL)
2020  *
2021  * Omitted upper and lower subscript values are replaced by the corresponding
2022  * array bound.
2023  *
2024  * NOTE: we assume it is OK to scribble on the provided subscript arrays
2025  * lowerIndx[] and upperIndx[]. These are generally just temporaries.
2026  */
2027 Datum
2029  int nSubscripts,
2030  int *upperIndx,
2031  int *lowerIndx,
2032  bool *upperProvided,
2033  bool *lowerProvided,
2034  int arraytyplen,
2035  int elmlen,
2036  bool elmbyval,
2037  char elmalign)
2038 {
2039  ArrayType *array;
2040  ArrayType *newarray;
2041  int i,
2042  ndim,
2043  *dim,
2044  *lb,
2045  *newlb;
2046  int fixedDim[1],
2047  fixedLb[1];
2048  Oid elemtype;
2049  char *arraydataptr;
2050  bits8 *arraynullsptr;
2051  int32 dataoffset;
2052  int bytes,
2053  span[MAXDIM];
2054 
2055  if (arraytyplen > 0)
2056  {
2057  /*
2058  * fixed-length arrays -- currently, cannot slice these because parser
2059  * labels output as being of the fixed-length array type! Code below
2060  * shows how we could support it if the parser were changed to label
2061  * output as a suitable varlena array type.
2062  */
2063  ereport(ERROR,
2064  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2065  errmsg("slices of fixed-length arrays not implemented")));
2066 
2067  /*
2068  * fixed-length arrays -- these are assumed to be 1-d, 0-based
2069  *
2070  * XXX where would we get the correct ELEMTYPE from?
2071  */
2072  ndim = 1;
2073  fixedDim[0] = arraytyplen / elmlen;
2074  fixedLb[0] = 0;
2075  dim = fixedDim;
2076  lb = fixedLb;
2077  elemtype = InvalidOid; /* XXX */
2078  arraydataptr = (char *) DatumGetPointer(arraydatum);
2079  arraynullsptr = NULL;
2080  }
2081  else
2082  {
2083  /* detoast input array if necessary */
2084  array = DatumGetArrayTypeP(arraydatum);
2085 
2086  ndim = ARR_NDIM(array);
2087  dim = ARR_DIMS(array);
2088  lb = ARR_LBOUND(array);
2089  elemtype = ARR_ELEMTYPE(array);
2090  arraydataptr = ARR_DATA_PTR(array);
2091  arraynullsptr = ARR_NULLBITMAP(array);
2092  }
2093 
2094  /*
2095  * Check provided subscripts. A slice exceeding the current array limits
2096  * is silently truncated to the array limits. If we end up with an empty
2097  * slice, return an empty array.
2098  */
2099  if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
2100  return PointerGetDatum(construct_empty_array(elemtype));
2101 
2102  for (i = 0; i < nSubscripts; i++)
2103  {
2104  if (!lowerProvided[i] || lowerIndx[i] < lb[i])
2105  lowerIndx[i] = lb[i];
2106  if (!upperProvided[i] || upperIndx[i] >= (dim[i] + lb[i]))
2107  upperIndx[i] = dim[i] + lb[i] - 1;
2108  if (lowerIndx[i] > upperIndx[i])
2109  return PointerGetDatum(construct_empty_array(elemtype));
2110  }
2111  /* fill any missing subscript positions with full array range */
2112  for (; i < ndim; i++)
2113  {
2114  lowerIndx[i] = lb[i];
2115  upperIndx[i] = dim[i] + lb[i] - 1;
2116  if (lowerIndx[i] > upperIndx[i])
2117  return PointerGetDatum(construct_empty_array(elemtype));
2118  }
2119 
2120  mda_get_range(ndim, span, lowerIndx, upperIndx);
2121 
2122  bytes = array_slice_size(arraydataptr, arraynullsptr,
2123  ndim, dim, lb,
2124  lowerIndx, upperIndx,
2125  elmlen, elmbyval, elmalign);
2126 
2127  /*
2128  * Currently, we put a null bitmap in the result if the source has one;
2129  * could be smarter ...
2130  */
2131  if (arraynullsptr)
2132  {
2133  dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, ArrayGetNItems(ndim, span));
2134  bytes += dataoffset;
2135  }
2136  else
2137  {
2138  dataoffset = 0; /* marker for no null bitmap */
2139  bytes += ARR_OVERHEAD_NONULLS(ndim);
2140  }
2141 
2142  newarray = (ArrayType *) palloc0(bytes);
2143  SET_VARSIZE(newarray, bytes);
2144  newarray->ndim = ndim;
2145  newarray->dataoffset = dataoffset;
2146  newarray->elemtype = elemtype;
2147  memcpy(ARR_DIMS(newarray), span, ndim * sizeof(int));
2148 
2149  /*
2150  * Lower bounds of the new array are set to 1. Formerly (before 7.3) we
2151  * copied the given lowerIndx values ... but that seems confusing.
2152  */
2153  newlb = ARR_LBOUND(newarray);
2154  for (i = 0; i < ndim; i++)
2155  newlb[i] = 1;
2156 
2157  array_extract_slice(newarray,
2158  ndim, dim, lb,
2159  arraydataptr, arraynullsptr,
2160  lowerIndx, upperIndx,
2161  elmlen, elmbyval, elmalign);
2162 
2163  return PointerGetDatum(newarray);
2164 }
2165 
2166 /*
2167  * array_set_element :
2168  * This routine sets the value of one array element (specified by
2169  * a subscript array) to a new value specified by "dataValue".
2170  *
2171  * This handles both ordinary varlena arrays and fixed-length arrays.
2172  *
2173  * Inputs:
2174  * arraydatum: the initial array object (mustn't be NULL)
2175  * nSubscripts: number of subscripts supplied
2176  * indx[]: the subscript values
2177  * dataValue: the datum to be inserted at the given position
2178  * isNull: whether dataValue is NULL
2179  * arraytyplen: pg_type.typlen for the array type
2180  * elmlen: pg_type.typlen for the array's element type
2181  * elmbyval: pg_type.typbyval for the array's element type
2182  * elmalign: pg_type.typalign for the array's element type
2183  *
2184  * Result:
2185  * A new array is returned, just like the old except for the one
2186  * modified entry. The original array object is not changed,
2187  * unless what is passed is a read-write reference to an expanded
2188  * array object; in that case the expanded array is updated in-place.
2189  *
2190  * For one-dimensional arrays only, we allow the array to be extended
2191  * by assigning to a position outside the existing subscript range; any
2192  * positions between the existing elements and the new one are set to NULLs.
2193  * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
2194  *
2195  * NOTE: For assignments, we throw an error for invalid subscripts etc,
2196  * rather than returning a NULL as the fetch operations do.
2197  */
2198 Datum
2200  int nSubscripts,
2201  int *indx,
2202  Datum dataValue,
2203  bool isNull,
2204  int arraytyplen,
2205  int elmlen,
2206  bool elmbyval,
2207  char elmalign)
2208 {
2209  ArrayType *array;
2210  ArrayType *newarray;
2211  int i,
2212  ndim,
2213  dim[MAXDIM],
2214  lb[MAXDIM],
2215  offset;
2216  char *elt_ptr;
2217  bool newhasnulls;
2218  bits8 *oldnullbitmap;
2219  int oldnitems,
2220  newnitems,
2221  olddatasize,
2222  newsize,
2223  olditemlen,
2224  newitemlen,
2225  overheadlen,
2226  oldoverheadlen,
2227  addedbefore,
2228  addedafter,
2229  lenbefore,
2230  lenafter;
2231 
2232  if (arraytyplen > 0)
2233  {
2234  /*
2235  * fixed-length arrays -- these are assumed to be 1-d, 0-based. We
2236  * cannot extend them, either.
2237  */
2238  char *resultarray;
2239 
2240  if (nSubscripts != 1)
2241  ereport(ERROR,
2242  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2243  errmsg("wrong number of array subscripts")));
2244 
2245  if (indx[0] < 0 || indx[0] * elmlen >= arraytyplen)
2246  ereport(ERROR,
2247  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2248  errmsg("array subscript out of range")));
2249 
2250  if (isNull)
2251  ereport(ERROR,
2252  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
2253  errmsg("cannot assign null value to an element of a fixed-length array")));
2254 
2255  resultarray = (char *) palloc(arraytyplen);
2256  memcpy(resultarray, DatumGetPointer(arraydatum), arraytyplen);
2257  elt_ptr = (char *) resultarray + indx[0] * elmlen;
2258  ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign, elt_ptr);
2259  return PointerGetDatum(resultarray);
2260  }
2261 
2262  if (nSubscripts <= 0 || nSubscripts > MAXDIM)
2263  ereport(ERROR,
2264  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2265  errmsg("wrong number of array subscripts")));
2266 
2267  /* make sure item to be inserted is not toasted */
2268  if (elmlen == -1 && !isNull)
2269  dataValue = PointerGetDatum(PG_DETOAST_DATUM(dataValue));
2270 
2272  {
2273  /* expanded array: let's do this in a separate function */
2274  return array_set_element_expanded(arraydatum,
2275  nSubscripts,
2276  indx,
2277  dataValue,
2278  isNull,
2279  arraytyplen,
2280  elmlen,
2281  elmbyval,
2282  elmalign);
2283  }
2284 
2285  /* detoast input array if necessary */
2286  array = DatumGetArrayTypeP(arraydatum);
2287 
2288  ndim = ARR_NDIM(array);
2289 
2290  /*
2291  * if number of dims is zero, i.e. an empty array, create an array with
2292  * nSubscripts dimensions, and set the lower bounds to the supplied
2293  * subscripts
2294  */
2295  if (ndim == 0)
2296  {
2297  Oid elmtype = ARR_ELEMTYPE(array);
2298 
2299  for (i = 0; i < nSubscripts; i++)
2300  {
2301  dim[i] = 1;
2302  lb[i] = indx[i];
2303  }
2304 
2305  return PointerGetDatum(construct_md_array(&dataValue, &isNull,
2306  nSubscripts, dim, lb,
2307  elmtype,
2308  elmlen, elmbyval, elmalign));
2309  }
2310 
2311  if (ndim != nSubscripts)
2312  ereport(ERROR,
2313  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2314  errmsg("wrong number of array subscripts")));
2315 
2316  /* copy dim/lb since we may modify them */
2317  memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
2318  memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
2319 
2320  newhasnulls = (ARR_HASNULL(array) || isNull);
2321  addedbefore = addedafter = 0;
2322 
2323  /*
2324  * Check subscripts
2325  */
2326  if (ndim == 1)
2327  {
2328  if (indx[0] < lb[0])
2329  {
2330  addedbefore = lb[0] - indx[0];
2331  dim[0] += addedbefore;
2332  lb[0] = indx[0];
2333  if (addedbefore > 1)
2334  newhasnulls = true; /* will insert nulls */
2335  }
2336  if (indx[0] >= (dim[0] + lb[0]))
2337  {
2338  addedafter = indx[0] - (dim[0] + lb[0]) + 1;
2339  dim[0] += addedafter;
2340  if (addedafter > 1)
2341  newhasnulls = true; /* will insert nulls */
2342  }
2343  }
2344  else
2345  {
2346  /*
2347  * XXX currently we do not support extending multi-dimensional arrays
2348  * during assignment
2349  */
2350  for (i = 0; i < ndim; i++)
2351  {
2352  if (indx[i] < lb[i] ||
2353  indx[i] >= (dim[i] + lb[i]))
2354  ereport(ERROR,
2355  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2356  errmsg("array subscript out of range")));
2357  }
2358  }
2359 
2360  /*
2361  * Compute sizes of items and areas to copy
2362  */
2363  newnitems = ArrayGetNItems(ndim, dim);
2364  if (newhasnulls)
2365  overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, newnitems);
2366  else
2367  overheadlen = ARR_OVERHEAD_NONULLS(ndim);
2368  oldnitems = ArrayGetNItems(ndim, ARR_DIMS(array));
2369  oldnullbitmap = ARR_NULLBITMAP(array);
2370  oldoverheadlen = ARR_DATA_OFFSET(array);
2371  olddatasize = ARR_SIZE(array) - oldoverheadlen;
2372  if (addedbefore)
2373  {
2374  offset = 0;
2375  lenbefore = 0;
2376  olditemlen = 0;
2377  lenafter = olddatasize;
2378  }
2379  else if (addedafter)
2380  {
2381  offset = oldnitems;
2382  lenbefore = olddatasize;
2383  olditemlen = 0;
2384  lenafter = 0;
2385  }
2386  else
2387  {
2388  offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
2389  elt_ptr = array_seek(ARR_DATA_PTR(array), 0, oldnullbitmap, offset,
2390  elmlen, elmbyval, elmalign);
2391  lenbefore = (int) (elt_ptr - ARR_DATA_PTR(array));
2392  if (array_get_isnull(oldnullbitmap, offset))
2393  olditemlen = 0;
2394  else
2395  {
2396  olditemlen = att_addlength_pointer(0, elmlen, elt_ptr);
2397  olditemlen = att_align_nominal(olditemlen, elmalign);
2398  }
2399  lenafter = (int) (olddatasize - lenbefore - olditemlen);
2400  }
2401 
2402  if (isNull)
2403  newitemlen = 0;
2404  else
2405  {
2406  newitemlen = att_addlength_datum(0, elmlen, dataValue);
2407  newitemlen = att_align_nominal(newitemlen, elmalign);
2408  }
2409 
2410  newsize = overheadlen + lenbefore + newitemlen + lenafter;
2411 
2412  /*
2413  * OK, create the new array and fill in header/dimensions
2414  */
2415  newarray = (ArrayType *) palloc0(newsize);
2416  SET_VARSIZE(newarray, newsize);
2417  newarray->ndim = ndim;
2418  newarray->dataoffset = newhasnulls ? overheadlen : 0;
2419  newarray->elemtype = ARR_ELEMTYPE(array);
2420  memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
2421  memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
2422 
2423  /*
2424  * Fill in data
2425  */
2426  memcpy((char *) newarray + overheadlen,
2427  (char *) array + oldoverheadlen,
2428  lenbefore);
2429  if (!isNull)
2430  ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign,
2431  (char *) newarray + overheadlen + lenbefore);
2432  memcpy((char *) newarray + overheadlen + lenbefore + newitemlen,
2433  (char *) array + oldoverheadlen + lenbefore + olditemlen,
2434  lenafter);
2435 
2436  /*
2437  * Fill in nulls bitmap if needed
2438  *
2439  * Note: it's possible we just replaced the last NULL with a non-NULL, and
2440  * could get rid of the bitmap. Seems not worth testing for though.
2441  */
2442  if (newhasnulls)
2443  {
2444  bits8 *newnullbitmap = ARR_NULLBITMAP(newarray);
2445 
2446  /* Zero the bitmap to take care of marking inserted positions null */
2447  MemSet(newnullbitmap, 0, (newnitems + 7) / 8);
2448  /* Fix the inserted value */
2449  if (addedafter)
2450  array_set_isnull(newnullbitmap, newnitems - 1, isNull);
2451  else
2452  array_set_isnull(newnullbitmap, offset, isNull);
2453  /* Fix the copied range(s) */
2454  if (addedbefore)
2455  array_bitmap_copy(newnullbitmap, addedbefore,
2456  oldnullbitmap, 0,
2457  oldnitems);
2458  else
2459  {
2460  array_bitmap_copy(newnullbitmap, 0,
2461  oldnullbitmap, 0,
2462  offset);
2463  if (addedafter == 0)
2464  array_bitmap_copy(newnullbitmap, offset + 1,
2465  oldnullbitmap, offset + 1,
2466  oldnitems - offset - 1);
2467  }
2468  }
2469 
2470  return PointerGetDatum(newarray);
2471 }
2472 
2473 /*
2474  * Implementation of array_set_element() for an expanded array
2475  *
2476  * Note: as with any operation on a read/write expanded object, we must
2477  * take pains not to leave the object in a corrupt state if we fail partway
2478  * through.
2479  */
2480 static Datum
2482  int nSubscripts, int *indx,
2483  Datum dataValue, bool isNull,
2484  int arraytyplen,
2485  int elmlen, bool elmbyval, char elmalign)
2486 {
2487  ExpandedArrayHeader *eah;
2488  Datum *dvalues;
2489  bool *dnulls;
2490  int i,
2491  ndim,
2492  dim[MAXDIM],
2493  lb[MAXDIM],
2494  offset;
2495  bool dimschanged,
2496  newhasnulls;
2497  int addedbefore,
2498  addedafter;
2499  char *oldValue;
2500 
2501  /* Convert to R/W object if not so already */
2502  eah = DatumGetExpandedArray(arraydatum);
2503 
2504  /* Sanity-check caller's info against object; we don't use it otherwise */
2505  Assert(arraytyplen == -1);
2506  Assert(elmlen == eah->typlen);
2507  Assert(elmbyval == eah->typbyval);
2508  Assert(elmalign == eah->typalign);
2509 
2510  /*
2511  * Copy dimension info into local storage. This allows us to modify the
2512  * dimensions if needed, while not messing up the expanded value if we
2513  * fail partway through.
2514  */
2515  ndim = eah->ndims;
2516  Assert(ndim >= 0 && ndim <= MAXDIM);
2517  memcpy(dim, eah->dims, ndim * sizeof(int));
2518  memcpy(lb, eah->lbound, ndim * sizeof(int));
2519  dimschanged = false;
2520 
2521  /*
2522  * if number of dims is zero, i.e. an empty array, create an array with
2523  * nSubscripts dimensions, and set the lower bounds to the supplied
2524  * subscripts.
2525  */
2526  if (ndim == 0)
2527  {
2528  /*
2529  * Allocate adequate space for new dimension info. This is harmless
2530  * if we fail later.
2531  */
2532  Assert(nSubscripts > 0 && nSubscripts <= MAXDIM);
2533  eah->dims = (int *) MemoryContextAllocZero(eah->hdr.eoh_context,
2534  nSubscripts * sizeof(int));
2535  eah->lbound = (int *) MemoryContextAllocZero(eah->hdr.eoh_context,
2536  nSubscripts * sizeof(int));
2537 
2538  /* Update local copies of dimension info */
2539  ndim = nSubscripts;
2540  for (i = 0; i < nSubscripts; i++)
2541  {
2542  dim[i] = 0;
2543  lb[i] = indx[i];
2544  }
2545  dimschanged = true;
2546  }
2547  else if (ndim != nSubscripts)
2548  ereport(ERROR,
2549  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2550  errmsg("wrong number of array subscripts")));
2551 
2552  /*
2553  * Deconstruct array if we didn't already. (Someday maybe add a special
2554  * case path for fixed-length, no-nulls cases, where we can overwrite an
2555  * element in place without ever deconstructing. But today is not that
2556  * day.)
2557  */
2559 
2560  /*
2561  * Copy new element into array's context, if needed (we assume it's
2562  * already detoasted, so no junk should be created). If we fail further
2563  * down, this memory is leaked, but that's reasonably harmless.
2564  */
2565  if (!eah->typbyval && !isNull)
2566  {
2568 
2569  dataValue = datumCopy(dataValue, false, eah->typlen);
2570  MemoryContextSwitchTo(oldcxt);
2571  }
2572 
2573  dvalues = eah->dvalues;
2574  dnulls = eah->dnulls;
2575 
2576  newhasnulls = ((dnulls != NULL) || isNull);
2577  addedbefore = addedafter = 0;
2578 
2579  /*
2580  * Check subscripts (this logic matches original array_set_element)
2581  */
2582  if (ndim == 1)
2583  {
2584  if (indx[0] < lb[0])
2585  {
2586  addedbefore = lb[0] - indx[0];
2587  dim[0] += addedbefore;
2588  lb[0] = indx[0];
2589  dimschanged = true;
2590  if (addedbefore > 1)
2591  newhasnulls = true; /* will insert nulls */
2592  }
2593  if (indx[0] >= (dim[0] + lb[0]))
2594  {
2595  addedafter = indx[0] - (dim[0] + lb[0]) + 1;
2596  dim[0] += addedafter;
2597  dimschanged = true;
2598  if (addedafter > 1)
2599  newhasnulls = true; /* will insert nulls */
2600  }
2601  }
2602  else
2603  {
2604  /*
2605  * XXX currently we do not support extending multi-dimensional arrays
2606  * during assignment
2607  */
2608  for (i = 0; i < ndim; i++)
2609  {
2610  if (indx[i] < lb[i] ||
2611  indx[i] >= (dim[i] + lb[i]))
2612  ereport(ERROR,
2613  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2614  errmsg("array subscript out of range")));
2615  }
2616  }
2617 
2618  /* Now we can calculate linear offset of target item in array */
2619  offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
2620 
2621  /* Physically enlarge existing dvalues/dnulls arrays if needed */
2622  if (dim[0] > eah->dvalueslen)
2623  {
2624  /* We want some extra space if we're enlarging */
2625  int newlen = dim[0] + dim[0] / 8;
2626 
2627  newlen = Max(newlen, dim[0]); /* integer overflow guard */
2628  eah->dvalues = dvalues = (Datum *)
2629  repalloc(dvalues, newlen * sizeof(Datum));
2630  if (dnulls)
2631  eah->dnulls = dnulls = (bool *)
2632  repalloc(dnulls, newlen * sizeof(bool));
2633  eah->dvalueslen = newlen;
2634  }
2635 
2636  /*
2637  * If we need a nulls bitmap and don't already have one, create it, being
2638  * sure to mark all existing entries as not null.
2639  */
2640  if (newhasnulls && dnulls == NULL)
2641  eah->dnulls = dnulls = (bool *)
2643  eah->dvalueslen * sizeof(bool));
2644 
2645  /*
2646  * We now have all the needed space allocated, so we're ready to make
2647  * irreversible changes. Be very wary of allowing failure below here.
2648  */
2649 
2650  /* Flattened value will no longer represent array accurately */
2651  eah->fvalue = NULL;
2652  /* And we don't know the flattened size either */
2653  eah->flat_size = 0;
2654 
2655  /* Update dimensionality info if needed */
2656  if (dimschanged)
2657  {
2658  eah->ndims = ndim;
2659  memcpy(eah->dims, dim, ndim * sizeof(int));
2660  memcpy(eah->lbound, lb, ndim * sizeof(int));
2661  }
2662 
2663  /* Reposition items if needed, and fill addedbefore items with nulls */
2664  if (addedbefore > 0)
2665  {
2666  memmove(dvalues + addedbefore, dvalues, eah->nelems * sizeof(Datum));
2667  for (i = 0; i < addedbefore; i++)
2668  dvalues[i] = (Datum) 0;
2669  if (dnulls)
2670  {
2671  memmove(dnulls + addedbefore, dnulls, eah->nelems * sizeof(bool));
2672  for (i = 0; i < addedbefore; i++)
2673  dnulls[i] = true;
2674  }
2675  eah->nelems += addedbefore;
2676  }
2677 
2678  /* fill addedafter items with nulls */
2679  if (addedafter > 0)
2680  {
2681  for (i = 0; i < addedafter; i++)
2682  dvalues[eah->nelems + i] = (Datum) 0;
2683  if (dnulls)
2684  {
2685  for (i = 0; i < addedafter; i++)
2686  dnulls[eah->nelems + i] = true;
2687  }
2688  eah->nelems += addedafter;
2689  }
2690 
2691  /* Grab old element value for pfree'ing, if needed. */
2692  if (!eah->typbyval && (dnulls == NULL || !dnulls[offset]))
2693  oldValue = (char *) DatumGetPointer(dvalues[offset]);
2694  else
2695  oldValue = NULL;
2696 
2697  /* And finally we can insert the new element. */
2698  dvalues[offset] = dataValue;
2699  if (dnulls)
2700  dnulls[offset] = isNull;
2701 
2702  /*
2703  * Free old element if needed; this keeps repeated element replacements
2704  * from bloating the array's storage. If the pfree somehow fails, it
2705  * won't corrupt the array.
2706  */
2707  if (oldValue)
2708  {
2709  /* Don't try to pfree a part of the original flat array */
2710  if (oldValue < eah->fstartptr || oldValue >= eah->fendptr)
2711  pfree(oldValue);
2712  }
2713 
2714  /* Done, return standard TOAST pointer for object */
2715  return EOHPGetRWDatum(&eah->hdr);
2716 }
2717 
2718 /*
2719  * array_set_slice :
2720  * This routine sets the value of a range of array locations (specified
2721  * by upper and lower subscript values) to new values passed as
2722  * another array.
2723  *
2724  * This handles both ordinary varlena arrays and fixed-length arrays.
2725  *
2726  * Inputs:
2727  * arraydatum: the initial array object (mustn't be NULL)
2728  * nSubscripts: number of subscripts supplied (must be same for upper/lower)
2729  * upperIndx[]: the upper subscript values
2730  * lowerIndx[]: the lower subscript values
2731  * upperProvided[]: true for provided upper subscript values
2732  * lowerProvided[]: true for provided lower subscript values
2733  * srcArrayDatum: the source for the inserted values
2734  * isNull: indicates whether srcArrayDatum is NULL
2735  * arraytyplen: pg_type.typlen for the array type
2736  * elmlen: pg_type.typlen for the array's element type
2737  * elmbyval: pg_type.typbyval for the array's element type
2738  * elmalign: pg_type.typalign for the array's element type
2739  *
2740  * Result:
2741  * A new array is returned, just like the old except for the
2742  * modified range. The original array object is not changed.
2743  *
2744  * Omitted upper and lower subscript values are replaced by the corresponding
2745  * array bound.
2746  *
2747  * For one-dimensional arrays only, we allow the array to be extended
2748  * by assigning to positions outside the existing subscript range; any
2749  * positions between the existing elements and the new ones are set to NULLs.
2750  * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
2751  *
2752  * NOTE: we assume it is OK to scribble on the provided index arrays
2753  * lowerIndx[] and upperIndx[]. These are generally just temporaries.
2754  *
2755  * NOTE: For assignments, we throw an error for silly subscripts etc,
2756  * rather than returning a NULL or empty array as the fetch operations do.
2757  */
2758 Datum
2760  int nSubscripts,
2761  int *upperIndx,
2762  int *lowerIndx,
2763  bool *upperProvided,
2764  bool *lowerProvided,
2765  Datum srcArrayDatum,
2766  bool isNull,
2767  int arraytyplen,
2768  int elmlen,
2769  bool elmbyval,
2770  char elmalign)
2771 {
2772  ArrayType *array;
2773  ArrayType *srcArray;
2774  ArrayType *newarray;
2775  int i,
2776  ndim,
2777  dim[MAXDIM],
2778  lb[MAXDIM],
2779  span[MAXDIM];
2780  bool newhasnulls;
2781  int nitems,
2782  nsrcitems,
2783  olddatasize,
2784  newsize,
2785  olditemsize,
2786  newitemsize,
2787  overheadlen,
2788  oldoverheadlen,
2789  addedbefore,
2790  addedafter,
2791  lenbefore,
2792  lenafter,
2793  itemsbefore,
2794  itemsafter,
2795  nolditems;
2796 
2797  /* Currently, assignment from a NULL source array is a no-op */
2798  if (isNull)
2799  return arraydatum;
2800 
2801  if (arraytyplen > 0)
2802  {
2803  /*
2804  * fixed-length arrays -- not got round to doing this...
2805  */
2806  ereport(ERROR,
2807  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2808  errmsg("updates on slices of fixed-length arrays not implemented")));
2809  }
2810 
2811  /* detoast arrays if necessary */
2812  array = DatumGetArrayTypeP(arraydatum);
2813  srcArray = DatumGetArrayTypeP(srcArrayDatum);
2814 
2815  /* note: we assume srcArray contains no toasted elements */
2816 
2817  ndim = ARR_NDIM(array);
2818 
2819  /*
2820  * if number of dims is zero, i.e. an empty array, create an array with
2821  * nSubscripts dimensions, and set the upper and lower bounds to the
2822  * supplied subscripts
2823  */
2824  if (ndim == 0)
2825  {
2826  Datum *dvalues;
2827  bool *dnulls;
2828  int nelems;
2829  Oid elmtype = ARR_ELEMTYPE(array);
2830 
2831  deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
2832  &dvalues, &dnulls, &nelems);
2833 
2834  for (i = 0; i < nSubscripts; i++)
2835  {
2836  if (!upperProvided[i] || !lowerProvided[i])
2837  ereport(ERROR,
2838  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2839  errmsg("array slice subscript must provide both boundaries"),
2840  errdetail("When assigning to a slice of an empty array value,"
2841  " slice boundaries must be fully specified.")));
2842 
2843  dim[i] = 1 + upperIndx[i] - lowerIndx[i];
2844  lb[i] = lowerIndx[i];
2845  }
2846 
2847  /* complain if too few source items; we ignore extras, however */
2848  if (nelems < ArrayGetNItems(nSubscripts, dim))
2849  ereport(ERROR,
2850  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2851  errmsg("source array too small")));
2852 
2853  return PointerGetDatum(construct_md_array(dvalues, dnulls, nSubscripts,
2854  dim, lb, elmtype,
2855  elmlen, elmbyval, elmalign));
2856  }
2857 
2858  if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
2859  ereport(ERROR,
2860  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2861  errmsg("wrong number of array subscripts")));
2862 
2863  /* copy dim/lb since we may modify them */
2864  memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
2865  memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
2866 
2867  newhasnulls = (ARR_HASNULL(array) || ARR_HASNULL(srcArray));
2868  addedbefore = addedafter = 0;
2869 
2870  /*
2871  * Check subscripts
2872  */
2873  if (ndim == 1)
2874  {
2875  Assert(nSubscripts == 1);
2876  if (!lowerProvided[0])
2877  lowerIndx[0] = lb[0];
2878  if (!upperProvided[0])
2879  upperIndx[0] = dim[0] + lb[0] - 1;
2880  if (lowerIndx[0] > upperIndx[0])
2881  ereport(ERROR,
2882  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2883  errmsg("upper bound cannot be less than lower bound")));
2884  if (lowerIndx[0] < lb[0])
2885  {
2886  if (upperIndx[0] < lb[0] - 1)
2887  newhasnulls = true; /* will insert nulls */
2888  addedbefore = lb[0] - lowerIndx[0];
2889  dim[0] += addedbefore;
2890  lb[0] = lowerIndx[0];
2891  }
2892  if (upperIndx[0] >= (dim[0] + lb[0]))
2893  {
2894  if (lowerIndx[0] > (dim[0] + lb[0]))
2895  newhasnulls = true; /* will insert nulls */
2896  addedafter = upperIndx[0] - (dim[0] + lb[0]) + 1;
2897  dim[0] += addedafter;
2898  }
2899  }
2900  else
2901  {
2902  /*
2903  * XXX currently we do not support extending multi-dimensional arrays
2904  * during assignment
2905  */
2906  for (i = 0; i < nSubscripts; i++)
2907  {
2908  if (!lowerProvided[i])
2909  lowerIndx[i] = lb[i];
2910  if (!upperProvided[i])
2911  upperIndx[i] = dim[i] + lb[i] - 1;
2912  if (lowerIndx[i] > upperIndx[i])
2913  ereport(ERROR,
2914  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2915  errmsg("upper bound cannot be less than lower bound")));
2916  if (lowerIndx[i] < lb[i] ||
2917  upperIndx[i] >= (dim[i] + lb[i]))
2918  ereport(ERROR,
2919  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2920  errmsg("array subscript out of range")));
2921  }
2922  /* fill any missing subscript positions with full array range */
2923  for (; i < ndim; i++)
2924  {
2925  lowerIndx[i] = lb[i];
2926  upperIndx[i] = dim[i] + lb[i] - 1;
2927  if (lowerIndx[i] > upperIndx[i])
2928  ereport(ERROR,
2929  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2930  errmsg("upper bound cannot be less than lower bound")));
2931  }
2932  }
2933 
2934  /* Do this mainly to check for overflow */
2935  nitems = ArrayGetNItems(ndim, dim);
2936 
2937  /*
2938  * Make sure source array has enough entries. Note we ignore the shape of
2939  * the source array and just read entries serially.
2940  */
2941  mda_get_range(ndim, span, lowerIndx, upperIndx);
2942  nsrcitems = ArrayGetNItems(ndim, span);
2943  if (nsrcitems > ArrayGetNItems(ARR_NDIM(srcArray), ARR_DIMS(srcArray)))
2944  ereport(ERROR,
2945  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2946  errmsg("source array too small")));
2947 
2948  /*
2949  * Compute space occupied by new entries, space occupied by replaced
2950  * entries, and required space for new array.
2951  */
2952  if (newhasnulls)
2953  overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
2954  else
2955  overheadlen = ARR_OVERHEAD_NONULLS(ndim);
2956  newitemsize = array_nelems_size(ARR_DATA_PTR(srcArray), 0,
2957  ARR_NULLBITMAP(srcArray), nsrcitems,
2958  elmlen, elmbyval, elmalign);
2959  oldoverheadlen = ARR_DATA_OFFSET(array);
2960  olddatasize = ARR_SIZE(array) - oldoverheadlen;
2961  if (ndim > 1)
2962  {
2963  /*
2964  * here we do not need to cope with extension of the array; it would
2965  * be a lot more complicated if we had to do so...
2966  */
2967  olditemsize = array_slice_size(ARR_DATA_PTR(array),
2968  ARR_NULLBITMAP(array),
2969  ndim, dim, lb,
2970  lowerIndx, upperIndx,
2971  elmlen, elmbyval, elmalign);
2972  lenbefore = lenafter = 0; /* keep compiler quiet */
2973  itemsbefore = itemsafter = nolditems = 0;
2974  }
2975  else
2976  {
2977  /*
2978  * here we must allow for possibility of slice larger than orig array
2979  * and/or not adjacent to orig array subscripts
2980  */
2981  int oldlb = ARR_LBOUND(array)[0];
2982  int oldub = oldlb + ARR_DIMS(array)[0] - 1;
2983  int slicelb = Max(oldlb, lowerIndx[0]);
2984  int sliceub = Min(oldub, upperIndx[0]);
2985  char *oldarraydata = ARR_DATA_PTR(array);
2986  bits8 *oldarraybitmap = ARR_NULLBITMAP(array);
2987 
2988  /* count/size of old array entries that will go before the slice */
2989  itemsbefore = Min(slicelb, oldub + 1) - oldlb;
2990  lenbefore = array_nelems_size(oldarraydata, 0, oldarraybitmap,
2991  itemsbefore,
2992  elmlen, elmbyval, elmalign);
2993  /* count/size of old array entries that will be replaced by slice */
2994  if (slicelb > sliceub)
2995  {
2996  nolditems = 0;
2997  olditemsize = 0;
2998  }
2999  else
3000  {
3001  nolditems = sliceub - slicelb + 1;
3002  olditemsize = array_nelems_size(oldarraydata + lenbefore,
3003  itemsbefore, oldarraybitmap,
3004  nolditems,
3005  elmlen, elmbyval, elmalign);
3006  }
3007  /* count/size of old array entries that will go after the slice */
3008  itemsafter = oldub + 1 - Max(sliceub + 1, oldlb);
3009  lenafter = olddatasize - lenbefore - olditemsize;
3010  }
3011 
3012  newsize = overheadlen + olddatasize - olditemsize + newitemsize;
3013 
3014  newarray = (ArrayType *) palloc0(newsize);
3015  SET_VARSIZE(newarray, newsize);
3016  newarray->ndim = ndim;
3017  newarray->dataoffset = newhasnulls ? overheadlen : 0;
3018  newarray->elemtype = ARR_ELEMTYPE(array);
3019  memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
3020  memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
3021 
3022  if (ndim > 1)
3023  {
3024  /*
3025  * here we do not need to cope with extension of the array; it would
3026  * be a lot more complicated if we had to do so...
3027  */
3028  array_insert_slice(newarray, array, srcArray,
3029  ndim, dim, lb,
3030  lowerIndx, upperIndx,
3031  elmlen, elmbyval, elmalign);
3032  }
3033  else
3034  {
3035  /* fill in data */
3036  memcpy((char *) newarray + overheadlen,
3037  (char *) array + oldoverheadlen,
3038  lenbefore);
3039  memcpy((char *) newarray + overheadlen + lenbefore,
3040  ARR_DATA_PTR(srcArray),
3041  newitemsize);
3042  memcpy((char *) newarray + overheadlen + lenbefore + newitemsize,
3043  (char *) array + oldoverheadlen + lenbefore + olditemsize,
3044  lenafter);
3045  /* fill in nulls bitmap if needed */
3046  if (newhasnulls)
3047  {
3048  bits8 *newnullbitmap = ARR_NULLBITMAP(newarray);
3049  bits8 *oldnullbitmap = ARR_NULLBITMAP(array);
3050 
3051  /* Zero the bitmap to handle marking inserted positions null */
3052  MemSet(newnullbitmap, 0, (nitems + 7) / 8);
3053  array_bitmap_copy(newnullbitmap, addedbefore,
3054  oldnullbitmap, 0,
3055  itemsbefore);
3056  array_bitmap_copy(newnullbitmap, lowerIndx[0] - lb[0],
3057  ARR_NULLBITMAP(srcArray), 0,
3058  nsrcitems);
3059  array_bitmap_copy(newnullbitmap, addedbefore + itemsbefore + nolditems,
3060  oldnullbitmap, itemsbefore + nolditems,
3061  itemsafter);
3062  }
3063  }
3064 
3065  return PointerGetDatum(newarray);
3066 }
3067 
3068 /*
3069  * array_ref : backwards compatibility wrapper for array_get_element
3070  *
3071  * This only works for detoasted/flattened varlena arrays, since the array
3072  * argument is declared as "ArrayType *". However there's enough code like
3073  * that to justify preserving this API.
3074  */
3075 Datum
3076 array_ref(ArrayType *array, int nSubscripts, int *indx,
3077  int arraytyplen, int elmlen, bool elmbyval, char elmalign,
3078  bool *isNull)
3079 {
3080  return array_get_element(PointerGetDatum(array), nSubscripts, indx,
3081  arraytyplen, elmlen, elmbyval, elmalign,
3082  isNull);
3083 }
3084 
3085 /*
3086  * array_set : backwards compatibility wrapper for array_set_element
3087  *
3088  * This only works for detoasted/flattened varlena arrays, since the array
3089  * argument and result are declared as "ArrayType *". However there's enough
3090  * code like that to justify preserving this API.
3091  */
3092 ArrayType *
3093 array_set(ArrayType *array, int nSubscripts, int *indx,
3094  Datum dataValue, bool isNull,
3095  int arraytyplen, int elmlen, bool elmbyval, char elmalign)
3096 {
3098  nSubscripts, indx,
3099  dataValue, isNull,
3100  arraytyplen,
3101  elmlen, elmbyval, elmalign));
3102 }
3103 
3104 /*
3105  * array_map()
3106  *
3107  * Map an array through an arbitrary expression. Return a new array with
3108  * the same dimensions and each source element transformed by the given,
3109  * already-compiled expression. Each source element is placed in the
3110  * innermost_caseval/innermost_casenull fields of the ExprState.
3111  *
3112  * Parameters are:
3113  * * arrayd: Datum representing array argument.
3114  * * exprstate: ExprState representing the per-element transformation.
3115  * * econtext: context for expression evaluation.
3116  * * retType: OID of element type of output array. This must be the same as,
3117  * or binary-compatible with, the result type of the expression. It might
3118  * be different from the input array's element type.
3119  * * amstate: workspace for array_map. Must be zeroed by caller before
3120  * first call, and not touched after that.
3121  *
3122  * It is legitimate to pass a freshly-zeroed ArrayMapState on each call,
3123  * but better performance can be had if the state can be preserved across
3124  * a series of calls.
3125  *
3126  * NB: caller must assure that input array is not NULL. NULL elements in
3127  * the array are OK however.
3128  * NB: caller should be running in econtext's per-tuple memory context.
3129  */
3130 Datum
3132  ExprState *exprstate, ExprContext *econtext,
3133  Oid retType, ArrayMapState *amstate)
3134 {
3135  AnyArrayType *v = DatumGetAnyArrayP(arrayd);
3136  ArrayType *result;
3137  Datum *values;
3138  bool *nulls;
3139  int *dim;
3140  int ndim;
3141  int nitems;
3142  int i;
3143  int32 nbytes = 0;
3144  int32 dataoffset;
3145  bool hasnulls;
3146  Oid inpType;
3147  int inp_typlen;
3148  bool inp_typbyval;
3149  char inp_typalign;
3150  int typlen;
3151  bool typbyval;
3152  char typalign;
3153  array_iter iter;
3154  ArrayMetaState *inp_extra;
3155  ArrayMetaState *ret_extra;
3156  Datum *transform_source = exprstate->innermost_caseval;
3157  bool *transform_source_isnull = exprstate->innermost_casenull;
3158 
3159  inpType = AARR_ELEMTYPE(v);
3160  ndim = AARR_NDIM(v);
3161  dim = AARR_DIMS(v);
3162  nitems = ArrayGetNItems(ndim, dim);
3163 
3164  /* Check for empty array */
3165  if (nitems <= 0)
3166  {
3167  /* Return empty array */
3168  return PointerGetDatum(construct_empty_array(retType));
3169  }
3170 
3171  /*
3172  * We arrange to look up info about input and return element types only
3173  * once per series of calls, assuming the element type doesn't change
3174  * underneath us.
3175  */
3176  inp_extra = &amstate->inp_extra;
3177  ret_extra = &amstate->ret_extra;
3178 
3179  if (inp_extra->element_type != inpType)
3180  {
3181  get_typlenbyvalalign(inpType,
3182  &inp_extra->typlen,
3183  &inp_extra->typbyval,
3184  &inp_extra->typalign);
3185  inp_extra->element_type = inpType;
3186  }
3187  inp_typlen = inp_extra->typlen;
3188  inp_typbyval = inp_extra->typbyval;
3189  inp_typalign = inp_extra->typalign;
3190 
3191  if (ret_extra->element_type != retType)
3192  {
3193  get_typlenbyvalalign(retType,
3194  &ret_extra->typlen,
3195  &ret_extra->typbyval,
3196  &ret_extra->typalign);
3197  ret_extra->element_type = retType;
3198  }
3199  typlen = ret_extra->typlen;
3200  typbyval = ret_extra->typbyval;
3201  typalign = ret_extra->typalign;
3202 
3203  /* Allocate temporary arrays for new values */
3204  values = (Datum *) palloc(nitems * sizeof(Datum));
3205  nulls = (bool *) palloc(nitems * sizeof(bool));
3206 
3207  /* Loop over source data */
3208  array_iter_setup(&iter, v);
3209  hasnulls = false;
3210 
3211  for (i = 0; i < nitems; i++)
3212  {
3213  /* Get source element, checking for NULL */
3214  *transform_source =
3215  array_iter_next(&iter, transform_source_isnull, i,
3216  inp_typlen, inp_typbyval, inp_typalign);
3217 
3218  /* Apply the given expression to source element */
3219  values[i] = ExecEvalExpr(exprstate, econtext, &nulls[i]);
3220 
3221  if (nulls[i])
3222  hasnulls = true;
3223  else
3224  {
3225  /* Ensure data is not toasted */
3226  if (typlen == -1)
3227  values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
3228  /* Update total result size */
3229  nbytes = att_addlength_datum(nbytes, typlen, values[i]);
3230  nbytes = att_align_nominal(nbytes, typalign);
3231  /* check for overflow of total request */
3232  if (!AllocSizeIsValid(nbytes))
3233  ereport(ERROR,
3234  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3235  errmsg("array size exceeds the maximum allowed (%d)",
3236  (int) MaxAllocSize)));
3237  }
3238  }
3239 
3240  /* Allocate and fill the result array */
3241  if (hasnulls)
3242  {
3243  dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
3244  nbytes += dataoffset;
3245  }
3246  else
3247  {
3248  dataoffset = 0; /* marker for no null bitmap */
3249  nbytes += ARR_OVERHEAD_NONULLS(ndim);
3250  }
3251  result = (ArrayType *) palloc0(nbytes);
3252  SET_VARSIZE(result, nbytes);
3253  result->ndim = ndim;
3254  result->dataoffset = dataoffset;
3255  result->elemtype = retType;
3256  memcpy(ARR_DIMS(result), AARR_DIMS(v), ndim * sizeof(int));
3257  memcpy(ARR_LBOUND(result), AARR_LBOUND(v), ndim * sizeof(int));
3258 
3259  CopyArrayEls(result,
3260  values, nulls, nitems,
3261  typlen, typbyval, typalign,
3262  false);
3263 
3264  /*
3265  * Note: do not risk trying to pfree the results of the called expression
3266  */
3267  pfree(values);
3268  pfree(nulls);
3269 
3270  return PointerGetDatum(result);
3271 }
3272 
3273 /*
3274  * construct_array --- simple method for constructing an array object
3275  *
3276  * elems: array of Datum items to become the array contents
3277  * (NULL element values are not supported).
3278  * nelems: number of items
3279  * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
3280  *
3281  * A palloc'd 1-D array object is constructed and returned. Note that
3282  * elem values will be copied into the object even if pass-by-ref type.
3283  * Also note the result will be 0-D not 1-D if nelems = 0.
3284  *
3285  * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
3286  * from the system catalogs, given the elmtype. However, the caller is
3287  * in a better position to cache this info across multiple uses, or even
3288  * to hard-wire values if the element type is hard-wired.
3289  */
3290 ArrayType *
3291 construct_array(Datum *elems, int nelems,
3292  Oid elmtype,
3293  int elmlen, bool elmbyval, char elmalign)
3294 {
3295  int dims[1];
3296  int lbs[1];
3297 
3298  dims[0] = nelems;
3299  lbs[0] = 1;
3300 
3301  return construct_md_array(elems, NULL, 1, dims, lbs,
3302  elmtype, elmlen, elmbyval, elmalign);
3303 }
3304 
3305 /*
3306  * construct_md_array --- simple method for constructing an array object
3307  * with arbitrary dimensions and possible NULLs
3308  *
3309  * elems: array of Datum items to become the array contents
3310  * nulls: array of is-null flags (can be NULL if no nulls)
3311  * ndims: number of dimensions
3312  * dims: integer array with size of each dimension
3313  * lbs: integer array with lower bound of each dimension
3314  * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
3315  *
3316  * A palloc'd ndims-D array object is constructed and returned. Note that
3317  * elem values will be copied into the object even if pass-by-ref type.
3318  * Also note the result will be 0-D not ndims-D if any dims[i] = 0.
3319  *
3320  * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
3321  * from the system catalogs, given the elmtype. However, the caller is
3322  * in a better position to cache this info across multiple uses, or even
3323  * to hard-wire values if the element type is hard-wired.
3324  */
3325 ArrayType *
3327  bool *nulls,
3328  int ndims,
3329  int *dims,
3330  int *lbs,
3331  Oid elmtype, int elmlen, bool elmbyval, char elmalign)
3332 {
3333  ArrayType *result;
3334  bool hasnulls;
3335  int32 nbytes;
3336  int32 dataoffset;
3337  int i;
3338  int nelems;
3339 
3340  if (ndims < 0) /* we do allow zero-dimension arrays */
3341  ereport(ERROR,
3342  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3343  errmsg("invalid number of dimensions: %d", ndims)));
3344  if (ndims > MAXDIM)
3345  ereport(ERROR,
3346  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3347  errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
3348  ndims, MAXDIM)));
3349 
3350  nelems = ArrayGetNItems(ndims, dims);
3351 
3352  /* if ndims <= 0 or any dims[i] == 0, return empty array */
3353  if (nelems <= 0)
3354  return construct_empty_array(elmtype);
3355 
3356  /* compute required space */
3357  nbytes = 0;
3358  hasnulls = false;
3359  for (i = 0; i < nelems; i++)
3360  {
3361  if (nulls && nulls[i])
3362  {
3363  hasnulls = true;
3364  continue;
3365  }
3366  /* make sure data is not toasted */
3367  if (elmlen == -1)
3368  elems[i] = PointerGetDatum(PG_DETOAST_DATUM(elems[i]));
3369  nbytes = att_addlength_datum(nbytes, elmlen, elems[i]);
3370  nbytes = att_align_nominal(nbytes, elmalign);
3371  /* check for overflow of total request */
3372  if (!AllocSizeIsValid(nbytes))
3373  ereport(ERROR,
3374  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3375  errmsg("array size exceeds the maximum allowed (%d)",
3376  (int) MaxAllocSize)));
3377  }
3378 
3379  /* Allocate and initialize result array */
3380  if (hasnulls)
3381  {
3382  dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nelems);
3383  nbytes += dataoffset;
3384  }
3385  else
3386  {
3387  dataoffset = 0; /* marker for no null bitmap */
3388  nbytes += ARR_OVERHEAD_NONULLS(ndims);
3389  }
3390  result = (ArrayType *) palloc0(nbytes);
3391  SET_VARSIZE(result, nbytes);
3392  result->ndim = ndims;
3393  result->dataoffset = dataoffset;
3394  result->elemtype = elmtype;
3395  memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
3396  memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
3397 
3398  CopyArrayEls(result,
3399  elems, nulls, nelems,
3400  elmlen, elmbyval, elmalign,
3401  false);
3402 
3403  return result;
3404 }
3405 
3406 /*
3407  * construct_empty_array --- make a zero-dimensional array of given type
3408  */
3409 ArrayType *
3411 {
3412  ArrayType *result;
3413 
3414  result = (ArrayType *) palloc0(sizeof(ArrayType));
3415  SET_VARSIZE(result, sizeof(ArrayType));
3416  result->ndim = 0;
3417  result->dataoffset = 0;
3418  result->elemtype = elmtype;
3419  return result;
3420 }
3421 
3422 /*
3423  * construct_empty_expanded_array: make an empty expanded array
3424  * given only type information. (metacache can be NULL if not needed.)
3425  */
3428  MemoryContext parentcontext,
3429  ArrayMetaState *metacache)
3430 {
3431  ArrayType *array = construct_empty_array(element_type);
3432  Datum d;
3433 
3434  d = expand_array(PointerGetDatum(array), parentcontext, metacache);
3435  pfree(array);
3436  return (ExpandedArrayHeader *) DatumGetEOHP(d);
3437 }
3438 
3439 /*
3440  * deconstruct_array --- simple method for extracting data from an array
3441  *
3442  * array: array object to examine (must not be NULL)
3443  * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
3444  * elemsp: return value, set to point to palloc'd array of Datum values
3445  * nullsp: return value, set to point to palloc'd array of isnull markers
3446  * nelemsp: return value, set to number of extracted values
3447  *
3448  * The caller may pass nullsp == NULL if it does not support NULLs in the
3449  * array. Note that this produces a very uninformative error message,
3450  * so do it only in cases where a NULL is really not expected.
3451  *
3452  * If array elements are pass-by-ref data type, the returned Datums will
3453  * be pointers into the array object.
3454  *
3455  * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
3456  * from the system catalogs, given the elmtype. However, in most current
3457  * uses the type is hard-wired into the caller and so we can save a lookup
3458  * cycle by hard-wiring the type info as well.
3459  */
3460 void
3462  Oid elmtype,
3463  int elmlen, bool elmbyval, char elmalign,
3464  Datum **elemsp, bool **nullsp, int *nelemsp)
3465 {
3466  Datum *elems;
3467  bool *nulls;
3468  int nelems;
3469  char *p;
3470  bits8 *bitmap;
3471  int bitmask;
3472  int i;
3473 
3474  Assert(ARR_ELEMTYPE(array) == elmtype);
3475 
3476  nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
3477  *elemsp = elems = (Datum *) palloc(nelems * sizeof(Datum));
3478  if (nullsp)
3479  *nullsp = nulls = (bool *) palloc0(nelems * sizeof(bool));
3480  else
3481  nulls = NULL;
3482  *nelemsp = nelems;
3483 
3484  p = ARR_DATA_PTR(array);
3485  bitmap = ARR_NULLBITMAP(array);
3486  bitmask = 1;
3487 
3488  for (i = 0; i < nelems; i++)
3489  {
3490  /* Get source element, checking for NULL */
3491  if (bitmap && (*bitmap & bitmask) == 0)
3492  {
3493  elems[i] = (Datum) 0;
3494  if (nulls)
3495  nulls[i] = true;
3496  else
3497  ereport(ERROR,
3498  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
3499  errmsg("null array element not allowed in this context")));
3500  }
3501  else
3502  {
3503  elems[i] = fetch_att(p, elmbyval, elmlen);
3504  p = att_addlength_pointer(p, elmlen, p);
3505  p = (char *) att_align_nominal(p, elmalign);
3506  }
3507 
3508  /* advance bitmap pointer if any */
3509  if (bitmap)
3510  {
3511  bitmask <<= 1;
3512  if (bitmask == 0x100)
3513  {
3514  bitmap++;
3515  bitmask = 1;
3516  }
3517  }
3518  }
3519 }
3520 
3521 /*
3522  * array_contains_nulls --- detect whether an array has any null elements
3523  *
3524  * This gives an accurate answer, whereas testing ARR_HASNULL only tells
3525  * if the array *might* contain a null.
3526  */
3527 bool
3529 {
3530  int nelems;
3531  bits8 *bitmap;
3532  int bitmask;
3533 
3534  /* Easy answer if there's no null bitmap */
3535  if (!ARR_HASNULL(array))
3536  return false;
3537 
3538  nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
3539 
3540  bitmap = ARR_NULLBITMAP(array);
3541 
3542  /* check whole bytes of the bitmap byte-at-a-time */
3543  while (nelems >= 8)
3544  {
3545  if (*bitmap != 0xFF)
3546  return true;
3547  bitmap++;
3548  nelems -= 8;
3549  }
3550 
3551  /* check last partial byte */
3552  bitmask = 1;
3553  while (nelems > 0)
3554  {
3555  if ((*bitmap & bitmask) == 0)
3556  return true;
3557  bitmask <<= 1;
3558  nelems--;
3559  }
3560 
3561  return false;
3562 }
3563 
3564 
3565 /*
3566  * array_eq :
3567  * compares two arrays for equality
3568  * result :
3569  * returns true if the arrays are equal, false otherwise.
3570  *
3571  * Note: we do not use array_cmp here, since equality may be meaningful in
3572  * datatypes that don't have a total ordering (and hence no btree support).
3573  */
3574 Datum
3576 {
3577  LOCAL_FCINFO(locfcinfo, 2);
3578  AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
3579  AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
3580  Oid collation = PG_GET_COLLATION();
3581  int ndims1 = AARR_NDIM(array1);
3582  int ndims2 = AARR_NDIM(array2);
3583  int *dims1 = AARR_DIMS(array1);
3584  int *dims2 = AARR_DIMS(array2);
3585  int *lbs1 = AARR_LBOUND(array1);
3586  int *lbs2 = AARR_LBOUND(array2);
3587  Oid element_type = AARR_ELEMTYPE(array1);
3588  bool result = true;
3589  int nitems;
3590  TypeCacheEntry *typentry;
3591  int typlen;
3592  bool typbyval;
3593  char typalign;
3594  array_iter it1;
3595  array_iter it2;
3596  int i;
3597 
3598  if (element_type != AARR_ELEMTYPE(array2))
3599  ereport(ERROR,
3600  (errcode(ERRCODE_DATATYPE_MISMATCH),
3601  errmsg("cannot compare arrays of different element types")));
3602 
3603  /* fast path if the arrays do not have the same dimensionality */
3604  if (ndims1 != ndims2 ||
3605  memcmp(dims1, dims2, ndims1 * sizeof(int)) != 0 ||
3606  memcmp(lbs1, lbs2, ndims1 * sizeof(int)) != 0)
3607  result = false;
3608  else
3609  {
3610  /*
3611  * We arrange to look up the equality function only once per series of
3612  * calls, assuming the element type doesn't change underneath us. The
3613  * typcache is used so that we have no memory leakage when being used
3614  * as an index support function.
3615  */
3616  typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3617  if (typentry == NULL ||
3618  typentry->type_id != element_type)
3619  {
3620  typentry = lookup_type_cache(element_type,
3622  if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
3623  ereport(ERROR,
3624  (errcode(ERRCODE_UNDEFINED_FUNCTION),
3625  errmsg("could not identify an equality operator for type %s",
3626  format_type_be(element_type))));
3627  fcinfo->flinfo->fn_extra = (void *) typentry;
3628  }
3629  typlen = typentry->typlen;
3630  typbyval = typentry->typbyval;
3631  typalign = typentry->typalign;
3632 
3633  /*
3634  * apply the operator to each pair of array elements.
3635  */
3636  InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
3637  collation, NULL, NULL);
3638 
3639  /* Loop over source data */
3640  nitems = ArrayGetNItems(ndims1, dims1);
3641  array_iter_setup(&it1, array1);
3642  array_iter_setup(&it2, array2);
3643 
3644  for (i = 0; i < nitems; i++)
3645  {
3646  Datum elt1;
3647  Datum elt2;
3648  bool isnull1;
3649  bool isnull2;
3650  bool oprresult;
3651 
3652  /* Get elements, checking for NULL */
3653  elt1 = array_iter_next(&it1, &isnull1, i,
3654  typlen, typbyval, typalign);
3655  elt2 = array_iter_next(&it2, &isnull2, i,
3656  typlen, typbyval, typalign);
3657 
3658  /*
3659  * We consider two NULLs equal; NULL and not-NULL are unequal.
3660  */
3661  if (isnull1 && isnull2)
3662  continue;
3663  if (isnull1 || isnull2)
3664  {
3665  result = false;
3666  break;
3667  }
3668 
3669  /*
3670  * Apply the operator to the element pair
3671  */
3672  locfcinfo->args[0].value = elt1;
3673  locfcinfo->args[0].isnull = false;
3674  locfcinfo->args[1].value = elt2;
3675  locfcinfo->args[1].isnull = false;
3676  locfcinfo->isnull = false;
3677  oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
3678  if (!oprresult)
3679  {
3680  result = false;
3681  break;
3682  }
3683  }
3684  }
3685 
3686  /* Avoid leaking memory when handed toasted input. */
3687  AARR_FREE_IF_COPY(array1, 0);
3688  AARR_FREE_IF_COPY(array2, 1);
3689 
3690  PG_RETURN_BOOL(result);
3691 }
3692 
3693 
3694 /*-----------------------------------------------------------------------------
3695  * array-array bool operators:
3696  * Given two arrays, iterate comparison operators
3697  * over the array. Uses logic similar to text comparison
3698  * functions, except element-by-element instead of
3699  * character-by-character.
3700  *----------------------------------------------------------------------------
3701  */
3702 
3703 Datum
3705 {
3707 }
3708 
3709 Datum
3711 {
3712  PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
3713 }
3714 
3715 Datum
3717 {
3718  PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
3719 }
3720 
3721 Datum
3723 {
3724  PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
3725 }
3726 
3727 Datum
3729 {
3730  PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
3731 }
3732 
3733 Datum
3735 {
3736  PG_RETURN_INT32(array_cmp(fcinfo));
3737 }
3738 
3739 /*
3740  * array_cmp()
3741  * Internal comparison function for arrays.
3742  *
3743  * Returns -1, 0 or 1
3744  */
3745 static int
3747 {
3748  LOCAL_FCINFO(locfcinfo, 2);
3749  AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
3750  AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
3751  Oid collation = PG_GET_COLLATION();
3752  int ndims1 = AARR_NDIM(array1);
3753  int ndims2 = AARR_NDIM(array2);
3754  int *dims1 = AARR_DIMS(array1);
3755  int *dims2 = AARR_DIMS(array2);
3756  int nitems1 = ArrayGetNItems(ndims1, dims1);
3757  int nitems2 = ArrayGetNItems(ndims2, dims2);
3758  Oid element_type = AARR_ELEMTYPE(array1);
3759  int result = 0;
3760  TypeCacheEntry *typentry;
3761  int typlen;
3762  bool typbyval;
3763  char typalign;
3764  int min_nitems;
3765  array_iter it1;
3766  array_iter it2;
3767  int i;
3768 
3769  if (element_type != AARR_ELEMTYPE(array2))
3770  ereport(ERROR,
3771  (errcode(ERRCODE_DATATYPE_MISMATCH),
3772  errmsg("cannot compare arrays of different element types")));
3773 
3774  /*
3775  * We arrange to look up the comparison function only once per series of
3776  * calls, assuming the element type doesn't change underneath us. The
3777  * typcache is used so that we have no memory leakage when being used as
3778  * an index support function.
3779  */
3780  typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3781  if (typentry == NULL ||
3782  typentry->type_id != element_type)
3783  {
3784  typentry = lookup_type_cache(element_type,
3786  if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
3787  ereport(ERROR,
3788  (errcode(ERRCODE_UNDEFINED_FUNCTION),
3789  errmsg("could not identify a comparison function for type %s",
3790  format_type_be(element_type))));
3791  fcinfo->flinfo->fn_extra = (void *) typentry;
3792  }
3793  typlen = typentry->typlen;
3794  typbyval = typentry->typbyval;
3795  typalign = typentry->typalign;
3796 
3797  /*
3798  * apply the operator to each pair of array elements.
3799  */
3800  InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
3801  collation, NULL, NULL);
3802 
3803  /* Loop over source data */
3804  min_nitems = Min(nitems1, nitems2);
3805  array_iter_setup(&it1, array1);
3806  array_iter_setup(&it2, array2);
3807 
3808  for (i = 0; i < min_nitems; i++)
3809  {
3810  Datum elt1;
3811  Datum elt2;
3812  bool isnull1;
3813  bool isnull2;
3814  int32 cmpresult;
3815 
3816  /* Get elements, checking for NULL */
3817  elt1 = array_iter_next(&it1, &isnull1, i, typlen, typbyval, typalign);
3818  elt2 = array_iter_next(&it2, &isnull2, i, typlen, typbyval, typalign);
3819 
3820  /*
3821  * We consider two NULLs equal; NULL > not-NULL.
3822  */
3823  if (isnull1 && isnull2)
3824  continue;
3825  if (isnull1)
3826  {
3827  /* arg1 is greater than arg2 */
3828  result = 1;
3829  break;
3830  }
3831  if (isnull2)
3832  {
3833  /* arg1 is less than arg2 */
3834  result = -1;
3835  break;
3836  }
3837 
3838  /* Compare the pair of elements */
3839  locfcinfo->args[0].value = elt1;
3840  locfcinfo->args[0].isnull = false;
3841  locfcinfo->args[1].value = elt2;
3842  locfcinfo->args[1].isnull = false;
3843  locfcinfo->isnull = false;
3844  cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
3845 
3846  if (cmpresult == 0)
3847  continue; /* equal */
3848 
3849  if (cmpresult < 0)
3850  {
3851  /* arg1 is less than arg2 */
3852  result = -1;
3853  break;
3854  }
3855  else
3856  {
3857  /* arg1 is greater than arg2 */
3858  result = 1;
3859  break;
3860  }
3861  }
3862 
3863  /*
3864  * If arrays contain same data (up to end of shorter one), apply
3865  * additional rules to sort by dimensionality. The relative significance
3866  * of the different bits of information is historical; mainly we just care
3867  * that we don't say "equal" for arrays of different dimensionality.
3868  */
3869  if (result == 0)
3870  {
3871  if (nitems1 != nitems2)
3872  result = (nitems1 < nitems2) ? -1 : 1;
3873  else if (ndims1 != ndims2)
3874  result = (ndims1 < ndims2) ? -1 : 1;
3875  else
3876  {
3877  for (i = 0; i < ndims1; i++)
3878  {
3879  if (dims1[i] != dims2[i])
3880  {
3881  result = (dims1[i] < dims2[i]) ? -1 : 1;
3882  break;
3883  }
3884  }
3885  if (result == 0)
3886  {
3887  int *lbound1 = AARR_LBOUND(array1);
3888  int *lbound2 = AARR_LBOUND(array2);
3889 
3890  for (i = 0; i < ndims1; i++)
3891  {
3892  if (lbound1[i] != lbound2[i])
3893  {
3894  result = (lbound1[i] < lbound2[i]) ? -1 : 1;
3895  break;
3896  }
3897  }
3898  }
3899  }
3900  }
3901 
3902  /* Avoid leaking memory when handed toasted input. */
3903  AARR_FREE_IF_COPY(array1, 0);
3904  AARR_FREE_IF_COPY(array2, 1);
3905 
3906  return result;
3907 }
3908 
3909 
3910 /*-----------------------------------------------------------------------------
3911  * array hashing
3912  * Hash the elements and combine the results.
3913  *----------------------------------------------------------------------------
3914  */
3915 
3916 Datum
3918 {
3919  LOCAL_FCINFO(locfcinfo, 1);
3920  AnyArrayType *array = PG_GETARG_ANY_ARRAY_P(0);
3921  int ndims = AARR_NDIM(array);
3922  int *dims = AARR_DIMS(array);
3923  Oid element_type = AARR_ELEMTYPE(array);
3924  uint32 result = 1;
3925  int nitems;
3926  TypeCacheEntry *typentry;
3927  int typlen;
3928  bool typbyval;
3929  char typalign;
3930  int i;
3931  array_iter iter;
3932 
3933  /*
3934  * We arrange to look up the hash function only once per series of calls,
3935  * assuming the element type doesn't change underneath us. The typcache
3936  * is used so that we have no memory leakage when being used as an index
3937  * support function.
3938  */
3939  typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3940  if (typentry == NULL ||
3941  typentry->type_id != element_type)
3942  {
3943  typentry = lookup_type_cache(element_type,
3945  if (!OidIsValid(typentry->hash_proc_finfo.fn_oid))
3946  ereport(ERROR,
3947  (errcode(ERRCODE_UNDEFINED_FUNCTION),
3948  errmsg("could not identify a hash function for type %s",
3949  format_type_be(element_type))));
3950  fcinfo->flinfo->fn_extra = (void *) typentry;
3951  }
3952  typlen = typentry->typlen;
3953  typbyval = typentry->typbyval;
3954  typalign = typentry->typalign;
3955 
3956  /*
3957  * apply the hash function to each array element.
3958  */
3959  InitFunctionCallInfoData(*locfcinfo, &typentry->hash_proc_finfo, 1,
3960  PG_GET_COLLATION(), NULL, NULL);
3961 
3962  /* Loop over source data */
3963  nitems = ArrayGetNItems(ndims, dims);
3964  array_iter_setup(&iter, array);
3965 
3966  for (i = 0; i < nitems; i++)
3967  {
3968  Datum elt;
3969  bool isnull;
3970  uint32 elthash;
3971 
3972  /* Get element, checking for NULL */
3973  elt = array_iter_next(&iter, &isnull, i, typlen, typbyval, typalign);
3974 
3975  if (isnull)
3976  {
3977  /* Treat nulls as having hashvalue 0 */
3978  elthash = 0;
3979  }
3980  else
3981  {
3982  /* Apply the hash function */
3983  locfcinfo->args[0].value = elt;
3984  locfcinfo->args[0].isnull = false;
3985  locfcinfo->isnull = false;
3986  elthash = DatumGetUInt32(FunctionCallInvoke(locfcinfo));
3987  }
3988 
3989  /*
3990  * Combine hash values of successive elements by multiplying the
3991  * current value by 31 and adding on the new element's hash value.
3992  *
3993  * The result is a sum in which each element's hash value is
3994  * multiplied by a different power of 31. This is modulo 2^32
3995  * arithmetic, and the powers of 31 modulo 2^32 form a cyclic group of
3996  * order 2^27. So for arrays of up to 2^27 elements, each element's
3997  * hash value is multiplied by a different (odd) number, resulting in
3998  * a good mixing of all the elements' hash values.
3999  */
4000  result = (result << 5) - result + elthash;
4001  }
4002 
4003  /* Avoid leaking memory when handed toasted input. */
4004  AARR_FREE_IF_COPY(array, 0);
4005 
4006  PG_RETURN_UINT32(result);
4007 }
4008 
4009 /*
4010  * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
4011  * Otherwise, similar to hash_array.
4012  */
4013 Datum
4015 {
4016  LOCAL_FCINFO(locfcinfo, 2);
4017  AnyArrayType *array = PG_GETARG_ANY_ARRAY_P(0);
4018  uint64 seed = PG_GETARG_INT64(1);
4019  int ndims = AARR_NDIM(array);
4020  int *dims = AARR_DIMS(array);
4021  Oid element_type = AARR_ELEMTYPE(array);
4022  uint64 result = 1;
4023  int nitems;
4024  TypeCacheEntry *typentry;
4025  int typlen;
4026  bool typbyval;
4027  char typalign;
4028  int i;
4029  array_iter iter;
4030 
4031  typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
4032  if (typentry == NULL ||
4033  typentry->type_id != element_type)
4034  {
4035  typentry = lookup_type_cache(element_type,
4037  if (!OidIsValid(typentry->hash_extended_proc_finfo.fn_oid))
4038  ereport(ERROR,
4039  (errcode(ERRCODE_UNDEFINED_FUNCTION),
4040  errmsg("could not identify an extended hash function for type %s",
4041  format_type_be(element_type))));
4042  fcinfo->flinfo->fn_extra = (void *) typentry;
4043  }
4044  typlen = typentry->typlen;
4045  typbyval = typentry->typbyval;
4046  typalign = typentry->typalign;
4047 
4048  InitFunctionCallInfoData(*locfcinfo, &typentry->hash_extended_proc_finfo, 2,
4049  InvalidOid, NULL, NULL);
4050 
4051  /* Loop over source data */
4052  nitems = ArrayGetNItems(ndims, dims);
4053  array_iter_setup(&iter, array);
4054 
4055  for (i = 0; i < nitems; i++)
4056  {
4057  Datum elt;
4058  bool isnull;
4059  uint64 elthash;
4060 
4061  /* Get element, checking for NULL */
4062  elt = array_iter_next(&iter, &isnull, i, typlen, typbyval, typalign);
4063 
4064  if (isnull)
4065  {
4066  elthash = 0;
4067  }
4068  else
4069  {
4070  /* Apply the hash function */
4071  locfcinfo->args[0].value = elt;
4072  locfcinfo->args[0].isnull = false;
4073  locfcinfo->args[1].value = Int64GetDatum(seed);
4074  locfcinfo->args[1].isnull = false;
4075  elthash = DatumGetUInt64(FunctionCallInvoke(locfcinfo));
4076  }
4077 
4078  result = (result << 5) - result + elthash;
4079  }
4080 
4081  AARR_FREE_IF_COPY(array, 0);
4082 
4083  PG_RETURN_UINT64(result);
4084 }
4085 
4086 
4087 /*-----------------------------------------------------------------------------
4088  * array overlap/containment comparisons
4089  * These use the same methods of comparing array elements as array_eq.
4090  * We consider only the elements of the arrays, ignoring dimensionality.
4091  *----------------------------------------------------------------------------
4092  */
4093 
4094 /*
4095  * array_contain_compare :
4096  * compares two arrays for overlap/containment
4097  *
4098  * When matchall is true, return true if all members of array1 are in array2.
4099  * When matchall is false, return true if any members of array1 are in array2.
4100  */
4101 static bool
4103  bool matchall, void **fn_extra)
4104 {
4105  LOCAL_FCINFO(locfcinfo, 2);
4106  bool result = matchall;
4107  Oid element_type = AARR_ELEMTYPE(array1);
4108  TypeCacheEntry *typentry;
4109  int nelems1;
4110  Datum *values2;
4111  bool *nulls2;
4112  int nelems2;
4113  int typlen;
4114  bool typbyval;
4115  char typalign;
4116  int i;
4117  int j;
4118  array_iter it1;
4119 
4120  if (element_type != AARR_ELEMTYPE(array2))
4121  ereport(ERROR,
4122  (errcode(ERRCODE_DATATYPE_MISMATCH),
4123  errmsg("cannot compare arrays of different element types")));
4124 
4125  /*
4126  * We arrange to look up the equality function only once per series of
4127  * calls, assuming the element type doesn't change underneath us. The
4128  * typcache is used so that we have no memory leakage when being used as
4129  * an index support function.
4130  */
4131  typentry = (TypeCacheEntry *) *fn_extra;
4132  if (typentry == NULL ||
4133  typentry->type_id != element_type)
4134  {
4135  typentry = lookup_type_cache(element_type,
4137  if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
4138  ereport(ERROR,
4139  (errcode(ERRCODE_UNDEFINED_FUNCTION),
4140  errmsg("could not identify an equality operator for type %s",
4141  format_type_be(element_type))));
4142  *fn_extra = (void *) typentry;
4143  }
4144  typlen = typentry->typlen;
4145  typbyval = typentry->typbyval;
4146  typalign = typentry->typalign;
4147 
4148  /*
4149  * Since we probably will need to scan array2 multiple times, it's
4150  * worthwhile to use deconstruct_array on it. We scan array1 the hard way
4151  * however, since we very likely won't need to look at all of it.
4152  */
4153  if (VARATT_IS_EXPANDED_HEADER(array2))
4154  {
4155  /* This should be safe even if input is read-only */
4156  deconstruct_expanded_array(&(array2->xpn));
4157  values2 = array2->xpn.dvalues;
4158  nulls2 = array2->xpn.dnulls;
4159  nelems2 = array2->xpn.nelems;
4160  }
4161  else
4162  deconstruct_array((ArrayType *) array2,
4163  element_type, typlen, typbyval, typalign,
4164  &values2, &nulls2, &nelems2);
4165 
4166  /*
4167  * Apply the comparison operator to each pair of array elements.
4168  */
4169  InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
4170  collation, NULL, NULL);
4171 
4172  /* Loop over source data */
4173  nelems1 = ArrayGetNItems(AARR_NDIM(array1), AARR_DIMS(array1));
4174  array_iter_setup(&it1, array1);
4175 
4176  for (i = 0; i < nelems1; i++)
4177  {
4178  Datum elt1;
4179  bool isnull1;
4180 
4181  /* Get element, checking for NULL */
4182  elt1 = array_iter_next(&it1, &isnull1, i, typlen, typbyval, typalign);
4183 
4184  /*
4185  * We assume that the comparison operator is strict, so a NULL can't
4186  * match anything. XXX this diverges from the "NULL=NULL" behavior of
4187  * array_eq, should we act like that?
4188  */
4189  if (isnull1)
4190  {
4191  if (matchall)
4192  {
4193  result = false;
4194  break;
4195  }
4196  continue;
4197  }
4198 
4199  for (j = 0; j < nelems2; j++)
4200  {
4201  Datum elt2 = values2[j];
4202  bool isnull2 = nulls2 ? nulls2[j] : false;
4203  bool oprresult;
4204 
4205  if (isnull2)
4206  continue; /* can't match */
4207 
4208  /*
4209  * Apply the operator to the element pair
4210  */
4211  locfcinfo->args[0].value = elt1;
4212  locfcinfo->args[0].isnull = false;
4213  locfcinfo->args[1].value = elt2;
4214  locfcinfo->args[1].isnull = false;
4215  locfcinfo->isnull = false;
4216  oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
4217  if (oprresult)
4218  break;
4219  }
4220 
4221  if (j < nelems2)
4222  {
4223  /* found a match for elt1 */
4224  if (!matchall)
4225  {
4226  result = true;
4227  break;
4228  }
4229  }
4230  else
4231  {
4232  /* no match for elt1 */
4233  if (matchall)
4234  {
4235  result = false;
4236  break;
4237  }
4238  }
4239  }
4240 
4241  return result;
4242 }
4243 
4244 Datum
4246 {
4247  AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
4248  AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
4249  Oid collation = PG_GET_COLLATION();
4250  bool result;
4251 
4252  result = array_contain_compare(array1, array2, collation, false,
4253  &fcinfo->flinfo->fn_extra);
4254 
4255  /* Avoid leaking memory when handed toasted input. */
4256  AARR_FREE_IF_COPY(array1, 0);
4257  AARR_FREE_IF_COPY(array2, 1);
4258 
4259  PG_RETURN_BOOL(result);
4260 }
4261 
4262 Datum
4264 {
4265  AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
4266  AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
4267  Oid collation = PG_GET_COLLATION();
4268  bool result;
4269 
4270  result = array_contain_compare(array2, array1, collation, true,
4271  &fcinfo->flinfo->fn_extra);
4272 
4273  /* Avoid leaking memory when handed toasted input. */
4274  AARR_FREE_IF_COPY(array1, 0);
4275  AARR_FREE_IF_COPY(array2, 1);
4276 
4277  PG_RETURN_BOOL(result);
4278 }
4279 
4280 Datum
4282 {
4283  AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
4284  AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
4285  Oid collation = PG_GET_COLLATION();
4286  bool result;
4287 
4288  result = array_contain_compare(array1, array2, collation, true,
4289  &fcinfo->flinfo->fn_extra);
4290 
4291  /* Avoid leaking memory when handed toasted input. */
4292  AARR_FREE_IF_COPY(array1, 0);
4293  AARR_FREE_IF_COPY(array2, 1);
4294 
4295  PG_RETURN_BOOL(result);
4296 }
4297 
4298 
4299 /*-----------------------------------------------------------------------------
4300  * Array iteration functions
4301  * These functions are used to iterate efficiently through arrays
4302  *-----------------------------------------------------------------------------
4303  */
4304 
4305 /*
4306  * array_create_iterator --- set up to iterate through an array
4307  *
4308  * If slice_ndim is zero, we will iterate element-by-element; the returned
4309  * datums are of the array's element type.
4310  *
4311  * If slice_ndim is 1..ARR_NDIM(arr), we will iterate by slices: the
4312  * returned datums are of the same array type as 'arr', but of size
4313  * equal to the rightmost N dimensions of 'arr'.
4314  *
4315  * The passed-in array must remain valid for the lifetime of the iterator.
4316  */
4319 {
4320  ArrayIterator iterator = palloc0(sizeof(ArrayIteratorData));
4321 
4322  /*
4323  * Sanity-check inputs --- caller should have got this right already
4324  */
4325  Assert(PointerIsValid(arr));
4326  if (slice_ndim < 0 || slice_ndim > ARR_NDIM(arr))
4327  elog(ERROR, "invalid arguments to array_create_iterator");
4328 
4329  /*
4330  * Remember basic info about the array and its element type
4331  */
4332  iterator->arr = arr;
4333  iterator->nullbitmap = ARR_NULLBITMAP(arr);
4334  iterator->nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
4335 
4336  if (mstate != NULL)
4337  {
4338  Assert(mstate->element_type == ARR_ELEMTYPE(arr));
4339 
4340  iterator->typlen = mstate->typlen;
4341  iterator->typbyval = mstate->typbyval;
4342  iterator->typalign = mstate->typalign;
4343  }
4344  else
4346  &iterator->typlen,
4347  &iterator->typbyval,
4348  &iterator->typalign);
4349 
4350  /*
4351  * Remember the slicing parameters.
4352  */
4353  iterator->slice_ndim = slice_ndim;
4354 
4355  if (slice_ndim > 0)
4356  {
4357  /*
4358  * Get pointers into the array's dims and lbound arrays to represent
4359  * the dims/lbound arrays of a slice. These are the same as the
4360  * rightmost N dimensions of the array.
4361  */
4362  iterator->slice_dims = ARR_DIMS(arr) + ARR_NDIM(arr) - slice_ndim;
4363  iterator->slice_lbound = ARR_LBOUND(arr) + ARR_NDIM(arr) - slice_ndim;
4364 
4365  /*
4366  * Compute number of elements in a slice.
4367  */
4368  iterator->slice_len = ArrayGetNItems(slice_ndim,
4369  iterator->slice_dims);
4370 
4371  /*
4372  * Create workspace for building sub-arrays.
4373  */
4374  iterator->slice_values = (Datum *)
4375  palloc(iterator->slice_len * sizeof(Datum));
4376  iterator->slice_nulls = (bool *)
4377  palloc(iterator->slice_len * sizeof(bool));
4378  }
4379 
4380  /*
4381  * Initialize our data pointer and linear element number. These will
4382  * advance through the array during array_iterate().
4383  */
4384  iterator->data_ptr = ARR_DATA_PTR(arr);
4385  iterator->current_item = 0;
4386 
4387  return iterator;
4388 }
4389 
4390 /*
4391  * Iterate through the array referenced by 'iterator'.
4392  *
4393  * As long as there is another element (or slice), return it into
4394  * *value / *isnull, and return true. Return false when no more data.
4395  */
4396 bool
4397 array_iterate(ArrayIterator iterator, Datum *value, bool *isnull)
4398 {
4399  /* Done if we have reached the end of the array */
4400  if (iterator->current_item >= iterator->nitems)
4401  return false;
4402 
4403  if (iterator->slice_ndim == 0)
4404  {
4405  /*
4406  * Scalar case: return one element.
4407  */
4408  if (array_get_isnull(iterator->nullbitmap, iterator->current_item++))
4409  {
4410  *isnull = true;
4411  *value = (Datum) 0;
4412  }
4413  else
4414  {
4415  /* non-NULL, so fetch the individual Datum to return */
4416  char *p = iterator->data_ptr;
4417 
4418  *isnull = false;
4419  *value = fetch_att(p, iterator->typbyval, iterator->typlen);
4420 
4421  /* Move our data pointer forward to the next element */
4422  p = att_addlength_pointer(p, iterator->typlen, p);
4423  p = (char *) att_align_nominal(p, iterator->typalign);
4424  iterator->data_ptr = p;
4425  }
4426  }
4427  else
4428  {
4429  /*
4430  * Slice case: build and return an array of the requested size.
4431  */
4432  ArrayType *result;
4433  Datum *values = iterator->slice_values;
4434  bool *nulls = iterator->slice_nulls;
4435  char *p = iterator->data_ptr;
4436  int i;
4437 
4438  for (i = 0; i < iterator->slice_len; i++)
4439  {
4440  if (array_get_isnull(iterator->nullbitmap,
4441  iterator->current_item++))
4442  {
4443  nulls[i] = true;
4444  values[i] = (Datum) 0;
4445  }
4446  else
4447  {
4448  nulls[i] = false;
4449  values[i] = fetch_att(p, iterator->typbyval, iterator->typlen);
4450 
4451  /* Move our data pointer forward to the next element */
4452  p = att_addlength_pointer(p, iterator->typlen, p);
4453  p = (char *) att_align_nominal(p, iterator->typalign);
4454  }
4455  }
4456 
4457  iterator->data_ptr = p;
4458 
4459  result = construct_md_array(values,
4460  nulls,
4461  iterator->slice_ndim,
4462  iterator->slice_dims,
4463  iterator->slice_lbound,
4464  ARR_ELEMTYPE(iterator->arr),
4465  iterator->typlen,
4466  iterator->typbyval,
4467  iterator->typalign);
4468 
4469  *isnull = false;
4470  *value = PointerGetDatum(result);
4471  }
4472 
4473  return true;
4474 }
4475 
4476 /*
4477  * Release an ArrayIterator data structure
4478  */
4479 void
4481 {
4482  if (iterator->slice_ndim > 0)
4483  {
4484  pfree(iterator->slice_values);
4485  pfree(iterator->slice_nulls);
4486  }
4487  pfree(iterator);
4488 }
4489 
4490 
4491 /***************************************************************************/
4492 /******************| Support Routines |*****************/
4493 /***************************************************************************/
4494 
4495 /*
4496  * Check whether a specific array element is NULL
4497  *
4498  * nullbitmap: pointer to array's null bitmap (NULL if none)
4499  * offset: 0-based linear element number of array element
4500  */
4501 static bool
4502 array_get_isnull(const bits8 *nullbitmap, int offset)
4503 {
4504  if (nullbitmap == NULL)
4505  return false; /* assume not null */
4506  if (nullbitmap[offset / 8] & (1 << (offset % 8)))
4507  return false; /* not null */
4508  return true;
4509 }
4510 
4511 /*
4512  * Set a specific array element's null-bitmap entry
4513  *
4514  * nullbitmap: pointer to array's null bitmap (mustn't be NULL)
4515  * offset: 0-based linear element number of array element
4516  * isNull: null status to set
4517  */
4518 static void
4519 array_set_isnull(bits8 *nullbitmap, int offset, bool isNull)
4520 {
4521  int bitmask;
4522 
4523  nullbitmap += offset / 8;
4524  bitmask = 1 << (offset % 8);
4525  if (isNull)
4526  *nullbitmap &= ~bitmask;
4527  else
4528  *nullbitmap |= bitmask;
4529 }
4530 
4531 /*
4532  * Fetch array element at pointer, converted correctly to a Datum
4533  *
4534  * Caller must have handled case of NULL element
4535  */
4536 static Datum
4537 ArrayCast(char *value, bool byval, int len)
4538 {
4539  return fetch_att(value, byval, len);
4540 }
4541 
4542 /*
4543  * Copy datum to *dest and return total space used (including align padding)
4544  *
4545  * Caller must have handled case of NULL element
4546  */
4547 static int
4549  int typlen,
4550  bool typbyval,
4551  char typalign,
4552  char *dest)
4553 {
4554  int inc;
4555 
4556  if (typlen > 0)
4557  {
4558  if (typbyval)
4559  store_att_byval(dest, src, typlen);
4560  else
4561  memmove(dest, DatumGetPointer(src), typlen);
4562  inc = att_align_nominal(typlen, typalign);
4563  }
4564  else
4565  {
4566  Assert(!typbyval);
4567  inc = att_addlength_datum(0, typlen, src);
4568  memmove(dest, DatumGetPointer(src), inc);
4569  inc = att_align_nominal(inc, typalign);
4570  }
4571 
4572  return inc;
4573 }
4574 
4575 /*
4576  * Advance ptr over nitems array elements
4577  *
4578  * ptr: starting location in array
4579  * offset: 0-based linear element number of first element (the one at *ptr)
4580  * nullbitmap: start of array's null bitmap, or NULL if none
4581  * nitems: number of array elements to advance over (>= 0)
4582  * typlen, typbyval, typalign: storage parameters of array element datatype
4583  *
4584  * It is caller's responsibility to ensure that nitems is within range
4585  */
4586 static char *
4587 array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
4588  int typlen, bool typbyval, char typalign)
4589 {
4590  int bitmask;
4591  int i;
4592 
4593  /* easy if fixed-size elements and no NULLs */
4594  if (typlen > 0 && !nullbitmap)
4595  return ptr + nitems * ((Size) att_align_nominal(typlen, typalign));
4596 
4597  /* seems worth having separate loops for NULL and no-NULLs cases */
4598  if (nullbitmap)
4599  {
4600  nullbitmap += offset / 8;
4601  bitmask = 1 << (offset % 8);
4602 
4603  for (i = 0; i < nitems; i++)
4604  {
4605  if (*nullbitmap & bitmask)
4606  {
4607  ptr = att_addlength_pointer(ptr, typlen, ptr);
4608  ptr = (char *) att_align_nominal(ptr, typalign);
4609  }
4610  bitmask <<= 1;
4611  if (bitmask == 0x100)
4612  {
4613  nullbitmap++;
4614  bitmask = 1;
4615  }
4616  }
4617  }
4618  else
4619  {
4620  for (i = 0; i < nitems; i++)
4621  {
4622  ptr = att_addlength_pointer(ptr, typlen, ptr);
4623  ptr = (char *) att_align_nominal(ptr, typalign);
4624  }
4625  }
4626  return ptr;
4627 }
4628 
4629 /*
4630  * Compute total size of the nitems array elements starting at *ptr
4631  *
4632  * Parameters same as for array_seek
4633  */
4634 static int
4635 array_nelems_size(char *ptr, int offset, bits8 *nullbitmap, int nitems,
4636  int typlen, bool typbyval, char typalign)
4637 {
4638  return array_seek(ptr, offset, nullbitmap, nitems,
4639  typlen, typbyval, typalign) - ptr;
4640 }
4641 
4642 /*
4643  * Copy nitems array elements from srcptr to destptr
4644  *
4645  * destptr: starting destination location (must be enough room!)
4646  * nitems: number of array elements to copy (>= 0)
4647  * srcptr: starting location in source array
4648  * offset: 0-based linear element number of first element (the one at *srcptr)
4649  * nullbitmap: start of source array's null bitmap, or NULL if none
4650  * typlen, typbyval, typalign: storage parameters of array element datatype
4651  *
4652  * Returns number of bytes copied
4653  *
4654  * NB: this does not take care of setting up the destination's null bitmap!
4655  */
4656 static int
4657 array_copy(char *destptr, int nitems,
4658  char *srcptr, int offset, bits8 *nullbitmap,
4659  int typlen, bool typbyval, char typalign)
4660 {
4661  int numbytes;
4662 
4663  numbytes = array_nelems_size(srcptr, offset, nullbitmap, nitems,
4664  typlen, typbyval, typalign);
4665  memcpy(destptr, srcptr, numbytes);
4666  return numbytes;
4667 }
4668 
4669 /*
4670  * Copy nitems null-bitmap bits from source to destination
4671  *
4672  * destbitmap: start of destination array's null bitmap (mustn't be NULL)
4673  * destoffset: 0-based linear element number of first dest element
4674  * srcbitmap: start of source array's null bitmap, or NULL if none
4675  * srcoffset: 0-based linear element number of first source element
4676  * nitems: number of bits to copy (>= 0)
4677  *
4678  * If srcbitmap is NULL then we assume the source is all-non-NULL and
4679  * fill 1's into the destination bitmap. Note that only the specified
4680  * bits in the destination map are changed, not any before or after.
4681  *
4682  * Note: this could certainly be optimized using standard bitblt methods.
4683  * However, it's not clear that the typical Postgres array has enough elements
4684  * to make it worth worrying too much. For the moment, KISS.
4685  */
4686 void
4687 array_bitmap_copy(bits8 *destbitmap, int destoffset,
4688  const bits8 *srcbitmap, int srcoffset,
4689  int nitems)
4690 {
4691  int destbitmask,
4692  destbitval,
4693  srcbitmask,
4694  srcbitval;
4695 
4696  Assert(destbitmap);
4697  if (nitems <= 0)
4698  return; /* don't risk fetch off end of memory */
4699  destbitmap += destoffset / 8;
4700  destbitmask = 1 << (destoffset % 8);
4701  destbitval = *destbitmap;
4702  if (srcbitmap)
4703  {
4704  srcbitmap += srcoffset / 8;
4705  srcbitmask = 1 << (srcoffset % 8);
4706  srcbitval = *srcbitmap;
4707  while (nitems-- > 0)
4708  {
4709  if (srcbitval & srcbitmask)
4710  destbitval |= destbitmask;
4711  else
4712  destbitval &= ~destbitmask;
4713  destbitmask <<= 1;
4714  if (destbitmask == 0x100)
4715  {
4716  *destbitmap++ = destbitval;
4717  destbitmask = 1;
4718  if (nitems > 0)
4719  destbitval = *destbitmap;
4720  }
4721  srcbitmask <<= 1;
4722  if (srcbitmask == 0x100)
4723  {
4724  srcbitmap++;
4725  srcbitmask = 1;
4726  if (nitems > 0)
4727  srcbitval = *srcbitmap;
4728  }
4729  }
4730  if (destbitmask != 1)
4731  *destbitmap = destbitval;
4732  }
4733  else
4734  {
4735  while (nitems-- > 0)
4736  {
4737  destbitval |= destbitmask;
4738  destbitmask <<= 1;
4739  if (destbitmask == 0x100)
4740  {
4741  *destbitmap++ = destbitval;
4742  destbitmask = 1;
4743  if (nitems > 0)
4744  destbitval = *destbitmap;
4745  }
4746  }
4747  if (destbitmask != 1)
4748  *destbitmap = destbitval;
4749  }
4750 }
4751 
4752 /*
4753  * Compute space needed for a slice of an array
4754  *
4755  * We assume the caller has verified that the slice coordinates are valid.
4756  */
4757 static int
4758 array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
4759  int ndim, int *dim, int *lb,
4760  int *st, int *endp,
4761  int typlen, bool typbyval, char typalign)
4762 {
4763  int src_offset,
4764  span[MAXDIM],
4765  prod[MAXDIM],
4766  dist[MAXDIM],
4767  indx[MAXDIM];
4768  char *ptr;
4769  int i,
4770  j,
4771  inc;
4772  int count = 0;
4773 
4774  mda_get_range(ndim, span, st, endp);
4775 
4776  /* Pretty easy for fixed element length without nulls ... */
4777  if (typlen > 0 && !arraynullsptr)
4778  return ArrayGetNItems(ndim, span) * att_align_nominal(typlen, typalign);
4779 
4780  /* Else gotta do it the hard way */
4781  src_offset = ArrayGetOffset(ndim, dim, lb, st);
4782  ptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
4783  typlen, typbyval, typalign);
4784  mda_get_prod(ndim, dim, prod);
4785  mda_get_offset_values(ndim, dist, prod, span);
4786  for (i = 0; i < ndim; i++)
4787  indx[i] = 0;
4788  j = ndim - 1;
4789  do
4790  {
4791  if (dist[j])
4792  {
4793  ptr = array_seek(ptr, src_offset, arraynullsptr, dist[j],
4794  typlen, typbyval, typalign);
4795  src_offset += dist[j];
4796  }
4797  if (!array_get_isnull(arraynullsptr, src_offset))
4798  {
4799  inc = att_addlength_pointer(0, typlen, ptr);
4800  inc = att_align_nominal(inc, typalign);
4801  ptr += inc;
4802  count += inc;
4803  }
4804  src_offset++;
4805  } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
4806  return count;
4807 }
4808 
4809 /*
4810  * Extract a slice of an array into consecutive elements in the destination
4811  * array.
4812  *
4813  * We assume the caller has verified that the slice coordinates are valid,
4814  * allocated enough storage for the result, and initialized the header
4815  * of the new array.
4816  */
4817 static void
4819  int ndim,
4820  int *dim,
4821  int *lb,
4822  char *arraydataptr,
4823  bits8 *arraynullsptr,
4824  int *st,
4825  int *endp,
4826  int typlen,
4827  bool typbyval,
4828  char typalign)
4829 {
4830  char *destdataptr = ARR_DATA_PTR(newarray);
4831  bits8 *destnullsptr = ARR_NULLBITMAP(newarray);
4832  char *srcdataptr;
4833  int src_offset,
4834  dest_offset,
4835  prod[MAXDIM],
4836  span[MAXDIM],
4837  dist[MAXDIM],
4838  indx[MAXDIM];
4839  int i,
4840  j,
4841  inc;
4842 
4843  src_offset = ArrayGetOffset(ndim, dim, lb, st);
4844  srcdataptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
4845  typlen, typbyval, typalign);
4846  mda_get_prod(ndim, dim, prod);
4847  mda_get_range(ndim, span, st, endp);
4848  mda_get_offset_values(ndim, dist, prod, span);
4849  for (i = 0; i < ndim; i++)
4850  indx[i] = 0;
4851  dest_offset = 0;
4852  j = ndim - 1;
4853  do
4854  {
4855  if (dist[j])
4856  {
4857  /* skip unwanted elements */
4858  srcdataptr = array_seek(srcdataptr, src_offset, arraynullsptr,
4859  dist[j],
4860  typlen, typbyval, typalign);
4861  src_offset += dist[j];
4862  }
4863  inc = array_copy(destdataptr, 1,
4864  srcdataptr, src_offset, arraynullsptr,
4865  typlen, typbyval, typalign);
4866  if (destnullsptr)
4867  array_bitmap_copy(destnullsptr, dest_offset,
4868  arraynullsptr, src_offset,
4869  1);
4870  destdataptr += inc;
4871  srcdataptr += inc;
4872  src_offset++;
4873  dest_offset++;
4874  } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
4875 }
4876 
4877 /*
4878  * Insert a slice into an array.
4879  *
4880  * ndim/dim[]/lb[] are dimensions of the original array. A new array with
4881  * those same dimensions is to be constructed. destArray must already
4882  * have been allocated and its header initialized.
4883  *
4884  * st[]/endp[] identify the slice to be replaced. Elements within the slice
4885  * volume are taken from consecutive elements of the srcArray; elements
4886  * outside it are copied from origArray.
4887  *
4888  * We assume the caller has verified that the slice coordinates are valid.
4889  */
4890 static void
4892  ArrayType *origArray,
4893  ArrayType *srcArray,
4894  int ndim,
4895  int *dim,
4896  int *lb,
4897  int *st,
4898  int *endp,
4899  int typlen,
4900  bool typbyval,
4901  char typalign)
4902 {
4903  char *destPtr = ARR_DATA_PTR(destArray);
4904  char *origPtr = ARR_DATA_PTR(origArray);
4905  char *srcPtr = ARR_DATA_PTR(srcArray);
4906  bits8 *destBitmap = ARR_NULLBITMAP(destArray);
4907  bits8 *origBitmap = ARR_NULLBITMAP(origArray);
4908  bits8 *srcBitmap = ARR_NULLBITMAP(srcArray);
4909  int orignitems = ArrayGetNItems(ARR_NDIM(origArray),
4910  ARR_DIMS(origArray));
4911  int dest_offset,
4912  orig_offset,
4913  src_offset,
4914  prod[MAXDIM],
4915  span[MAXDIM],
4916  dist[MAXDIM],
4917  indx[MAXDIM];
4918  int i,
4919  j,
4920  inc;
4921 
4922  dest_offset = ArrayGetOffset(ndim, dim, lb, st);
4923  /* copy items before the slice start */
4924  inc = array_copy(destPtr, dest_offset,
4925  origPtr, 0, origBitmap,
4926  typlen, typbyval, typalign);
4927  destPtr += inc;
4928  origPtr += inc;
4929  if (destBitmap)
4930  array_bitmap_copy(destBitmap, 0, origBitmap, 0, dest_offset);
4931  orig_offset = dest_offset;
4932  mda_get_prod(ndim, dim, prod);
4933  mda_get_range(ndim, span, st, endp);
4934  mda_get_offset_values(ndim, dist, prod, span);
4935  for (i = 0; i < ndim; i++)
4936  indx[i] = 0;
4937  src_offset = 0;
4938  j = ndim - 1;
4939  do
4940  {
4941  /* Copy/advance over elements between here and next part of slice */
4942  if (dist[j])
4943  {
4944  inc = array_copy(destPtr, dist[j],
4945  origPtr, orig_offset, origBitmap,
4946  typlen, typbyval, typalign);
4947  destPtr += inc;
4948  origPtr += inc;
4949  if (destBitmap)
4950  array_bitmap_copy(destBitmap, dest_offset,
4951  origBitmap, orig_offset,
4952  dist[j]);
4953  dest_offset += dist[j];
4954  orig_offset += dist[j];
4955  }
4956  /* Copy new element at this slice position */
4957  inc = array_copy(destPtr, 1,
4958  srcPtr, src_offset, srcBitmap,
4959  typlen, typbyval, typalign);
4960  if (destBitmap)
4961  array_bitmap_copy(destBitmap, dest_offset,
4962  srcBitmap, src_offset,
4963  1);
4964  destPtr += inc;
4965  srcPtr += inc;
4966  dest_offset++;
4967  src_offset++;
4968  /* Advance over old element at this slice position */
4969  origPtr = array_seek(origPtr, orig_offset, origBitmap, 1,
4970  typlen, typbyval, typalign);
4971  orig_offset++;
4972  } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
4973 
4974  /* don't miss any data at the end */
4975  array_copy(destPtr, orignitems - orig_offset,
4976  origPtr, orig_offset, origBitmap,
4977  typlen, typbyval, typalign);
4978  if (destBitmap)
4979  array_bitmap_copy(destBitmap, dest_offset,
4980  origBitmap, orig_offset,
4981  orignitems - orig_offset);
4982 }
4983 
4984 /*
4985  * initArrayResult - initialize an empty ArrayBuildState
4986  *
4987  * element_type is the array element type (must be a valid array element type)
4988  * rcontext is where to keep working state
4989  * subcontext is a flag determining whether to use a separate memory context
4990  *
4991  * Note: there are two common schemes for using accumArrayResult().
4992  * In the older scheme, you start with a NULL ArrayBuildState pointer, and
4993  * call accumArrayResult once per element. In this scheme you end up with
4994  * a NULL pointer if there were no elements, which you need to special-case.
4995  * In the newer scheme, call initArrayResult and then call accumArrayResult
4996  * once per element. In this scheme you always end with a non-NULL pointer
4997  * that you can pass to makeArrayResult; you get an empty array if there
4998  * were no elements. This is preferred if an empty array is what you want.
4999  *
5000  * It's possible to choose whether to create a separate memory context for the
5001  * array build state, or whether to allocate it directly within rcontext.
5002  *
5003  * When there are many concurrent small states (e.g. array_agg() using hash
5004  * aggregation of many small groups), using a separate memory context for each
5005  * one may result in severe memory bloat. In such cases, use the same memory
5006  * context to initialize all such array build states, and pass
5007  * subcontext=false.
5008  *
5009  * In cases when the array build states have different lifetimes, using a
5010  * single memory context is impractical. Instead, pass subcontext=true so that
5011  * the array build states can be freed individually.
5012  */
5014 initArrayResult(Oid element_type, MemoryContext rcontext, bool subcontext)
5015 {
5016  ArrayBuildState *astate;
5017  MemoryContext arr_context = rcontext;
5018 
5019  /* Make a temporary context to hold all the junk */
5020  if (subcontext)
5021  arr_context = AllocSetContextCreate(rcontext,
5022  "accumArrayResult",
5024 
5025  astate = (ArrayBuildState *)
5026  MemoryContextAlloc(arr_context, sizeof(ArrayBuildState));
5027  astate->mcontext = arr_context;
5028  astate->private_cxt = subcontext;
5029  astate->alen = (subcontext ? 64 : 8); /* arbitrary starting array size */
5030  astate->dvalues = (Datum *)
5031  MemoryContextAlloc(arr_context, astate->alen * sizeof(Datum));
5032  astate->dnulls = (bool *)
5033  MemoryContextAlloc(arr_context, astate->alen * sizeof(bool));
5034  astate->nelems = 0;
5035  astate->element_type = element_type;
5036  get_typlenbyvalalign(element_type,
5037  &astate->typlen,
5038  &astate->typbyval,
5039  &astate->typalign);
5040 
5041  return astate;
5042 }
5043 
5044 /*
5045  * accumArrayResult - accumulate one (more) Datum for an array result
5046  *
5047  * astate is working state (can be NULL on first call)
5048  * dvalue/disnull represent the new Datum to append to the array
5049  * element_type is the Datum's type (must be a valid array element type)
5050  * rcontext is where to keep working state
5051  */
5054  Datum dvalue, bool disnull,
5055  Oid element_type,
5056  MemoryContext rcontext)
5057 {
5058  MemoryContext oldcontext;
5059 
5060  if (astate == NULL)
5061  {
5062  /* First time through --- initialize */
5063  astate = initArrayResult(element_type, rcontext, true);
5064  }
5065  else
5066  {
5067  Assert(astate->element_type == element_type);
5068  }
5069 
5070  oldcontext = MemoryContextSwitchTo(astate->mcontext);
5071 
5072  /* enlarge dvalues[]/dnulls[] if needed */
5073  if (astate->nelems >= astate->alen)
5074  {
5075  astate->alen *= 2;
5076  astate->dvalues = (Datum *)
5077  repalloc(astate->dvalues, astate->alen * sizeof(Datum));
5078  astate->dnulls = (bool *)
5079  repalloc(astate->dnulls, astate->alen * sizeof(bool));
5080  }
5081 
5082  /*
5083  * Ensure pass-by-ref stuff is copied into mcontext; and detoast it too if
5084  * it's varlena. (You might think that detoasting is not needed here
5085  * because construct_md_array can detoast the array elements later.
5086  * However, we must not let construct_md_array modify the ArrayBuildState
5087  * because that would mean array_agg_finalfn damages its input, which is
5088  * verboten. Also, this way frequently saves one copying step.)
5089  */
5090  if (!disnull && !astate->typbyval)
5091  {
5092  if (astate->typlen == -1)
5093  dvalue = PointerGetDatum(PG_DETOAST_DATUM_COPY(dvalue));
5094  else
5095  dvalue = datumCopy(dvalue, astate->typbyval, astate->typlen);
5096  }
5097 
5098  astate->dvalues[astate->nelems] = dvalue;
5099  astate->dnulls[astate->nelems] = disnull;
5100  astate->nelems++;
5101 
5102  MemoryContextSwitchTo(oldcontext);
5103 
5104  return astate;
5105 }
5106 
5107 /*
5108  * makeArrayResult - produce 1-D final result of accumArrayResult
5109  *
5110  * Note: only releases astate if it was initialized within a separate memory
5111  * context (i.e. using subcontext=true when calling initArrayResult).
5112  *
5113  * astate is working state (must not be NULL)
5114  * rcontext is where to construct result
5115  */
5116 Datum
5118  MemoryContext rcontext)
5119 {
5120  int ndims;
5121  int dims[1];
5122  int lbs[1];
5123 
5124  /* If no elements were presented, we want to create an empty array */
5125  ndims = (astate->nelems > 0) ? 1 : 0;
5126  dims[0] = astate->nelems;
5127  lbs[0] = 1;
5128 
5129  return makeMdArrayResult(astate, ndims, dims, lbs, rcontext,
5130  astate->private_cxt);
5131 }
5132 
5133 /*
5134  * makeMdArrayResult - produce multi-D final result of accumArrayResult
5135  *
5136  * beware: no check that specified dimensions match the number of values
5137  * accumulated.
5138  *
5139  * Note: if the astate was not initialized within a separate memory context
5140  * (that is, initArrayResult was called with subcontext=false), then using
5141  * release=true is illegal. Instead, release astate along with the rest of its
5142  * context when appropriate.
5143  *
5144  * astate is working state (must not be NULL)
5145  * rcontext is where to construct result
5146  * release is true if okay to release working state
5147  */
5148 Datum
5150  int ndims,
5151  int *dims,
5152  int *lbs,
5153  MemoryContext rcontext,
5154  bool release)
5155 {
5156  ArrayType *result;
5157  MemoryContext oldcontext;
5158 
5159  /* Build the final array result in rcontext */
5160  oldcontext = MemoryContextSwitchTo(rcontext);
5161 
5162  result = construct_md_array(astate->dvalues,
5163  astate->dnulls,
5164  ndims,
5165  dims,
5166  lbs,
5167  astate->element_type,
5168  astate->typlen,
5169  astate->typbyval,
5170  astate->typalign);
5171 
5172  MemoryContextSwitchTo(oldcontext);
5173 
5174  /* Clean up all the junk */
5175  if (release)
5176  {
5177  Assert(astate->private_cxt);
5178  MemoryContextDelete(astate->mcontext);
5179  }
5180 
5181  return PointerGetDatum(result);
5182 }
5183 
5184 /*
5185  * The following three functions provide essentially the same API as
5186  * initArrayResult/accumArrayResult/makeArrayResult, but instead of accepting
5187  * inputs that are array elements, they accept inputs that are arrays and
5188  * produce an output array having N+1 dimensions. The inputs must all have
5189  * identical dimensionality as well as element type.
5190  */
5191 
5192 /*
5193  * initArrayResultArr - initialize an empty ArrayBuildStateArr
5194  *
5195  * array_type is the array type (must be a valid varlena array type)
5196  * element_type is the type of the array's elements (lookup if InvalidOid)
5197  * rcontext is where to keep working state
5198  * subcontext is a flag determining whether to use a separate memory context
5199  */
5201 initArrayResultArr(Oid array_type, Oid element_type, MemoryContext rcontext,
5202  bool subcontext)
5203 {
5204  ArrayBuildStateArr *astate;
5205  MemoryContext arr_context = rcontext; /* by default use the parent ctx */
5206 
5207  /* Lookup element type, unless element_type already provided */
5208  if (!OidIsValid(element_type))
5209  {
5210  element_type = get_element_type(array_type);
5211 
5212  if (!OidIsValid(element_type))
5213  ereport(ERROR,
5214  (errcode(ERRCODE_DATATYPE_MISMATCH),
5215  errmsg("data type %s is not an array type",
5216  format_type_be(array_type))));
5217  }
5218 
5219  /* Make a temporary context to hold all the junk */
5220  if (subcontext)
5221  arr_context = AllocSetContextCreate(rcontext,
5222  "accumArrayResultArr",
5224 
5225  /* Note we initialize all fields to zero */
5226  astate = (ArrayBuildStateArr *)
5227  MemoryContextAllocZero(arr_context, sizeof(ArrayBuildStateArr));
5228  astate->mcontext = arr_context;
5229  astate->private_cxt = subcontext;
5230 
5231  /* Save relevant datatype information */
5232  astate->array_type = array_type;
5233  astate->element_type = element_type;
5234 
5235  return astate;
5236 }
5237 
5238 /*
5239  * accumArrayResultArr - accumulate one (more) sub-array for an array result
5240  *
5241  * astate is working state (can be NULL on first call)
5242  * dvalue/disnull represent the new sub-array to append to the array
5243  * array_type is the array type (must be a valid varlena array type)
5244  * rcontext is where to keep working state
5245  */
5248  Datum dvalue, bool disnull,
5249  Oid array_type,
5250  MemoryContext rcontext)
5251 {
5252  ArrayType *arg;
5253  MemoryContext oldcontext;
5254  int *dims,
5255  *lbs,
5256  ndims,
5257  nitems,
5258  ndatabytes;
5259  char *data;
5260  int i;
5261 
5262  /*
5263  * We disallow accumulating null subarrays. Another plausible definition
5264  * is to ignore them, but callers that want that can just skip calling
5265  * this function.
5266  */
5267  if (disnull)
5268  ereport(ERROR,
5269  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5270  errmsg("cannot accumulate null arrays")));
5271 
5272  /* Detoast input array in caller's context */
5273  arg = DatumGetArrayTypeP(dvalue);
5274 
5275  if (astate == NULL)
5276  astate = initArrayResultArr(array_type, InvalidOid, rcontext, true);
5277  else
5278  Assert(astate->array_type == array_type);
5279 
5280  oldcontext = MemoryContextSwitchTo(astate->mcontext);
5281 
5282  /* Collect this input's dimensions */
5283  ndims = ARR_NDIM(arg);
5284  dims = ARR_DIMS(arg);
5285  lbs = ARR_LBOUND(arg);
5286  data = ARR_DATA_PTR(arg);
5287  nitems = ArrayGetNItems(ndims, dims);
5288  ndatabytes = ARR_SIZE(arg) - ARR_DATA_OFFSET(arg);
5289 
5290  if (astate->ndims == 0)
5291  {
5292  /* First input; check/save the dimensionality info */
5293 
5294  /* Should we allow empty inputs and just produce an empty output? */
5295  if (ndims == 0)
5296  ereport(ERROR,
5297  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5298  errmsg("cannot accumulate empty arrays")));
5299  if (ndims + 1 > MAXDIM)
5300  ereport(ERROR,
5301  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
5302  errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
5303  ndims + 1, MAXDIM)));
5304 
5305  /*
5306  * The output array will have n+1 dimensions, with the ones after the
5307  * first matching the input's dimensions.
5308  */
5309  astate->ndims = ndims + 1;
5310  astate->dims[0] = 0;
5311  memcpy(&astate->dims[1], dims, ndims * sizeof(int));
5312  astate->lbs[0] = 1;
5313  memcpy(&astate->lbs[1], lbs, ndims * sizeof(int));
5314 
5315  /* Allocate at least enough data space for this item */
5316  astate->abytes = 1024;
5317  while (astate->abytes <= ndatabytes)
5318  astate->abytes *= 2;
5319  astate->data = (char *) palloc(astate->abytes);
5320  }
5321  else
5322  {
5323  /* Second or later input: must match first input's dimensionality */
5324  if (astate->ndims != ndims + 1)
5325  ereport(ERROR,
5326  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5327  errmsg("cannot accumulate arrays of different dimensionality")));
5328  for (i = 0; i < ndims; i++)
5329  {
5330  if (astate->dims[i + 1] != dims[i] || astate->lbs[i + 1] != lbs[i])
5331  ereport(ERROR,
5332  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5333  errmsg("cannot accumulate arrays of different dimensionality")));
5334  }
5335 
5336  /* Enlarge data space if needed */
5337  if (astate->nbytes + ndatabytes > astate->abytes)
5338  {
5339  astate->abytes = Max(astate->abytes * 2,
5340  astate->nbytes + ndatabytes);
5341  astate->data = (char *) repalloc(astate->data, astate->abytes);
5342  }
5343  }
5344 
5345  /*
5346  * Copy the data portion of the sub-array. Note we assume that the
5347  * advertised data length of the sub-array is properly aligned. We do not
5348  * have to worry about detoasting elements since whatever's in the
5349  * sub-array should be OK already.
5350  */
5351  memcpy(astate->data + astate->nbytes, data, ndatabytes);
5352  astate->nbytes += ndatabytes;
5353 
5354  /* Deal with null bitmap if needed */
5355  if (astate->nullbitmap || ARR_HASNULL(arg))
5356  {
5357  int newnitems = astate->nitems + nitems;
5358 
5359  if (astate->nullbitmap == NULL)
5360  {
5361  /*
5362  * First input with nulls; we must retrospectively handle any
5363  * previous inputs by marking all their items non-null.
5364  */
5365  astate->aitems = 256;
5366  while (astate->aitems <= newnitems)
5367  astate->aitems *= 2;
5368  astate->nullbitmap = (bits8 *) palloc((astate->aitems + 7) / 8);
5369  array_bitmap_copy(astate->nullbitmap, 0,
5370  NULL, 0,
5371  astate->nitems);
5372  }
5373  else if (newnitems > astate->aitems)
5374  {
5375  astate->aitems = Max(astate->aitems * 2, newnitems);
5376  astate->nullbitmap = (bits8 *)
5377  repalloc(astate->nullbitmap, (astate->aitems + 7) / 8);
5378  }
5379  array_bitmap_copy(astate->nullbitmap, astate->nitems,
5380  ARR_NULLBITMAP(arg), 0,
5381  nitems);
5382  }
5383 
5384  astate->nitems += nitems;
5385  astate->dims[0] += 1;
5386 
5387  MemoryContextSwitchTo(oldcontext);
5388 
5389  /* Release detoasted copy if any */
5390  if ((Pointer) arg != DatumGetPointer(dvalue))
5391  pfree(arg);
5392 
5393  return astate;
5394 }
5395 
5396 /*
5397  * makeArrayResultArr - produce N+1-D final result of accumArrayResultArr
5398  *
5399  * astate is working state (must not be NULL)
5400  * rcontext is where to construct result
5401  * release is true if okay to release working state
5402  */
5403 Datum
5405  MemoryContext rcontext,
5406  bool release)
5407 {
5408  ArrayType *result;
5409  MemoryContext oldcontext;
5410 
5411  /* Build the final array result in rcontext */
5412  oldcontext = MemoryContextSwitchTo(rcontext);
5413 
5414  if (astate->ndims == 0)
5415  {
5416  /* No inputs, return empty array */
5417  result = construct_empty_array(astate->element_type);
5418  }
5419  else
5420  {
5421  int dataoffset,
5422  nbytes;
5423 
5424  /* Compute required space */
5425  nbytes = astate->nbytes;
5426  if (astate->nullbitmap != NULL)
5427  {
5428  dataoffset = ARR_OVERHEAD_WITHNULLS(astate->ndims, astate->nitems);
5429  nbytes += dataoffset;
5430  }
5431  else
5432  {
5433  dataoffset = 0;
5434  nbytes += ARR_OVERHEAD_NONULLS(astate->ndims);
5435  }
5436 
5437  result = (ArrayType *) palloc0(nbytes);
5438  SET_VARSIZE(result, nbytes);
5439  result->ndim = astate->ndims;
5440  result->dataoffset = dataoffset;
5441  result->elemtype = astate->element_type;
5442 
5443  memcpy(ARR_DIMS(result), astate->dims, astate->ndims * sizeof(int));
5444  memcpy(ARR_LBOUND(result), astate->lbs, astate->ndims * sizeof(int));
5445  memcpy(ARR_DATA_PTR(result), astate->data, astate->nbytes);
5446 
5447  if (astate->nullbitmap != NULL)
5448  array_bitmap_copy(ARR_NULLBITMAP(result), 0,
5449  astate->nullbitmap, 0,
5450  astate->nitems);
5451  }
5452 
5453  MemoryContextSwitchTo(oldcontext);
5454 
5455  /* Clean up all the junk */
5456  if (release)
5457  {
5458  Assert(astate->private_cxt);
5459  MemoryContextDelete(astate->mcontext);
5460  }
5461 
5462  return PointerGetDatum(result);
5463 }
5464 
5465 /*
5466  * The following three functions provide essentially the same API as
5467  * initArrayResult/accumArrayResult/makeArrayResult, but can accept either
5468  * scalar or array inputs, invoking the appropriate set of functions above.
5469  */
5470 
5471 /*
5472  * initArrayResultAny - initialize an empty ArrayBuildStateAny
5473  *
5474  * input_type is the input datatype (either element or array type)
5475  * rcontext is where to keep working state
5476  * subcontext is a flag determining whether to use a separate memory context
5477  */
5479 initArrayResultAny(Oid input_type, MemoryContext rcontext, bool subcontext)
5480 {
5481  ArrayBuildStateAny *astate;
5482  Oid element_type = get_element_type(input_type);
5483 
5484  if (OidIsValid(element_type))
5485  {
5486  /* Array case */
5487  ArrayBuildStateArr *arraystate;
5488 
5489  arraystate = initArrayResultArr(input_type, InvalidOid, rcontext, subcontext);
5490  astate = (ArrayBuildStateAny *)
5491  MemoryContextAlloc(arraystate->mcontext,
5492  sizeof(ArrayBuildStateAny));
5493  astate->scalarstate = NULL;
5494  astate->arraystate = arraystate;
5495  }
5496  else
5497  {
5498  /* Scalar case */
5499  ArrayBuildState *scalarstate;
5500 
5501  /* Let's just check that we have a type that can be put into arrays */
5502  Assert(OidIsValid(get_array_type(input_type)));
5503 
5504  scalarstate = initArrayResult(input_type, rcontext, subcontext);
5505  astate = (ArrayBuildStateAny *)
5506  MemoryContextAlloc(scalarstate->mcontext,
5507  sizeof(ArrayBuildStateAny));
5508  astate->scalarstate = scalarstate;
5509  astate->arraystate = NULL;
5510  }
5511 
5512  return astate;
5513 }
5514 
5515 /*
5516  * accumArrayResultAny - accumulate one (more) input for an array result
5517  *
5518  * astate is working state (can be NULL on first call)
5519  * dvalue/disnull represent the new input to append to the array
5520  * input_type is the input datatype (either element or array type)
5521  * rcontext is where to keep working state
5522  */
5525  Datum dvalue, bool disnull,
5526  Oid input_type,
5527  MemoryContext rcontext)
5528 {
5529  if (astate == NULL)
5530  astate = initArrayResultAny(input_type, rcontext, true);
5531 
5532  if (astate->scalarstate)
5533  (void) accumArrayResult(astate->scalarstate,
5534  dvalue, disnull,
5535  input_type, rcontext);
5536  else
5537  (void) accumArrayResultArr(astate->arraystate,
5538  dvalue, disnull,
5539  input_type, rcontext);
5540 
5541  return astate;
5542 }
5543 
5544 /*
5545  * makeArrayResultAny - produce final result of accumArrayResultAny
5546  *
5547  * astate is working state (must not be NULL)
5548  * rcontext is where to construct result
5549  * release is true if okay to release working state
5550  */
5551 Datum
5553  MemoryContext rcontext, bool release)
5554 {
5555  Datum result;
5556 
5557  if (astate->scalarstate)
5558  {
5559  /* Must use makeMdArrayResult to support "release" parameter */
5560  int ndims;
5561  int dims[1];
5562  int lbs[1];
5563 
5564  /* If no elements were presented, we want to create an empty array */
5565  ndims = (astate->scalarstate->nelems > 0) ? 1 : 0;
5566  dims[0] = astate->scalarstate->nelems;
5567  lbs[0] = 1;
5568 
5569  result = makeMdArrayResult(astate->scalarstate, ndims, dims, lbs,
5570  rcontext, release);
5571  }
5572  else
5573  {
5574  result = makeArrayResultArr(astate->arraystate,
5575  rcontext, release);
5576  }
5577  return result;
5578 }
5579 
5580 
5581 Datum
5583 {
5584  if (array_cmp(fcinfo) > 0)
5586  else
5588 }
5589 
5590 Datum
5592 {
5593  if (array_cmp(fcinfo) < 0)
5595  else
5597 }
5598 
5599 
5601 {
5604  bool reverse;
5606 
5607 /*
5608  * generate_subscripts(array anyarray, dim int [, reverse bool])
5609  * Returns all subscripts of the array for any dimension
5610  */
5611 Datum
5613 {
5614  FuncCallContext *funcctx;
5615  MemoryContext oldcontext;
5617 
5618  /* stuff done only on the first call of the function */
5619  if (SRF_IS_FIRSTCALL())
5620  {
5622  int reqdim = PG_GETARG_INT32(1);
5623  int *lb,
5624  *dimv;
5625 
5626  /* create a function context for cross-call persistence */
5627  funcctx = SRF_FIRSTCALL_INIT();
5628 
5629  /* Sanity check: does it look like an array at all? */
5630  if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
5631  SRF_RETURN_DONE(funcctx);
5632 
5633  /* Sanity check: was the requested dim valid */
5634  if (reqdim <= 0 || reqdim > AARR_NDIM(v))
5635  SRF_RETURN_DONE(funcctx);
5636 
5637  /*
5638  * switch to memory context appropriate for multiple function calls
5639  */
5640  oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
5642 
5643  lb = AARR_LBOUND(v);
5644  dimv = AARR_DIMS(v);
5645 
5646  fctx->lower = lb[reqdim - 1];
5647  fctx->upper = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
5648  fctx->reverse = (PG_NARGS() < 3) ? false : PG_GETARG_BOOL(2);
5649 
5650  funcctx->user_fctx = fctx;
5651 
5652  MemoryContextSwitchTo(oldcontext);
5653  }
5654 
5655  funcctx = SRF_PERCALL_SETUP();
5656 
5657  fctx = funcctx->user_fctx;
5658 
5659  if (fctx->lower <= fctx->upper)
5660  {
5661  if (!fctx->reverse)
5662  SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->lower++));
5663  else
5664  SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->upper--));
5665  }
5666  else
5667  /* done when there are no more elements left */
5668  SRF_RETURN_DONE(funcctx);
5669 }
5670 
5671 /*
5672  * generate_subscripts_nodir
5673  * Implements the 2-argument version of generate_subscripts
5674  */
5675 Datum
5677 {
5678  /* just call the other one -- it can handle both cases */
5679  return generate_subscripts(fcinfo);
5680 }
5681 
5682 /*
5683  * array_fill_with_lower_bounds
5684  * Create and fill array with defined lower bounds.
5685  */
5686 Datum
5688 {
5689  ArrayType *dims;
5690  ArrayType *lbs;
5691  ArrayType *result;
5692  Oid elmtype;
5693  Datum value;
5694  bool isnull;
5695 
5696  if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
5697  ereport(ERROR,
5698  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5699  errmsg("dimension array or low bound array cannot be null")));
5700 
5701  dims = PG_GETARG_ARRAYTYPE_P(1);
5702  lbs = PG_GETARG_ARRAYTYPE_P(2);
5703 
5704  if (!PG_ARGISNULL(0))
5705  {
5706  value = PG_GETARG_DATUM(0);
5707  isnull = false;
5708  }
5709  else
5710  {
5711  value = 0;
5712  isnull = true;
5713  }
5714 
5715  elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
5716  if (!OidIsValid(elmtype))
5717  elog(ERROR, "could not determine data type of input");
5718 
5719  result = array_fill_internal(dims, lbs, value, isnull, elmtype, fcinfo);
5720  PG_RETURN_ARRAYTYPE_P(result);
5721 }
5722 
5723 /*
5724  * array_fill
5725  * Create and fill array with default lower bounds.
5726  */
5727 Datum
5729 {
5730  ArrayType *dims;
5731  ArrayType *result;
5732  Oid elmtype;
5733  Datum value;
5734  bool isnull;
5735 
5736  if (PG_ARGISNULL(1))
5737  ereport(ERROR,
5738  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5739  errmsg("dimension array or low bound array cannot be null")));
5740 
5741  dims = PG_GETARG_ARRAYTYPE_P(1);
5742 
5743  if (!PG_ARGISNULL(0))
5744  {
5745  value = PG_GETARG_DATUM(0);
5746  isnull = false;
5747  }
5748  else
5749  {
5750  value = 0;
5751  isnull = true;
5752  }
5753 
5754  elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
5755  if (!OidIsValid(elmtype))
5756  elog(ERROR, "could not determine data type of input");
5757 
5758  result = array_fill_internal(dims, NULL, value, isnull, elmtype, fcinfo);
5759  PG_RETURN_ARRAYTYPE_P(result);
5760 }
5761 
5762 static ArrayType *
5763 create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
5764  Oid elmtype, int dataoffset)
5765 {
5766  ArrayType *result;
5767 
5768  result = (ArrayType *) palloc0(nbytes);
5769  SET_VARSIZE(result, nbytes);
5770  result->ndim = ndims;
5771  result->dataoffset = dataoffset;
5772  result->elemtype = elmtype;
5773  memcpy(ARR_DIMS(result), dimv, ndims * sizeof(int));
5774  memcpy(ARR_LBOUND(result), lbsv, ndims * sizeof(int));
5775 
5776  return result;
5777 }
5778 
5779 static ArrayType *
5781  Datum value, bool isnull, Oid elmtype,
5782  FunctionCallInfo fcinfo)
5783 {
5784  ArrayType *result;
5785  int *dimv;
5786  int *lbsv;
5787  int ndims;
5788  int nitems;
5789  int deflbs[MAXDIM];
5790  int16 elmlen;
5791  bool elmbyval;
5792  char elmalign;
5793  ArrayMetaState *my_extra;
5794 
5795  /*
5796  * Params checks
5797  */
5798  if (ARR_NDIM(dims) > 1)
5799  ereport(ERROR,
5800  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5801  errmsg("wrong number of array subscripts"),
5802  errdetail("Dimension array must be one dimensional.")));
5803 
5804  if (array_contains_nulls(dims))
5805  ereport(ERROR,
5806  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5807  errmsg("dimension values cannot be null")));
5808 
5809  dimv = (int *) ARR_DATA_PTR(dims);
5810  ndims = (ARR_NDIM(dims) > 0) ? ARR_DIMS(dims)[0] : 0;
5811 
5812  if (ndims < 0) /* we do allow zero-dimension arrays */
5813  ereport(ERROR,
5814  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
5815  errmsg("invalid number of dimensions: %d", ndims)));
5816  if (ndims > MAXDIM)
5817  ereport(ERROR,
5818  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
5819  errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
5820  ndims, MAXDIM)));
5821 
5822  if (lbs != NULL)
5823  {
5824  if (ARR_NDIM(lbs) > 1)
5825  ereport(ERROR,
5826  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5827  errmsg("wrong number of array subscripts"),
5828  errdetail("Dimension array must be one dimensional.")));
5829 
5830  if (array_contains_nulls(lbs))
5831  ereport(ERROR,
5832  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5833  errmsg("dimension values cannot be null")));
5834 
5835  if (ndims != ((ARR_NDIM(lbs) > 0) ? ARR_DIMS(lbs)[0] : 0))
5836  ereport(ERROR,
5837  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5838  errmsg("wrong number of array subscripts"),
5839  errdetail("Low bound array has different size than dimensions array.")));
5840 
5841  lbsv = (int *) ARR_DATA_PTR(lbs);
5842  }
5843  else
5844  {
5845  int i;
5846 
5847  for (i = 0; i < MAXDIM; i++)
5848  deflbs[i] = 1;
5849 
5850  lbsv = deflbs;
5851  }
5852 
5853  nitems = ArrayGetNItems(ndims, dimv);
5854 
5855  /* fast track for empty array */
5856  if (nitems <= 0)
5857  return construct_empty_array(elmtype);
5858 
5859  /*
5860  * We arrange to look up info about element type only once per series of
5861  * calls, assuming the element type doesn't change underneath us.
5862  */
5863  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
5864  if (my_extra == NULL)
5865  {
5866  fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
5867  sizeof(ArrayMetaState));
5868  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
5869  my_extra->element_type = InvalidOid;
5870  }
5871 
5872  if (my_extra->element_type != elmtype)
5873  {
5874  /* Get info about element type */
5875  get_typlenbyvalalign(elmtype,
5876  &my_extra->typlen,
5877  &my_extra->typbyval,
5878  &my_extra->typalign);
5879  my_extra->element_type = elmtype;
5880  }
5881 
5882  elmlen = my_extra->typlen;
5883  elmbyval = my_extra->typbyval;
5884  elmalign = my_extra->typalign;
5885 
5886  /* compute required space */
5887  if (!isnull)
5888  {
5889  int i;
5890  char *p;
5891  int nbytes;
5892  int totbytes;
5893 
5894  /* make sure data is not toasted */
5895  if (elmlen == -1)
5896  value = PointerGetDatum(PG_DETOAST_DATUM(value));
5897 
5898  nbytes = att_addlength_datum(0, elmlen, value);
5899  nbytes = att_align_nominal(nbytes, elmalign);
5900  Assert(nbytes > 0);
5901 
5902  totbytes = nbytes * nitems;
5903 
5904  /* check for overflow of multiplication or total request */
5905  if (totbytes / nbytes != nitems ||
5906  !AllocSizeIsValid(totbytes))
5907  ereport(ERROR,
5908  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
5909  errmsg("array size exceeds the maximum allowed (%d)",
5910  (int) MaxAllocSize)));
5911 
5912  /*
5913  * This addition can't overflow, but it might cause us to go past
5914  * MaxAllocSize. We leave it to palloc to complain in that case.
5915  */
5916  totbytes += ARR_OVERHEAD_NONULLS(ndims);
5917 
5918  result = create_array_envelope(ndims, dimv, lbsv, totbytes,
5919  elmtype, 0);
5920 
5921  p = ARR_DATA_PTR(result);
5922  for (i = 0; i < nitems; i++)
5923  p += ArrayCastAndSet(value, elmlen, elmbyval, elmalign, p);
5924  }
5925  else
5926  {
5927  int nbytes;
5928  int dataoffset;
5929 
5930  dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
5931  nbytes = dataoffset;
5932 
5933  result = create_array_envelope(ndims, dimv, lbsv, nbytes,
5934  elmtype, dataoffset);
5935 
5936  /* create_array_envelope already zeroed the bitmap, so we're done */
5937  }
5938 
5939  return result;
5940 }
5941 
5942 
5943 /*
5944  * UNNEST
5945  */
5946 Datum
5948 {
5949  typedef struct
5950  {
5951  array_iter iter;
5952  int nextelem;
5953  int numelems;
5954  int16 elmlen;
5955  bool elmbyval;
5956  char elmalign;
5957  } array_unnest_fctx;
5958 
5959  FuncCallContext *funcctx;
5960  array_unnest_fctx *fctx;
5961  MemoryContext oldcontext;
5962 
5963  /* stuff done only on the first call of the function */
5964  if (SRF_IS_FIRSTCALL())
5965  {
5966  AnyArrayType *arr;
5967 
5968  /* create a function context for cross-call persistence */
5969  funcctx = SRF_FIRSTCALL_INIT();
5970 
5971  /*
5972  * switch to memory context appropriate for multiple function calls
5973  */
5974  oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
5975 
5976  /*
5977  * Get the array value and detoast if needed. We can't do this
5978  * earlier because if we have to detoast, we want the detoasted copy
5979  * to be in multi_call_memory_ctx, so it will go away when we're done
5980  * and not before. (If no detoast happens, we assume the originally
5981  * passed array will stick around till then.)
5982  */
5983  arr = PG_GETARG_ANY_ARRAY_P(0);
5984 
5985  /* allocate memory for user context */
5986  fctx = (array_unnest_fctx *) palloc(sizeof(array_unnest_fctx));
5987 
5988  /* initialize state */
5989  array_iter_setup(&fctx->iter, arr);
5990  fctx->nextelem = 0;
5991  fctx->numelems = ArrayGetNItems(AARR_NDIM(arr), AARR_DIMS(arr));
5992 
5993  if (VARATT_IS_EXPANDED_HEADER(arr))
5994  {
5995  /* we can just grab the type data from expanded array */
5996  fctx->elmlen = arr->xpn.typlen;
5997  fctx->elmbyval = arr->xpn.typbyval;
5998  fctx->elmalign = arr->xpn.typalign;
5999  }
6000  else
6002  &fctx->elmlen,
6003  &fctx->elmbyval,
6004  &fctx->elmalign);
6005 
6006  funcctx->user_fctx = fctx;
6007  MemoryContextSwitchTo(oldcontext);
6008  }
6009 
6010  /* stuff done on every call of the function */
6011  funcctx = SRF_PERCALL_SETUP();
6012  fctx = funcctx->user_fctx;
6013 
6014  if (fctx->nextelem < fctx->numelems)
6015  {
6016  int offset = fctx->nextelem++;
6017  Datum elem;
6018 
6019  elem = array_iter_next(&fctx->iter, &fcinfo->isnull, offset,
6020  fctx->elmlen, fctx->elmbyval, fctx->elmalign);
6021 
6022  SRF_RETURN_NEXT(funcctx, elem);
6023  }
6024  else
6025  {
6026  /* do when there is no more left */
6027  SRF_RETURN_DONE(funcctx);
6028  }
6029 }
6030 
6031 /*
6032  * Planner support function for array_unnest(anyarray)
6033  */
6034 Datum
6036 {
6037  Node *rawreq = (Node *) PG_GETARG_POINTER(0);
6038  Node *ret = NULL;
6039 
6040  if (IsA(rawreq, SupportRequestRows))
6041  {
6042  /* Try to estimate the number of rows returned */
6043  SupportRequestRows *req = (SupportRequestRows *) rawreq;
6044 
6045  if (is_funcclause(req->node)) /* be paranoid */
6046  {
6047  List *args = ((FuncExpr *) req->node)->args;
6048  Node *arg1;
6049 
6050  /* We can use estimated argument values here */
6051  arg1 = estimate_expression_value(req->root, linitial(args));
6052 
6053  req->rows = estimate_array_length(arg1);
6054  ret = (Node *) req;
6055  }
6056  }
6057 
6058  PG_RETURN_POINTER(ret);
6059 }
6060 
6061 
6062 /*
6063  * array_replace/array_remove support
6064  *
6065  * Find all array entries matching (not distinct from) search/search_isnull,
6066  * and delete them if remove is true, else replace them with
6067  * replace/replace_isnull. Comparisons are done using the specified
6068  * collation. fcinfo is passed only for caching purposes.
6069  */
6070 static ArrayType *
6072  Datum search, bool search_isnull,
6073  Datum replace, bool replace_isnull,
6074  bool remove, Oid collation,
6075  FunctionCallInfo fcinfo)
6076 {
6077  LOCAL_FCINFO(locfcinfo, 2);
6078  ArrayType *result;
6079  Oid element_type;
6080  Datum *values;
6081  bool *nulls;
6082  int *dim;
6083  int ndim;
6084  int nitems,
6085  nresult;
6086  int i;
6087  int32 nbytes = 0;
6088  int32 dataoffset;
6089  bool hasnulls;
6090  int typlen;
6091  bool typbyval;
6092  char typalign;
6093  char *arraydataptr;
6094  bits8 *bitmap;
6095  int bitmask;
6096  bool changed = false;
6097  TypeCacheEntry *typentry;
6098 
6099  element_type = ARR_ELEMTYPE(array);
6100  ndim = ARR_NDIM(array);
6101  dim = ARR_DIMS(array);
6102  nitems = ArrayGetNItems(ndim, dim);
6103 
6104  /* Return input array unmodified if it is empty */
6105  if (nitems <= 0)
6106  return array;
6107 
6108  /*
6109  * We can't remove elements from multi-dimensional arrays, since the
6110  * result might not be rectangular.
6111  */
6112  if (remove && ndim > 1)
6113  ereport(ERROR,
6114  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6115  errmsg("removing elements from multidimensional arrays is not supported")));
6116 
6117  /*
6118  * We arrange to look up the equality function only once per series of
6119  * calls, assuming the element type doesn't change underneath us.
6120  */
6121  typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
6122  if (typentry == NULL ||
6123  typentry->type_id != element_type)
6124  {
6125  typentry = lookup_type_cache(element_type,
6127  if (!OidIsValid(typentry->eq_opr_finfo.fn_oid