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-2018, 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 #ifdef _MSC_VER
19 #include <float.h> /* for _isnan */
20 #endif
21 #include <math.h>
22 
23 #include "access/hash.h"
24 #include "access/htup_details.h"
25 #include "catalog/pg_type.h"
26 #include "funcapi.h"
27 #include "libpq/pqformat.h"
28 #include "utils/array.h"
29 #include "utils/arrayaccess.h"
30 #include "utils/builtins.h"
31 #include "utils/datum.h"
32 #include "utils/lsyscache.h"
33 #include "utils/memutils.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 *lbv, 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  int nitems,
1034  overall_length,
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 = 1; /* don't forget to count \0 at end. */
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 */
1162  overall_length += 1;
1163  }
1164 
1165  /*
1166  * count total number of curly braces in output string
1167  */
1168  for (i = j = 0, k = 1; i < ndim; i++)
1169  k *= dims[i], j += k;
1170 
1171  dims_str[0] = '\0';
1172 
1173  /* add explicit dimensions if required */
1174  if (needdims)
1175  {
1176  char *ptr = dims_str;
1177 
1178  for (i = 0; i < ndim; i++)
1179  {
1180  sprintf(ptr, "[%d:%d]", lb[i], lb[i] + dims[i] - 1);
1181  ptr += strlen(ptr);
1182  }
1183  *ptr++ = *ASSGN;
1184  *ptr = '\0';
1185  }
1186 
1187  retval = (char *) palloc(strlen(dims_str) + overall_length + 2 * j);
1188  p = retval;
1189 
1190 #define APPENDSTR(str) (strcpy(p, (str)), p += strlen(p))
1191 #define APPENDCHAR(ch) (*p++ = (ch), *p = '\0')
1192 
1193  if (needdims)
1194  APPENDSTR(dims_str);
1195  APPENDCHAR('{');
1196  for (i = 0; i < ndim; i++)
1197  indx[i] = 0;
1198  j = 0;
1199  k = 0;
1200  do
1201  {
1202  for (i = j; i < ndim - 1; i++)
1203  APPENDCHAR('{');
1204 
1205  if (needquotes[k])
1206  {
1207  APPENDCHAR('"');
1208  for (tmp = values[k]; *tmp; tmp++)
1209  {
1210  char ch = *tmp;
1211 
1212  if (ch == '"' || ch == '\\')
1213  *p++ = '\\';
1214  *p++ = ch;
1215  }
1216  *p = '\0';
1217  APPENDCHAR('"');
1218  }
1219  else
1220  APPENDSTR(values[k]);
1221  pfree(values[k++]);
1222 
1223  for (i = ndim - 1; i >= 0; i--)
1224  {
1225  indx[i] = (indx[i] + 1) % dims[i];
1226  if (indx[i])
1227  {
1228  APPENDCHAR(typdelim);
1229  break;
1230  }
1231  else
1232  APPENDCHAR('}');
1233  }
1234  j = i;
1235  } while (j != -1);
1236 
1237 #undef APPENDSTR
1238 #undef APPENDCHAR
1239 
1240  pfree(values);
1241  pfree(needquotes);
1242 
1243  PG_RETURN_CSTRING(retval);
1244 }
1245 
1246 /*
1247  * array_recv :
1248  * converts an array from the external binary format to
1249  * its internal format.
1250  *
1251  * return value :
1252  * the internal representation of the input array
1253  */
1254 Datum
1256 {
1258  Oid spec_element_type = PG_GETARG_OID(1); /* type of an array
1259  * element */
1260  int32 typmod = PG_GETARG_INT32(2); /* typmod for array elements */
1261  Oid element_type;
1262  int typlen;
1263  bool typbyval;
1264  char typalign;
1265  Oid typioparam;
1266  int i,
1267  nitems;
1268  Datum *dataPtr;
1269  bool *nullsPtr;
1270  bool hasnulls;
1271  int32 nbytes;
1272  int32 dataoffset;
1273  ArrayType *retval;
1274  int ndim,
1275  flags,
1276  dim[MAXDIM],
1277  lBound[MAXDIM];
1278  ArrayMetaState *my_extra;
1279 
1280  /* Get the array header information */
1281  ndim = pq_getmsgint(buf, 4);
1282  if (ndim < 0) /* we do allow zero-dimension arrays */
1283  ereport(ERROR,
1284  (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1285  errmsg("invalid number of dimensions: %d", ndim)));
1286  if (ndim > MAXDIM)
1287  ereport(ERROR,
1288  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1289  errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
1290  ndim, MAXDIM)));
1291 
1292  flags = pq_getmsgint(buf, 4);
1293  if (flags != 0 && flags != 1)
1294  ereport(ERROR,
1295  (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1296  errmsg("invalid array flags")));
1297 
1298  element_type = pq_getmsgint(buf, sizeof(Oid));
1299  if (element_type != spec_element_type)
1300  {
1301  /* XXX Can we allow taking the input element type in any cases? */
1302  ereport(ERROR,
1303  (errcode(ERRCODE_DATATYPE_MISMATCH),
1304  errmsg("wrong element type")));
1305  }
1306 
1307  for (i = 0; i < ndim; i++)
1308  {
1309  dim[i] = pq_getmsgint(buf, 4);
1310  lBound[i] = pq_getmsgint(buf, 4);
1311 
1312  /*
1313  * Check overflow of upper bound. (ArrayNItems() below checks that
1314  * dim[i] >= 0)
1315  */
1316  if (dim[i] != 0)
1317  {
1318  int ub = lBound[i] + dim[i] - 1;
1319 
1320  if (lBound[i] > ub)
1321  ereport(ERROR,
1322  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
1323  errmsg("integer out of range")));
1324  }
1325  }
1326 
1327  /* This checks for overflow of array dimensions */
1328  nitems = ArrayGetNItems(ndim, dim);
1329 
1330  /*
1331  * We arrange to look up info about element type, including its receive
1332  * conversion proc, only once per series of calls, assuming the element
1333  * type doesn't change underneath us.
1334  */
1335  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1336  if (my_extra == NULL)
1337  {
1338  fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1339  sizeof(ArrayMetaState));
1340  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1341  my_extra->element_type = ~element_type;
1342  }
1343 
1344  if (my_extra->element_type != element_type)
1345  {
1346  /* Get info about element type, including its receive proc */
1347  get_type_io_data(element_type, IOFunc_receive,
1348  &my_extra->typlen, &my_extra->typbyval,
1349  &my_extra->typalign, &my_extra->typdelim,
1350  &my_extra->typioparam, &my_extra->typiofunc);
1351  if (!OidIsValid(my_extra->typiofunc))
1352  ereport(ERROR,
1353  (errcode(ERRCODE_UNDEFINED_FUNCTION),
1354  errmsg("no binary input function available for type %s",
1355  format_type_be(element_type))));
1356  fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1357  fcinfo->flinfo->fn_mcxt);
1358  my_extra->element_type = element_type;
1359  }
1360 
1361  if (nitems == 0)
1362  {
1363  /* Return empty array ... but not till we've validated element_type */
1365  }
1366 
1367  typlen = my_extra->typlen;
1368  typbyval = my_extra->typbyval;
1369  typalign = my_extra->typalign;
1370  typioparam = my_extra->typioparam;
1371 
1372  dataPtr = (Datum *) palloc(nitems * sizeof(Datum));
1373  nullsPtr = (bool *) palloc(nitems * sizeof(bool));
1374  ReadArrayBinary(buf, nitems,
1375  &my_extra->proc, typioparam, typmod,
1376  typlen, typbyval, typalign,
1377  dataPtr, nullsPtr,
1378  &hasnulls, &nbytes);
1379  if (hasnulls)
1380  {
1381  dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
1382  nbytes += dataoffset;
1383  }
1384  else
1385  {
1386  dataoffset = 0; /* marker for no null bitmap */
1387  nbytes += ARR_OVERHEAD_NONULLS(ndim);
1388  }
1389  retval = (ArrayType *) palloc0(nbytes);
1390  SET_VARSIZE(retval, nbytes);
1391  retval->ndim = ndim;
1392  retval->dataoffset = dataoffset;
1393  retval->elemtype = element_type;
1394  memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
1395  memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
1396 
1397  CopyArrayEls(retval,
1398  dataPtr, nullsPtr, nitems,
1399  typlen, typbyval, typalign,
1400  true);
1401 
1402  pfree(dataPtr);
1403  pfree(nullsPtr);
1404 
1405  PG_RETURN_ARRAYTYPE_P(retval);
1406 }
1407 
1408 /*
1409  * ReadArrayBinary:
1410  * collect the data elements of an array being read in binary style.
1411  *
1412  * Inputs:
1413  * buf: the data buffer to read from.
1414  * nitems: total number of array elements (already read).
1415  * receiveproc: type-specific receive procedure for element datatype.
1416  * typioparam, typmod: auxiliary values to pass to receiveproc.
1417  * typlen, typbyval, typalign: storage parameters of element datatype.
1418  *
1419  * Outputs:
1420  * values[]: filled with converted data values.
1421  * nulls[]: filled with is-null markers.
1422  * *hasnulls: set true iff there are any null elements.
1423  * *nbytes: set to total size of data area needed (including alignment
1424  * padding but not including array header overhead).
1425  *
1426  * Note that values[] and nulls[] are allocated by the caller, and must have
1427  * nitems elements.
1428  */
1429 static void
1431  int nitems,
1432  FmgrInfo *receiveproc,
1433  Oid typioparam,
1434  int32 typmod,
1435  int typlen,
1436  bool typbyval,
1437  char typalign,
1438  Datum *values,
1439  bool *nulls,
1440  bool *hasnulls,
1441  int32 *nbytes)
1442 {
1443  int i;
1444  bool hasnull;
1445  int32 totbytes;
1446 
1447  for (i = 0; i < nitems; i++)
1448  {
1449  int itemlen;
1450  StringInfoData elem_buf;
1451  char csave;
1452 
1453  /* Get and check the item length */
1454  itemlen = pq_getmsgint(buf, 4);
1455  if (itemlen < -1 || itemlen > (buf->len - buf->cursor))
1456  ereport(ERROR,
1457  (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1458  errmsg("insufficient data left in message")));
1459 
1460  if (itemlen == -1)
1461  {
1462  /* -1 length means NULL */
1463  values[i] = ReceiveFunctionCall(receiveproc, NULL,
1464  typioparam, typmod);
1465  nulls[i] = true;
1466  continue;
1467  }
1468 
1469  /*
1470  * Rather than copying data around, we just set up a phony StringInfo
1471  * pointing to the correct portion of the input buffer. We assume we
1472  * can scribble on the input buffer so as to maintain the convention
1473  * that StringInfos have a trailing null.
1474  */
1475  elem_buf.data = &buf->data[buf->cursor];
1476  elem_buf.maxlen = itemlen + 1;
1477  elem_buf.len = itemlen;
1478  elem_buf.cursor = 0;
1479 
1480  buf->cursor += itemlen;
1481 
1482  csave = buf->data[buf->cursor];
1483  buf->data[buf->cursor] = '\0';
1484 
1485  /* Now call the element's receiveproc */
1486  values[i] = ReceiveFunctionCall(receiveproc, &elem_buf,
1487  typioparam, typmod);
1488  nulls[i] = false;
1489 
1490  /* Trouble if it didn't eat the whole buffer */
1491  if (elem_buf.cursor != itemlen)
1492  ereport(ERROR,
1493  (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1494  errmsg("improper binary format in array element %d",
1495  i + 1)));
1496 
1497  buf->data[buf->cursor] = csave;
1498  }
1499 
1500  /*
1501  * Check for nulls, compute total data space needed
1502  */
1503  hasnull = false;
1504  totbytes = 0;
1505  for (i = 0; i < nitems; i++)
1506  {
1507  if (nulls[i])
1508  hasnull = true;
1509  else
1510  {
1511  /* let's just make sure data is not toasted */
1512  if (typlen == -1)
1513  values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
1514  totbytes = att_addlength_datum(totbytes, typlen, values[i]);
1515  totbytes = att_align_nominal(totbytes, typalign);
1516  /* check for overflow of total request */
1517  if (!AllocSizeIsValid(totbytes))
1518  ereport(ERROR,
1519  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1520  errmsg("array size exceeds the maximum allowed (%d)",
1521  (int) MaxAllocSize)));
1522  }
1523  }
1524  *hasnulls = hasnull;
1525  *nbytes = totbytes;
1526 }
1527 
1528 
1529 /*
1530  * array_send :
1531  * takes the internal representation of an array and returns a bytea
1532  * containing the array in its external binary format.
1533  */
1534 Datum
1536 {
1538  Oid element_type = AARR_ELEMTYPE(v);
1539  int typlen;
1540  bool typbyval;
1541  char typalign;
1542  int nitems,
1543  i;
1544  int ndim,
1545  *dim,
1546  *lb;
1548  array_iter iter;
1549  ArrayMetaState *my_extra;
1550 
1551  /*
1552  * We arrange to look up info about element type, including its send
1553  * conversion proc, only once per series of calls, assuming the element
1554  * type doesn't change underneath us.
1555  */
1556  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1557  if (my_extra == NULL)
1558  {
1559  fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1560  sizeof(ArrayMetaState));
1561  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1562  my_extra->element_type = ~element_type;
1563  }
1564 
1565  if (my_extra->element_type != element_type)
1566  {
1567  /* Get info about element type, including its send proc */
1568  get_type_io_data(element_type, IOFunc_send,
1569  &my_extra->typlen, &my_extra->typbyval,
1570  &my_extra->typalign, &my_extra->typdelim,
1571  &my_extra->typioparam, &my_extra->typiofunc);
1572  if (!OidIsValid(my_extra->typiofunc))
1573  ereport(ERROR,
1574  (errcode(ERRCODE_UNDEFINED_FUNCTION),
1575  errmsg("no binary output function available for type %s",
1576  format_type_be(element_type))));
1577  fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1578  fcinfo->flinfo->fn_mcxt);
1579  my_extra->element_type = element_type;
1580  }
1581  typlen = my_extra->typlen;
1582  typbyval = my_extra->typbyval;
1583  typalign = my_extra->typalign;
1584 
1585  ndim = AARR_NDIM(v);
1586  dim = AARR_DIMS(v);
1587  lb = AARR_LBOUND(v);
1588  nitems = ArrayGetNItems(ndim, dim);
1589 
1590  pq_begintypsend(&buf);
1591 
1592  /* Send the array header information */
1593  pq_sendint32(&buf, ndim);
1594  pq_sendint32(&buf, AARR_HASNULL(v) ? 1 : 0);
1595  pq_sendint32(&buf, element_type);
1596  for (i = 0; i < ndim; i++)
1597  {
1598  pq_sendint32(&buf, dim[i]);
1599  pq_sendint32(&buf, lb[i]);
1600  }
1601 
1602  /* Send the array elements using the element's own sendproc */
1603  array_iter_setup(&iter, v);
1604 
1605  for (i = 0; i < nitems; i++)
1606  {
1607  Datum itemvalue;
1608  bool isnull;
1609 
1610  /* Get source element, checking for NULL */
1611  itemvalue = array_iter_next(&iter, &isnull, i,
1612  typlen, typbyval, typalign);
1613 
1614  if (isnull)
1615  {
1616  /* -1 length means a NULL */
1617  pq_sendint32(&buf, -1);
1618  }
1619  else
1620  {
1621  bytea *outputbytes;
1622 
1623  outputbytes = SendFunctionCall(&my_extra->proc, itemvalue);
1624  pq_sendint32(&buf, VARSIZE(outputbytes) - VARHDRSZ);
1625  pq_sendbytes(&buf, VARDATA(outputbytes),
1626  VARSIZE(outputbytes) - VARHDRSZ);
1627  pfree(outputbytes);
1628  }
1629  }
1630 
1632 }
1633 
1634 /*
1635  * array_ndims :
1636  * returns the number of dimensions of the array pointed to by "v"
1637  */
1638 Datum
1640 {
1642 
1643  /* Sanity check: does it look like an array at all? */
1644  if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
1645  PG_RETURN_NULL();
1646 
1648 }
1649 
1650 /*
1651  * array_dims :
1652  * returns the dimensions of the array pointed to by "v", as a "text"
1653  */
1654 Datum
1656 {
1658  char *p;
1659  int i;
1660  int *dimv,
1661  *lb;
1662 
1663  /*
1664  * 33 since we assume 15 digits per number + ':' +'[]'
1665  *
1666  * +1 for trailing null
1667  */
1668  char buf[MAXDIM * 33 + 1];
1669 
1670  /* Sanity check: does it look like an array at all? */
1671  if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
1672  PG_RETURN_NULL();
1673 
1674  dimv = AARR_DIMS(v);
1675  lb = AARR_LBOUND(v);
1676 
1677  p = buf;
1678  for (i = 0; i < AARR_NDIM(v); i++)
1679  {
1680  sprintf(p, "[%d:%d]", lb[i], dimv[i] + lb[i] - 1);
1681  p += strlen(p);
1682  }
1683 
1685 }
1686 
1687 /*
1688  * array_lower :
1689  * returns the lower dimension, of the DIM requested, for
1690  * the array pointed to by "v", as an int4
1691  */
1692 Datum
1694 {
1696  int reqdim = PG_GETARG_INT32(1);
1697  int *lb;
1698  int result;
1699 
1700  /* Sanity check: does it look like an array at all? */
1701  if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
1702  PG_RETURN_NULL();
1703 
1704  /* Sanity check: was the requested dim valid */
1705  if (reqdim <= 0 || reqdim > AARR_NDIM(v))
1706  PG_RETURN_NULL();
1707 
1708  lb = AARR_LBOUND(v);
1709  result = lb[reqdim - 1];
1710 
1711  PG_RETURN_INT32(result);
1712 }
1713 
1714 /*
1715  * array_upper :
1716  * returns the upper dimension, of the DIM requested, for
1717  * the array pointed to by "v", as an int4
1718  */
1719 Datum
1721 {
1723  int reqdim = PG_GETARG_INT32(1);
1724  int *dimv,
1725  *lb;
1726  int result;
1727 
1728  /* Sanity check: does it look like an array at all? */
1729  if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
1730  PG_RETURN_NULL();
1731 
1732  /* Sanity check: was the requested dim valid */
1733  if (reqdim <= 0 || reqdim > AARR_NDIM(v))
1734  PG_RETURN_NULL();
1735 
1736  lb = AARR_LBOUND(v);
1737  dimv = AARR_DIMS(v);
1738 
1739  result = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
1740 
1741  PG_RETURN_INT32(result);
1742 }
1743 
1744 /*
1745  * array_length :
1746  * returns the length, of the dimension requested, for
1747  * the array pointed to by "v", as an int4
1748  */
1749 Datum
1751 {
1753  int reqdim = PG_GETARG_INT32(1);
1754  int *dimv;
1755  int result;
1756 
1757  /* Sanity check: does it look like an array at all? */
1758  if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
1759  PG_RETURN_NULL();
1760 
1761  /* Sanity check: was the requested dim valid */
1762  if (reqdim <= 0 || reqdim > AARR_NDIM(v))
1763  PG_RETURN_NULL();
1764 
1765  dimv = AARR_DIMS(v);
1766 
1767  result = dimv[reqdim - 1];
1768 
1769  PG_RETURN_INT32(result);
1770 }
1771 
1772 /*
1773  * array_cardinality:
1774  * returns the total number of elements in an array
1775  */
1776 Datum
1778 {
1780 
1782 }
1783 
1784 
1785 /*
1786  * array_get_element :
1787  * This routine takes an array datum and a subscript array and returns
1788  * the referenced item as a Datum. Note that for a pass-by-reference
1789  * datatype, the returned Datum is a pointer into the array object.
1790  *
1791  * This handles both ordinary varlena arrays and fixed-length arrays.
1792  *
1793  * Inputs:
1794  * arraydatum: the array object (mustn't be NULL)
1795  * nSubscripts: number of subscripts supplied
1796  * indx[]: the subscript values
1797  * arraytyplen: pg_type.typlen for the array type
1798  * elmlen: pg_type.typlen for the array's element type
1799  * elmbyval: pg_type.typbyval for the array's element type
1800  * elmalign: pg_type.typalign for the array's element type
1801  *
1802  * Outputs:
1803  * The return value is the element Datum.
1804  * *isNull is set to indicate whether the element is NULL.
1805  */
1806 Datum
1808  int nSubscripts,
1809  int *indx,
1810  int arraytyplen,
1811  int elmlen,
1812  bool elmbyval,
1813  char elmalign,
1814  bool *isNull)
1815 {
1816  int i,
1817  ndim,
1818  *dim,
1819  *lb,
1820  offset,
1821  fixedDim[1],
1822  fixedLb[1];
1823  char *arraydataptr,
1824  *retptr;
1825  bits8 *arraynullsptr;
1826 
1827  if (arraytyplen > 0)
1828  {
1829  /*
1830  * fixed-length arrays -- these are assumed to be 1-d, 0-based
1831  */
1832  ndim = 1;
1833  fixedDim[0] = arraytyplen / elmlen;
1834  fixedLb[0] = 0;
1835  dim = fixedDim;
1836  lb = fixedLb;
1837  arraydataptr = (char *) DatumGetPointer(arraydatum);
1838  arraynullsptr = NULL;
1839  }
1840  else if (VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(arraydatum)))
1841  {
1842  /* expanded array: let's do this in a separate function */
1843  return array_get_element_expanded(arraydatum,
1844  nSubscripts,
1845  indx,
1846  arraytyplen,
1847  elmlen,
1848  elmbyval,
1849  elmalign,
1850  isNull);
1851  }
1852  else
1853  {
1854  /* detoast array if necessary, producing normal varlena input */
1855  ArrayType *array = DatumGetArrayTypeP(arraydatum);
1856 
1857  ndim = ARR_NDIM(array);
1858  dim = ARR_DIMS(array);
1859  lb = ARR_LBOUND(array);
1860  arraydataptr = ARR_DATA_PTR(array);
1861  arraynullsptr = ARR_NULLBITMAP(array);
1862  }
1863 
1864  /*
1865  * Return NULL for invalid subscript
1866  */
1867  if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
1868  {
1869  *isNull = true;
1870  return (Datum) 0;
1871  }
1872  for (i = 0; i < ndim; i++)
1873  {
1874  if (indx[i] < lb[i] || indx[i] >= (dim[i] + lb[i]))
1875  {
1876  *isNull = true;
1877  return (Datum) 0;
1878  }
1879  }
1880 
1881  /*
1882  * Calculate the element number
1883  */
1884  offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
1885 
1886  /*
1887  * Check for NULL array element
1888  */
1889  if (array_get_isnull(arraynullsptr, offset))
1890  {
1891  *isNull = true;
1892  return (Datum) 0;
1893  }
1894 
1895  /*
1896  * OK, get the element
1897  */
1898  *isNull = false;
1899  retptr = array_seek(arraydataptr, 0, arraynullsptr, offset,
1900  elmlen, elmbyval, elmalign);
1901  return ArrayCast(retptr, elmbyval, elmlen);
1902 }
1903 
1904 /*
1905  * Implementation of array_get_element() for an expanded array
1906  */
1907 static Datum
1909  int nSubscripts, int *indx,
1910  int arraytyplen,
1911  int elmlen, bool elmbyval, char elmalign,
1912  bool *isNull)
1913 {
1914  ExpandedArrayHeader *eah;
1915  int i,
1916  ndim,
1917  *dim,
1918  *lb,
1919  offset;
1920  Datum *dvalues;
1921  bool *dnulls;
1922 
1923  eah = (ExpandedArrayHeader *) DatumGetEOHP(arraydatum);
1924  Assert(eah->ea_magic == EA_MAGIC);
1925 
1926  /* sanity-check caller's info against object */
1927  Assert(arraytyplen == -1);
1928  Assert(elmlen == eah->typlen);
1929  Assert(elmbyval == eah->typbyval);
1930  Assert(elmalign == eah->typalign);
1931 
1932  ndim = eah->ndims;
1933  dim = eah->dims;
1934  lb = eah->lbound;
1935 
1936  /*
1937  * Return NULL for invalid subscript
1938  */
1939  if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
1940  {
1941  *isNull = true;
1942  return (Datum) 0;
1943  }
1944  for (i = 0; i < ndim; i++)
1945  {
1946  if (indx[i] < lb[i] || indx[i] >= (dim[i] + lb[i]))
1947  {
1948  *isNull = true;
1949  return (Datum) 0;
1950  }
1951  }
1952 
1953  /*
1954  * Calculate the element number
1955  */
1956  offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
1957 
1958  /*
1959  * Deconstruct array if we didn't already. Note that we apply this even
1960  * if the input is nominally read-only: it should be safe enough.
1961  */
1963 
1964  dvalues = eah->dvalues;
1965  dnulls = eah->dnulls;
1966 
1967  /*
1968  * Check for NULL array element
1969  */
1970  if (dnulls && dnulls[offset])
1971  {
1972  *isNull = true;
1973  return (Datum) 0;
1974  }
1975 
1976  /*
1977  * OK, get the element. It's OK to return a pass-by-ref value as a
1978  * pointer into the expanded array, for the same reason that regular
1979  * array_get_element can return a pointer into flat arrays: the value is
1980  * assumed not to change for as long as the Datum reference can exist.
1981  */
1982  *isNull = false;
1983  return dvalues[offset];
1984 }
1985 
1986 /*
1987  * array_get_slice :
1988  * This routine takes an array and a range of indices (upperIndex and
1989  * lowerIndx), creates a new array structure for the referred elements
1990  * and returns a pointer to it.
1991  *
1992  * This handles both ordinary varlena arrays and fixed-length arrays.
1993  *
1994  * Inputs:
1995  * arraydatum: the array object (mustn't be NULL)
1996  * nSubscripts: number of subscripts supplied (must be same for upper/lower)
1997  * upperIndx[]: the upper subscript values
1998  * lowerIndx[]: the lower subscript values
1999  * upperProvided[]: true for provided upper subscript values
2000  * lowerProvided[]: true for provided lower subscript values
2001  * arraytyplen: pg_type.typlen for the array type
2002  * elmlen: pg_type.typlen for the array's element type
2003  * elmbyval: pg_type.typbyval for the array's element type
2004  * elmalign: pg_type.typalign for the array's element type
2005  *
2006  * Outputs:
2007  * The return value is the new array Datum (it's never NULL)
2008  *
2009  * Omitted upper and lower subscript values are replaced by the corresponding
2010  * array bound.
2011  *
2012  * NOTE: we assume it is OK to scribble on the provided subscript arrays
2013  * lowerIndx[] and upperIndx[]. These are generally just temporaries.
2014  */
2015 Datum
2017  int nSubscripts,
2018  int *upperIndx,
2019  int *lowerIndx,
2020  bool *upperProvided,
2021  bool *lowerProvided,
2022  int arraytyplen,
2023  int elmlen,
2024  bool elmbyval,
2025  char elmalign)
2026 {
2027  ArrayType *array;
2028  ArrayType *newarray;
2029  int i,
2030  ndim,
2031  *dim,
2032  *lb,
2033  *newlb;
2034  int fixedDim[1],
2035  fixedLb[1];
2036  Oid elemtype;
2037  char *arraydataptr;
2038  bits8 *arraynullsptr;
2039  int32 dataoffset;
2040  int bytes,
2041  span[MAXDIM];
2042 
2043  if (arraytyplen > 0)
2044  {
2045  /*
2046  * fixed-length arrays -- currently, cannot slice these because parser
2047  * labels output as being of the fixed-length array type! Code below
2048  * shows how we could support it if the parser were changed to label
2049  * output as a suitable varlena array type.
2050  */
2051  ereport(ERROR,
2052  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2053  errmsg("slices of fixed-length arrays not implemented")));
2054 
2055  /*
2056  * fixed-length arrays -- these are assumed to be 1-d, 0-based
2057  *
2058  * XXX where would we get the correct ELEMTYPE from?
2059  */
2060  ndim = 1;
2061  fixedDim[0] = arraytyplen / elmlen;
2062  fixedLb[0] = 0;
2063  dim = fixedDim;
2064  lb = fixedLb;
2065  elemtype = InvalidOid; /* XXX */
2066  arraydataptr = (char *) DatumGetPointer(arraydatum);
2067  arraynullsptr = NULL;
2068  }
2069  else
2070  {
2071  /* detoast input array if necessary */
2072  array = DatumGetArrayTypeP(arraydatum);
2073 
2074  ndim = ARR_NDIM(array);
2075  dim = ARR_DIMS(array);
2076  lb = ARR_LBOUND(array);
2077  elemtype = ARR_ELEMTYPE(array);
2078  arraydataptr = ARR_DATA_PTR(array);
2079  arraynullsptr = ARR_NULLBITMAP(array);
2080  }
2081 
2082  /*
2083  * Check provided subscripts. A slice exceeding the current array limits
2084  * is silently truncated to the array limits. If we end up with an empty
2085  * slice, return an empty array.
2086  */
2087  if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
2088  return PointerGetDatum(construct_empty_array(elemtype));
2089 
2090  for (i = 0; i < nSubscripts; i++)
2091  {
2092  if (!lowerProvided[i] || lowerIndx[i] < lb[i])
2093  lowerIndx[i] = lb[i];
2094  if (!upperProvided[i] || upperIndx[i] >= (dim[i] + lb[i]))
2095  upperIndx[i] = dim[i] + lb[i] - 1;
2096  if (lowerIndx[i] > upperIndx[i])
2097  return PointerGetDatum(construct_empty_array(elemtype));
2098  }
2099  /* fill any missing subscript positions with full array range */
2100  for (; i < ndim; i++)
2101  {
2102  lowerIndx[i] = lb[i];
2103  upperIndx[i] = dim[i] + lb[i] - 1;
2104  if (lowerIndx[i] > upperIndx[i])
2105  return PointerGetDatum(construct_empty_array(elemtype));
2106  }
2107 
2108  mda_get_range(ndim, span, lowerIndx, upperIndx);
2109 
2110  bytes = array_slice_size(arraydataptr, arraynullsptr,
2111  ndim, dim, lb,
2112  lowerIndx, upperIndx,
2113  elmlen, elmbyval, elmalign);
2114 
2115  /*
2116  * Currently, we put a null bitmap in the result if the source has one;
2117  * could be smarter ...
2118  */
2119  if (arraynullsptr)
2120  {
2121  dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, ArrayGetNItems(ndim, span));
2122  bytes += dataoffset;
2123  }
2124  else
2125  {
2126  dataoffset = 0; /* marker for no null bitmap */
2127  bytes += ARR_OVERHEAD_NONULLS(ndim);
2128  }
2129 
2130  newarray = (ArrayType *) palloc0(bytes);
2131  SET_VARSIZE(newarray, bytes);
2132  newarray->ndim = ndim;
2133  newarray->dataoffset = dataoffset;
2134  newarray->elemtype = elemtype;
2135  memcpy(ARR_DIMS(newarray), span, ndim * sizeof(int));
2136 
2137  /*
2138  * Lower bounds of the new array are set to 1. Formerly (before 7.3) we
2139  * copied the given lowerIndx values ... but that seems confusing.
2140  */
2141  newlb = ARR_LBOUND(newarray);
2142  for (i = 0; i < ndim; i++)
2143  newlb[i] = 1;
2144 
2145  array_extract_slice(newarray,
2146  ndim, dim, lb,
2147  arraydataptr, arraynullsptr,
2148  lowerIndx, upperIndx,
2149  elmlen, elmbyval, elmalign);
2150 
2151  return PointerGetDatum(newarray);
2152 }
2153 
2154 /*
2155  * array_set_element :
2156  * This routine sets the value of one array element (specified by
2157  * a subscript array) to a new value specified by "dataValue".
2158  *
2159  * This handles both ordinary varlena arrays and fixed-length arrays.
2160  *
2161  * Inputs:
2162  * arraydatum: the initial array object (mustn't be NULL)
2163  * nSubscripts: number of subscripts supplied
2164  * indx[]: the subscript values
2165  * dataValue: the datum to be inserted at the given position
2166  * isNull: whether dataValue is NULL
2167  * arraytyplen: pg_type.typlen for the array type
2168  * elmlen: pg_type.typlen for the array's element type
2169  * elmbyval: pg_type.typbyval for the array's element type
2170  * elmalign: pg_type.typalign for the array's element type
2171  *
2172  * Result:
2173  * A new array is returned, just like the old except for the one
2174  * modified entry. The original array object is not changed,
2175  * unless what is passed is a read-write reference to an expanded
2176  * array object; in that case the expanded array is updated in-place.
2177  *
2178  * For one-dimensional arrays only, we allow the array to be extended
2179  * by assigning to a position outside the existing subscript range; any
2180  * positions between the existing elements and the new one are set to NULLs.
2181  * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
2182  *
2183  * NOTE: For assignments, we throw an error for invalid subscripts etc,
2184  * rather than returning a NULL as the fetch operations do.
2185  */
2186 Datum
2188  int nSubscripts,
2189  int *indx,
2190  Datum dataValue,
2191  bool isNull,
2192  int arraytyplen,
2193  int elmlen,
2194  bool elmbyval,
2195  char elmalign)
2196 {
2197  ArrayType *array;
2198  ArrayType *newarray;
2199  int i,
2200  ndim,
2201  dim[MAXDIM],
2202  lb[MAXDIM],
2203  offset;
2204  char *elt_ptr;
2205  bool newhasnulls;
2206  bits8 *oldnullbitmap;
2207  int oldnitems,
2208  newnitems,
2209  olddatasize,
2210  newsize,
2211  olditemlen,
2212  newitemlen,
2213  overheadlen,
2214  oldoverheadlen,
2215  addedbefore,
2216  addedafter,
2217  lenbefore,
2218  lenafter;
2219 
2220  if (arraytyplen > 0)
2221  {
2222  /*
2223  * fixed-length arrays -- these are assumed to be 1-d, 0-based. We
2224  * cannot extend them, either.
2225  */
2226  char *resultarray;
2227 
2228  if (nSubscripts != 1)
2229  ereport(ERROR,
2230  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2231  errmsg("wrong number of array subscripts")));
2232 
2233  if (indx[0] < 0 || indx[0] * elmlen >= arraytyplen)
2234  ereport(ERROR,
2235  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2236  errmsg("array subscript out of range")));
2237 
2238  if (isNull)
2239  ereport(ERROR,
2240  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
2241  errmsg("cannot assign null value to an element of a fixed-length array")));
2242 
2243  resultarray = (char *) palloc(arraytyplen);
2244  memcpy(resultarray, DatumGetPointer(arraydatum), arraytyplen);
2245  elt_ptr = (char *) resultarray + indx[0] * elmlen;
2246  ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign, elt_ptr);
2247  return PointerGetDatum(resultarray);
2248  }
2249 
2250  if (nSubscripts <= 0 || nSubscripts > MAXDIM)
2251  ereport(ERROR,
2252  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2253  errmsg("wrong number of array subscripts")));
2254 
2255  /* make sure item to be inserted is not toasted */
2256  if (elmlen == -1 && !isNull)
2257  dataValue = PointerGetDatum(PG_DETOAST_DATUM(dataValue));
2258 
2260  {
2261  /* expanded array: let's do this in a separate function */
2262  return array_set_element_expanded(arraydatum,
2263  nSubscripts,
2264  indx,
2265  dataValue,
2266  isNull,
2267  arraytyplen,
2268  elmlen,
2269  elmbyval,
2270  elmalign);
2271  }
2272 
2273  /* detoast input array if necessary */
2274  array = DatumGetArrayTypeP(arraydatum);
2275 
2276  ndim = ARR_NDIM(array);
2277 
2278  /*
2279  * if number of dims is zero, i.e. an empty array, create an array with
2280  * nSubscripts dimensions, and set the lower bounds to the supplied
2281  * subscripts
2282  */
2283  if (ndim == 0)
2284  {
2285  Oid elmtype = ARR_ELEMTYPE(array);
2286 
2287  for (i = 0; i < nSubscripts; i++)
2288  {
2289  dim[i] = 1;
2290  lb[i] = indx[i];
2291  }
2292 
2293  return PointerGetDatum(construct_md_array(&dataValue, &isNull,
2294  nSubscripts, dim, lb,
2295  elmtype,
2296  elmlen, elmbyval, elmalign));
2297  }
2298 
2299  if (ndim != nSubscripts)
2300  ereport(ERROR,
2301  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2302  errmsg("wrong number of array subscripts")));
2303 
2304  /* copy dim/lb since we may modify them */
2305  memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
2306  memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
2307 
2308  newhasnulls = (ARR_HASNULL(array) || isNull);
2309  addedbefore = addedafter = 0;
2310 
2311  /*
2312  * Check subscripts
2313  */
2314  if (ndim == 1)
2315  {
2316  if (indx[0] < lb[0])
2317  {
2318  addedbefore = lb[0] - indx[0];
2319  dim[0] += addedbefore;
2320  lb[0] = indx[0];
2321  if (addedbefore > 1)
2322  newhasnulls = true; /* will insert nulls */
2323  }
2324  if (indx[0] >= (dim[0] + lb[0]))
2325  {
2326  addedafter = indx[0] - (dim[0] + lb[0]) + 1;
2327  dim[0] += addedafter;
2328  if (addedafter > 1)
2329  newhasnulls = true; /* will insert nulls */
2330  }
2331  }
2332  else
2333  {
2334  /*
2335  * XXX currently we do not support extending multi-dimensional arrays
2336  * during assignment
2337  */
2338  for (i = 0; i < ndim; i++)
2339  {
2340  if (indx[i] < lb[i] ||
2341  indx[i] >= (dim[i] + lb[i]))
2342  ereport(ERROR,
2343  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2344  errmsg("array subscript out of range")));
2345  }
2346  }
2347 
2348  /*
2349  * Compute sizes of items and areas to copy
2350  */
2351  newnitems = ArrayGetNItems(ndim, dim);
2352  if (newhasnulls)
2353  overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, newnitems);
2354  else
2355  overheadlen = ARR_OVERHEAD_NONULLS(ndim);
2356  oldnitems = ArrayGetNItems(ndim, ARR_DIMS(array));
2357  oldnullbitmap = ARR_NULLBITMAP(array);
2358  oldoverheadlen = ARR_DATA_OFFSET(array);
2359  olddatasize = ARR_SIZE(array) - oldoverheadlen;
2360  if (addedbefore)
2361  {
2362  offset = 0;
2363  lenbefore = 0;
2364  olditemlen = 0;
2365  lenafter = olddatasize;
2366  }
2367  else if (addedafter)
2368  {
2369  offset = oldnitems;
2370  lenbefore = olddatasize;
2371  olditemlen = 0;
2372  lenafter = 0;
2373  }
2374  else
2375  {
2376  offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
2377  elt_ptr = array_seek(ARR_DATA_PTR(array), 0, oldnullbitmap, offset,
2378  elmlen, elmbyval, elmalign);
2379  lenbefore = (int) (elt_ptr - ARR_DATA_PTR(array));
2380  if (array_get_isnull(oldnullbitmap, offset))
2381  olditemlen = 0;
2382  else
2383  {
2384  olditemlen = att_addlength_pointer(0, elmlen, elt_ptr);
2385  olditemlen = att_align_nominal(olditemlen, elmalign);
2386  }
2387  lenafter = (int) (olddatasize - lenbefore - olditemlen);
2388  }
2389 
2390  if (isNull)
2391  newitemlen = 0;
2392  else
2393  {
2394  newitemlen = att_addlength_datum(0, elmlen, dataValue);
2395  newitemlen = att_align_nominal(newitemlen, elmalign);
2396  }
2397 
2398  newsize = overheadlen + lenbefore + newitemlen + lenafter;
2399 
2400  /*
2401  * OK, create the new array and fill in header/dimensions
2402  */
2403  newarray = (ArrayType *) palloc0(newsize);
2404  SET_VARSIZE(newarray, newsize);
2405  newarray->ndim = ndim;
2406  newarray->dataoffset = newhasnulls ? overheadlen : 0;
2407  newarray->elemtype = ARR_ELEMTYPE(array);
2408  memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
2409  memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
2410 
2411  /*
2412  * Fill in data
2413  */
2414  memcpy((char *) newarray + overheadlen,
2415  (char *) array + oldoverheadlen,
2416  lenbefore);
2417  if (!isNull)
2418  ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign,
2419  (char *) newarray + overheadlen + lenbefore);
2420  memcpy((char *) newarray + overheadlen + lenbefore + newitemlen,
2421  (char *) array + oldoverheadlen + lenbefore + olditemlen,
2422  lenafter);
2423 
2424  /*
2425  * Fill in nulls bitmap if needed
2426  *
2427  * Note: it's possible we just replaced the last NULL with a non-NULL, and
2428  * could get rid of the bitmap. Seems not worth testing for though.
2429  */
2430  if (newhasnulls)
2431  {
2432  bits8 *newnullbitmap = ARR_NULLBITMAP(newarray);
2433 
2434  /* Zero the bitmap to take care of marking inserted positions null */
2435  MemSet(newnullbitmap, 0, (newnitems + 7) / 8);
2436  /* Fix the inserted value */
2437  if (addedafter)
2438  array_set_isnull(newnullbitmap, newnitems - 1, isNull);
2439  else
2440  array_set_isnull(newnullbitmap, offset, isNull);
2441  /* Fix the copied range(s) */
2442  if (addedbefore)
2443  array_bitmap_copy(newnullbitmap, addedbefore,
2444  oldnullbitmap, 0,
2445  oldnitems);
2446  else
2447  {
2448  array_bitmap_copy(newnullbitmap, 0,
2449  oldnullbitmap, 0,
2450  offset);
2451  if (addedafter == 0)
2452  array_bitmap_copy(newnullbitmap, offset + 1,
2453  oldnullbitmap, offset + 1,
2454  oldnitems - offset - 1);
2455  }
2456  }
2457 
2458  return PointerGetDatum(newarray);
2459 }
2460 
2461 /*
2462  * Implementation of array_set_element() for an expanded array
2463  *
2464  * Note: as with any operation on a read/write expanded object, we must
2465  * take pains not to leave the object in a corrupt state if we fail partway
2466  * through.
2467  */
2468 static Datum
2470  int nSubscripts, int *indx,
2471  Datum dataValue, bool isNull,
2472  int arraytyplen,
2473  int elmlen, bool elmbyval, char elmalign)
2474 {
2475  ExpandedArrayHeader *eah;
2476  Datum *dvalues;
2477  bool *dnulls;
2478  int i,
2479  ndim,
2480  dim[MAXDIM],
2481  lb[MAXDIM],
2482  offset;
2483  bool dimschanged,
2484  newhasnulls;
2485  int addedbefore,
2486  addedafter;
2487  char *oldValue;
2488 
2489  /* Convert to R/W object if not so already */
2490  eah = DatumGetExpandedArray(arraydatum);
2491 
2492  /* Sanity-check caller's info against object; we don't use it otherwise */
2493  Assert(arraytyplen == -1);
2494  Assert(elmlen == eah->typlen);
2495  Assert(elmbyval == eah->typbyval);
2496  Assert(elmalign == eah->typalign);
2497 
2498  /*
2499  * Copy dimension info into local storage. This allows us to modify the
2500  * dimensions if needed, while not messing up the expanded value if we
2501  * fail partway through.
2502  */
2503  ndim = eah->ndims;
2504  Assert(ndim >= 0 && ndim <= MAXDIM);
2505  memcpy(dim, eah->dims, ndim * sizeof(int));
2506  memcpy(lb, eah->lbound, ndim * sizeof(int));
2507  dimschanged = false;
2508 
2509  /*
2510  * if number of dims is zero, i.e. an empty array, create an array with
2511  * nSubscripts dimensions, and set the lower bounds to the supplied
2512  * subscripts.
2513  */
2514  if (ndim == 0)
2515  {
2516  /*
2517  * Allocate adequate space for new dimension info. This is harmless
2518  * if we fail later.
2519  */
2520  Assert(nSubscripts > 0 && nSubscripts <= MAXDIM);
2521  eah->dims = (int *) MemoryContextAllocZero(eah->hdr.eoh_context,
2522  nSubscripts * sizeof(int));
2523  eah->lbound = (int *) MemoryContextAllocZero(eah->hdr.eoh_context,
2524  nSubscripts * sizeof(int));
2525 
2526  /* Update local copies of dimension info */
2527  ndim = nSubscripts;
2528  for (i = 0; i < nSubscripts; i++)
2529  {
2530  dim[i] = 0;
2531  lb[i] = indx[i];
2532  }
2533  dimschanged = true;
2534  }
2535  else if (ndim != nSubscripts)
2536  ereport(ERROR,
2537  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2538  errmsg("wrong number of array subscripts")));
2539 
2540  /*
2541  * Deconstruct array if we didn't already. (Someday maybe add a special
2542  * case path for fixed-length, no-nulls cases, where we can overwrite an
2543  * element in place without ever deconstructing. But today is not that
2544  * day.)
2545  */
2547 
2548  /*
2549  * Copy new element into array's context, if needed (we assume it's
2550  * already detoasted, so no junk should be created). If we fail further
2551  * down, this memory is leaked, but that's reasonably harmless.
2552  */
2553  if (!eah->typbyval && !isNull)
2554  {
2556 
2557  dataValue = datumCopy(dataValue, false, eah->typlen);
2558  MemoryContextSwitchTo(oldcxt);
2559  }
2560 
2561  dvalues = eah->dvalues;
2562  dnulls = eah->dnulls;
2563 
2564  newhasnulls = ((dnulls != NULL) || isNull);
2565  addedbefore = addedafter = 0;
2566 
2567  /*
2568  * Check subscripts (this logic matches original array_set_element)
2569  */
2570  if (ndim == 1)
2571  {
2572  if (indx[0] < lb[0])
2573  {
2574  addedbefore = lb[0] - indx[0];
2575  dim[0] += addedbefore;
2576  lb[0] = indx[0];
2577  dimschanged = true;
2578  if (addedbefore > 1)
2579  newhasnulls = true; /* will insert nulls */
2580  }
2581  if (indx[0] >= (dim[0] + lb[0]))
2582  {
2583  addedafter = indx[0] - (dim[0] + lb[0]) + 1;
2584  dim[0] += addedafter;
2585  dimschanged = true;
2586  if (addedafter > 1)
2587  newhasnulls = true; /* will insert nulls */
2588  }
2589  }
2590  else
2591  {
2592  /*
2593  * XXX currently we do not support extending multi-dimensional arrays
2594  * during assignment
2595  */
2596  for (i = 0; i < ndim; i++)
2597  {
2598  if (indx[i] < lb[i] ||
2599  indx[i] >= (dim[i] + lb[i]))
2600  ereport(ERROR,
2601  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2602  errmsg("array subscript out of range")));
2603  }
2604  }
2605 
2606  /* Now we can calculate linear offset of target item in array */
2607  offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
2608 
2609  /* Physically enlarge existing dvalues/dnulls arrays if needed */
2610  if (dim[0] > eah->dvalueslen)
2611  {
2612  /* We want some extra space if we're enlarging */
2613  int newlen = dim[0] + dim[0] / 8;
2614 
2615  newlen = Max(newlen, dim[0]); /* integer overflow guard */
2616  eah->dvalues = dvalues = (Datum *)
2617  repalloc(dvalues, newlen * sizeof(Datum));
2618  if (dnulls)
2619  eah->dnulls = dnulls = (bool *)
2620  repalloc(dnulls, newlen * sizeof(bool));
2621  eah->dvalueslen = newlen;
2622  }
2623 
2624  /*
2625  * If we need a nulls bitmap and don't already have one, create it, being
2626  * sure to mark all existing entries as not null.
2627  */
2628  if (newhasnulls && dnulls == NULL)
2629  eah->dnulls = dnulls = (bool *)
2631  eah->dvalueslen * sizeof(bool));
2632 
2633  /*
2634  * We now have all the needed space allocated, so we're ready to make
2635  * irreversible changes. Be very wary of allowing failure below here.
2636  */
2637 
2638  /* Flattened value will no longer represent array accurately */
2639  eah->fvalue = NULL;
2640  /* And we don't know the flattened size either */
2641  eah->flat_size = 0;
2642 
2643  /* Update dimensionality info if needed */
2644  if (dimschanged)
2645  {
2646  eah->ndims = ndim;
2647  memcpy(eah->dims, dim, ndim * sizeof(int));
2648  memcpy(eah->lbound, lb, ndim * sizeof(int));
2649  }
2650 
2651  /* Reposition items if needed, and fill addedbefore items with nulls */
2652  if (addedbefore > 0)
2653  {
2654  memmove(dvalues + addedbefore, dvalues, eah->nelems * sizeof(Datum));
2655  for (i = 0; i < addedbefore; i++)
2656  dvalues[i] = (Datum) 0;
2657  if (dnulls)
2658  {
2659  memmove(dnulls + addedbefore, dnulls, eah->nelems * sizeof(bool));
2660  for (i = 0; i < addedbefore; i++)
2661  dnulls[i] = true;
2662  }
2663  eah->nelems += addedbefore;
2664  }
2665 
2666  /* fill addedafter items with nulls */
2667  if (addedafter > 0)
2668  {
2669  for (i = 0; i < addedafter; i++)
2670  dvalues[eah->nelems + i] = (Datum) 0;
2671  if (dnulls)
2672  {
2673  for (i = 0; i < addedafter; i++)
2674  dnulls[eah->nelems + i] = true;
2675  }
2676  eah->nelems += addedafter;
2677  }
2678 
2679  /* Grab old element value for pfree'ing, if needed. */
2680  if (!eah->typbyval && (dnulls == NULL || !dnulls[offset]))
2681  oldValue = (char *) DatumGetPointer(dvalues[offset]);
2682  else
2683  oldValue = NULL;
2684 
2685  /* And finally we can insert the new element. */
2686  dvalues[offset] = dataValue;
2687  if (dnulls)
2688  dnulls[offset] = isNull;
2689 
2690  /*
2691  * Free old element if needed; this keeps repeated element replacements
2692  * from bloating the array's storage. If the pfree somehow fails, it
2693  * won't corrupt the array.
2694  */
2695  if (oldValue)
2696  {
2697  /* Don't try to pfree a part of the original flat array */
2698  if (oldValue < eah->fstartptr || oldValue >= eah->fendptr)
2699  pfree(oldValue);
2700  }
2701 
2702  /* Done, return standard TOAST pointer for object */
2703  return EOHPGetRWDatum(&eah->hdr);
2704 }
2705 
2706 /*
2707  * array_set_slice :
2708  * This routine sets the value of a range of array locations (specified
2709  * by upper and lower subscript values) to new values passed as
2710  * another array.
2711  *
2712  * This handles both ordinary varlena arrays and fixed-length arrays.
2713  *
2714  * Inputs:
2715  * arraydatum: the initial array object (mustn't be NULL)
2716  * nSubscripts: number of subscripts supplied (must be same for upper/lower)
2717  * upperIndx[]: the upper subscript values
2718  * lowerIndx[]: the lower subscript values
2719  * upperProvided[]: true for provided upper subscript values
2720  * lowerProvided[]: true for provided lower subscript values
2721  * srcArrayDatum: the source for the inserted values
2722  * isNull: indicates whether srcArrayDatum is NULL
2723  * arraytyplen: pg_type.typlen for the array type
2724  * elmlen: pg_type.typlen for the array's element type
2725  * elmbyval: pg_type.typbyval for the array's element type
2726  * elmalign: pg_type.typalign for the array's element type
2727  *
2728  * Result:
2729  * A new array is returned, just like the old except for the
2730  * modified range. The original array object is not changed.
2731  *
2732  * Omitted upper and lower subscript values are replaced by the corresponding
2733  * array bound.
2734  *
2735  * For one-dimensional arrays only, we allow the array to be extended
2736  * by assigning to positions outside the existing subscript range; any
2737  * positions between the existing elements and the new ones are set to NULLs.
2738  * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
2739  *
2740  * NOTE: we assume it is OK to scribble on the provided index arrays
2741  * lowerIndx[] and upperIndx[]. These are generally just temporaries.
2742  *
2743  * NOTE: For assignments, we throw an error for silly subscripts etc,
2744  * rather than returning a NULL or empty array as the fetch operations do.
2745  */
2746 Datum
2748  int nSubscripts,
2749  int *upperIndx,
2750  int *lowerIndx,
2751  bool *upperProvided,
2752  bool *lowerProvided,
2753  Datum srcArrayDatum,
2754  bool isNull,
2755  int arraytyplen,
2756  int elmlen,
2757  bool elmbyval,
2758  char elmalign)
2759 {
2760  ArrayType *array;
2761  ArrayType *srcArray;
2762  ArrayType *newarray;
2763  int i,
2764  ndim,
2765  dim[MAXDIM],
2766  lb[MAXDIM],
2767  span[MAXDIM];
2768  bool newhasnulls;
2769  int nitems,
2770  nsrcitems,
2771  olddatasize,
2772  newsize,
2773  olditemsize,
2774  newitemsize,
2775  overheadlen,
2776  oldoverheadlen,
2777  addedbefore,
2778  addedafter,
2779  lenbefore,
2780  lenafter,
2781  itemsbefore,
2782  itemsafter,
2783  nolditems;
2784 
2785  /* Currently, assignment from a NULL source array is a no-op */
2786  if (isNull)
2787  return arraydatum;
2788 
2789  if (arraytyplen > 0)
2790  {
2791  /*
2792  * fixed-length arrays -- not got round to doing this...
2793  */
2794  ereport(ERROR,
2795  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2796  errmsg("updates on slices of fixed-length arrays not implemented")));
2797  }
2798 
2799  /* detoast arrays if necessary */
2800  array = DatumGetArrayTypeP(arraydatum);
2801  srcArray = DatumGetArrayTypeP(srcArrayDatum);
2802 
2803  /* note: we assume srcArray contains no toasted elements */
2804 
2805  ndim = ARR_NDIM(array);
2806 
2807  /*
2808  * if number of dims is zero, i.e. an empty array, create an array with
2809  * nSubscripts dimensions, and set the upper and lower bounds to the
2810  * supplied subscripts
2811  */
2812  if (ndim == 0)
2813  {
2814  Datum *dvalues;
2815  bool *dnulls;
2816  int nelems;
2817  Oid elmtype = ARR_ELEMTYPE(array);
2818 
2819  deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
2820  &dvalues, &dnulls, &nelems);
2821 
2822  for (i = 0; i < nSubscripts; i++)
2823  {
2824  if (!upperProvided[i] || !lowerProvided[i])
2825  ereport(ERROR,
2826  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2827  errmsg("array slice subscript must provide both boundaries"),
2828  errdetail("When assigning to a slice of an empty array value,"
2829  " slice boundaries must be fully specified.")));
2830 
2831  dim[i] = 1 + upperIndx[i] - lowerIndx[i];
2832  lb[i] = lowerIndx[i];
2833  }
2834 
2835  /* complain if too few source items; we ignore extras, however */
2836  if (nelems < ArrayGetNItems(nSubscripts, dim))
2837  ereport(ERROR,
2838  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2839  errmsg("source array too small")));
2840 
2841  return PointerGetDatum(construct_md_array(dvalues, dnulls, nSubscripts,
2842  dim, lb, elmtype,
2843  elmlen, elmbyval, elmalign));
2844  }
2845 
2846  if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
2847  ereport(ERROR,
2848  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2849  errmsg("wrong number of array subscripts")));
2850 
2851  /* copy dim/lb since we may modify them */
2852  memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
2853  memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
2854 
2855  newhasnulls = (ARR_HASNULL(array) || ARR_HASNULL(srcArray));
2856  addedbefore = addedafter = 0;
2857 
2858  /*
2859  * Check subscripts
2860  */
2861  if (ndim == 1)
2862  {
2863  Assert(nSubscripts == 1);
2864  if (!lowerProvided[0])
2865  lowerIndx[0] = lb[0];
2866  if (!upperProvided[0])
2867  upperIndx[0] = dim[0] + lb[0] - 1;
2868  if (lowerIndx[0] > upperIndx[0])
2869  ereport(ERROR,
2870  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2871  errmsg("upper bound cannot be less than lower bound")));
2872  if (lowerIndx[0] < lb[0])
2873  {
2874  if (upperIndx[0] < lb[0] - 1)
2875  newhasnulls = true; /* will insert nulls */
2876  addedbefore = lb[0] - lowerIndx[0];
2877  dim[0] += addedbefore;
2878  lb[0] = lowerIndx[0];
2879  }
2880  if (upperIndx[0] >= (dim[0] + lb[0]))
2881  {
2882  if (lowerIndx[0] > (dim[0] + lb[0]))
2883  newhasnulls = true; /* will insert nulls */
2884  addedafter = upperIndx[0] - (dim[0] + lb[0]) + 1;
2885  dim[0] += addedafter;
2886  }
2887  }
2888  else
2889  {
2890  /*
2891  * XXX currently we do not support extending multi-dimensional arrays
2892  * during assignment
2893  */
2894  for (i = 0; i < nSubscripts; i++)
2895  {
2896  if (!lowerProvided[i])
2897  lowerIndx[i] = lb[i];
2898  if (!upperProvided[i])
2899  upperIndx[i] = dim[i] + lb[i] - 1;
2900  if (lowerIndx[i] > upperIndx[i])
2901  ereport(ERROR,
2902  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2903  errmsg("upper bound cannot be less than lower bound")));
2904  if (lowerIndx[i] < lb[i] ||
2905  upperIndx[i] >= (dim[i] + lb[i]))
2906  ereport(ERROR,
2907  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2908  errmsg("array subscript out of range")));
2909  }
2910  /* fill any missing subscript positions with full array range */
2911  for (; i < ndim; i++)
2912  {
2913  lowerIndx[i] = lb[i];
2914  upperIndx[i] = dim[i] + lb[i] - 1;
2915  if (lowerIndx[i] > upperIndx[i])
2916  ereport(ERROR,
2917  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2918  errmsg("upper bound cannot be less than lower bound")));
2919  }
2920  }
2921 
2922  /* Do this mainly to check for overflow */
2923  nitems = ArrayGetNItems(ndim, dim);
2924 
2925  /*
2926  * Make sure source array has enough entries. Note we ignore the shape of
2927  * the source array and just read entries serially.
2928  */
2929  mda_get_range(ndim, span, lowerIndx, upperIndx);
2930  nsrcitems = ArrayGetNItems(ndim, span);
2931  if (nsrcitems > ArrayGetNItems(ARR_NDIM(srcArray), ARR_DIMS(srcArray)))
2932  ereport(ERROR,
2933  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2934  errmsg("source array too small")));
2935 
2936  /*
2937  * Compute space occupied by new entries, space occupied by replaced
2938  * entries, and required space for new array.
2939  */
2940  if (newhasnulls)
2941  overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
2942  else
2943  overheadlen = ARR_OVERHEAD_NONULLS(ndim);
2944  newitemsize = array_nelems_size(ARR_DATA_PTR(srcArray), 0,
2945  ARR_NULLBITMAP(srcArray), nsrcitems,
2946  elmlen, elmbyval, elmalign);
2947  oldoverheadlen = ARR_DATA_OFFSET(array);
2948  olddatasize = ARR_SIZE(array) - oldoverheadlen;
2949  if (ndim > 1)
2950  {
2951  /*
2952  * here we do not need to cope with extension of the array; it would
2953  * be a lot more complicated if we had to do so...
2954  */
2955  olditemsize = array_slice_size(ARR_DATA_PTR(array),
2956  ARR_NULLBITMAP(array),
2957  ndim, dim, lb,
2958  lowerIndx, upperIndx,
2959  elmlen, elmbyval, elmalign);
2960  lenbefore = lenafter = 0; /* keep compiler quiet */
2961  itemsbefore = itemsafter = nolditems = 0;
2962  }
2963  else
2964  {
2965  /*
2966  * here we must allow for possibility of slice larger than orig array
2967  * and/or not adjacent to orig array subscripts
2968  */
2969  int oldlb = ARR_LBOUND(array)[0];
2970  int oldub = oldlb + ARR_DIMS(array)[0] - 1;
2971  int slicelb = Max(oldlb, lowerIndx[0]);
2972  int sliceub = Min(oldub, upperIndx[0]);
2973  char *oldarraydata = ARR_DATA_PTR(array);
2974  bits8 *oldarraybitmap = ARR_NULLBITMAP(array);
2975 
2976  /* count/size of old array entries that will go before the slice */
2977  itemsbefore = Min(slicelb, oldub + 1) - oldlb;
2978  lenbefore = array_nelems_size(oldarraydata, 0, oldarraybitmap,
2979  itemsbefore,
2980  elmlen, elmbyval, elmalign);
2981  /* count/size of old array entries that will be replaced by slice */
2982  if (slicelb > sliceub)
2983  {
2984  nolditems = 0;
2985  olditemsize = 0;
2986  }
2987  else
2988  {
2989  nolditems = sliceub - slicelb + 1;
2990  olditemsize = array_nelems_size(oldarraydata + lenbefore,
2991  itemsbefore, oldarraybitmap,
2992  nolditems,
2993  elmlen, elmbyval, elmalign);
2994  }
2995  /* count/size of old array entries that will go after the slice */
2996  itemsafter = oldub + 1 - Max(sliceub + 1, oldlb);
2997  lenafter = olddatasize - lenbefore - olditemsize;
2998  }
2999 
3000  newsize = overheadlen + olddatasize - olditemsize + newitemsize;
3001 
3002  newarray = (ArrayType *) palloc0(newsize);
3003  SET_VARSIZE(newarray, newsize);
3004  newarray->ndim = ndim;
3005  newarray->dataoffset = newhasnulls ? overheadlen : 0;
3006  newarray->elemtype = ARR_ELEMTYPE(array);
3007  memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
3008  memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
3009 
3010  if (ndim > 1)
3011  {
3012  /*
3013  * here we do not need to cope with extension of the array; it would
3014  * be a lot more complicated if we had to do so...
3015  */
3016  array_insert_slice(newarray, array, srcArray,
3017  ndim, dim, lb,
3018  lowerIndx, upperIndx,
3019  elmlen, elmbyval, elmalign);
3020  }
3021  else
3022  {
3023  /* fill in data */
3024  memcpy((char *) newarray + overheadlen,
3025  (char *) array + oldoverheadlen,
3026  lenbefore);
3027  memcpy((char *) newarray + overheadlen + lenbefore,
3028  ARR_DATA_PTR(srcArray),
3029  newitemsize);
3030  memcpy((char *) newarray + overheadlen + lenbefore + newitemsize,
3031  (char *) array + oldoverheadlen + lenbefore + olditemsize,
3032  lenafter);
3033  /* fill in nulls bitmap if needed */
3034  if (newhasnulls)
3035  {
3036  bits8 *newnullbitmap = ARR_NULLBITMAP(newarray);
3037  bits8 *oldnullbitmap = ARR_NULLBITMAP(array);
3038 
3039  /* Zero the bitmap to handle marking inserted positions null */
3040  MemSet(newnullbitmap, 0, (nitems + 7) / 8);
3041  array_bitmap_copy(newnullbitmap, addedbefore,
3042  oldnullbitmap, 0,
3043  itemsbefore);
3044  array_bitmap_copy(newnullbitmap, lowerIndx[0] - lb[0],
3045  ARR_NULLBITMAP(srcArray), 0,
3046  nsrcitems);
3047  array_bitmap_copy(newnullbitmap, addedbefore + itemsbefore + nolditems,
3048  oldnullbitmap, itemsbefore + nolditems,
3049  itemsafter);
3050  }
3051  }
3052 
3053  return PointerGetDatum(newarray);
3054 }
3055 
3056 /*
3057  * array_ref : backwards compatibility wrapper for array_get_element
3058  *
3059  * This only works for detoasted/flattened varlena arrays, since the array
3060  * argument is declared as "ArrayType *". However there's enough code like
3061  * that to justify preserving this API.
3062  */
3063 Datum
3064 array_ref(ArrayType *array, int nSubscripts, int *indx,
3065  int arraytyplen, int elmlen, bool elmbyval, char elmalign,
3066  bool *isNull)
3067 {
3068  return array_get_element(PointerGetDatum(array), nSubscripts, indx,
3069  arraytyplen, elmlen, elmbyval, elmalign,
3070  isNull);
3071 }
3072 
3073 /*
3074  * array_set : backwards compatibility wrapper for array_set_element
3075  *
3076  * This only works for detoasted/flattened varlena arrays, since the array
3077  * argument and result are declared as "ArrayType *". However there's enough
3078  * code like that to justify preserving this API.
3079  */
3080 ArrayType *
3081 array_set(ArrayType *array, int nSubscripts, int *indx,
3082  Datum dataValue, bool isNull,
3083  int arraytyplen, int elmlen, bool elmbyval, char elmalign)
3084 {
3086  nSubscripts, indx,
3087  dataValue, isNull,
3088  arraytyplen,
3089  elmlen, elmbyval, elmalign));
3090 }
3091 
3092 /*
3093  * array_map()
3094  *
3095  * Map an array through an arbitrary expression. Return a new array with
3096  * the same dimensions and each source element transformed by the given,
3097  * already-compiled expression. Each source element is placed in the
3098  * innermost_caseval/innermost_casenull fields of the ExprState.
3099  *
3100  * Parameters are:
3101  * * arrayd: Datum representing array argument.
3102  * * exprstate: ExprState representing the per-element transformation.
3103  * * econtext: context for expression evaluation.
3104  * * retType: OID of element type of output array. This must be the same as,
3105  * or binary-compatible with, the result type of the expression. It might
3106  * be different from the input array's element type.
3107  * * amstate: workspace for array_map. Must be zeroed by caller before
3108  * first call, and not touched after that.
3109  *
3110  * It is legitimate to pass a freshly-zeroed ArrayMapState on each call,
3111  * but better performance can be had if the state can be preserved across
3112  * a series of calls.
3113  *
3114  * NB: caller must assure that input array is not NULL. NULL elements in
3115  * the array are OK however.
3116  * NB: caller should be running in econtext's per-tuple memory context.
3117  */
3118 Datum
3120  ExprState *exprstate, ExprContext *econtext,
3121  Oid retType, ArrayMapState *amstate)
3122 {
3123  AnyArrayType *v = DatumGetAnyArrayP(arrayd);
3124  ArrayType *result;
3125  Datum *values;
3126  bool *nulls;
3127  int *dim;
3128  int ndim;
3129  int nitems;
3130  int i;
3131  int32 nbytes = 0;
3132  int32 dataoffset;
3133  bool hasnulls;
3134  Oid inpType;
3135  int inp_typlen;
3136  bool inp_typbyval;
3137  char inp_typalign;
3138  int typlen;
3139  bool typbyval;
3140  char typalign;
3141  array_iter iter;
3142  ArrayMetaState *inp_extra;
3143  ArrayMetaState *ret_extra;
3144  Datum *transform_source = exprstate->innermost_caseval;
3145  bool *transform_source_isnull = exprstate->innermost_casenull;
3146 
3147  inpType = AARR_ELEMTYPE(v);
3148  ndim = AARR_NDIM(v);
3149  dim = AARR_DIMS(v);
3150  nitems = ArrayGetNItems(ndim, dim);
3151 
3152  /* Check for empty array */
3153  if (nitems <= 0)
3154  {
3155  /* Return empty array */
3156  return PointerGetDatum(construct_empty_array(retType));
3157  }
3158 
3159  /*
3160  * We arrange to look up info about input and return element types only
3161  * once per series of calls, assuming the element type doesn't change
3162  * underneath us.
3163  */
3164  inp_extra = &amstate->inp_extra;
3165  ret_extra = &amstate->ret_extra;
3166 
3167  if (inp_extra->element_type != inpType)
3168  {
3169  get_typlenbyvalalign(inpType,
3170  &inp_extra->typlen,
3171  &inp_extra->typbyval,
3172  &inp_extra->typalign);
3173  inp_extra->element_type = inpType;
3174  }
3175  inp_typlen = inp_extra->typlen;
3176  inp_typbyval = inp_extra->typbyval;
3177  inp_typalign = inp_extra->typalign;
3178 
3179  if (ret_extra->element_type != retType)
3180  {
3181  get_typlenbyvalalign(retType,
3182  &ret_extra->typlen,
3183  &ret_extra->typbyval,
3184  &ret_extra->typalign);
3185  ret_extra->element_type = retType;
3186  }
3187  typlen = ret_extra->typlen;
3188  typbyval = ret_extra->typbyval;
3189  typalign = ret_extra->typalign;
3190 
3191  /* Allocate temporary arrays for new values */
3192  values = (Datum *) palloc(nitems * sizeof(Datum));
3193  nulls = (bool *) palloc(nitems * sizeof(bool));
3194 
3195  /* Loop over source data */
3196  array_iter_setup(&iter, v);
3197  hasnulls = false;
3198 
3199  for (i = 0; i < nitems; i++)
3200  {
3201  /* Get source element, checking for NULL */
3202  *transform_source =
3203  array_iter_next(&iter, transform_source_isnull, i,
3204  inp_typlen, inp_typbyval, inp_typalign);
3205 
3206  /* Apply the given expression to source element */
3207  values[i] = ExecEvalExpr(exprstate, econtext, &nulls[i]);
3208 
3209  if (nulls[i])
3210  hasnulls = true;
3211  else
3212  {
3213  /* Ensure data is not toasted */
3214  if (typlen == -1)
3215  values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
3216  /* Update total result size */
3217  nbytes = att_addlength_datum(nbytes, typlen, values[i]);
3218  nbytes = att_align_nominal(nbytes, typalign);
3219  /* check for overflow of total request */
3220  if (!AllocSizeIsValid(nbytes))
3221  ereport(ERROR,
3222  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3223  errmsg("array size exceeds the maximum allowed (%d)",
3224  (int) MaxAllocSize)));
3225  }
3226  }
3227 
3228  /* Allocate and fill the result array */
3229  if (hasnulls)
3230  {
3231  dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
3232  nbytes += dataoffset;
3233  }
3234  else
3235  {
3236  dataoffset = 0; /* marker for no null bitmap */
3237  nbytes += ARR_OVERHEAD_NONULLS(ndim);
3238  }
3239  result = (ArrayType *) palloc0(nbytes);
3240  SET_VARSIZE(result, nbytes);
3241  result->ndim = ndim;
3242  result->dataoffset = dataoffset;
3243  result->elemtype = retType;
3244  memcpy(ARR_DIMS(result), AARR_DIMS(v), ndim * sizeof(int));
3245  memcpy(ARR_LBOUND(result), AARR_LBOUND(v), ndim * sizeof(int));
3246 
3247  CopyArrayEls(result,
3248  values, nulls, nitems,
3249  typlen, typbyval, typalign,
3250  false);
3251 
3252  /*
3253  * Note: do not risk trying to pfree the results of the called expression
3254  */
3255  pfree(values);
3256  pfree(nulls);
3257 
3258  return PointerGetDatum(result);
3259 }
3260 
3261 /*
3262  * construct_array --- simple method for constructing an array object
3263  *
3264  * elems: array of Datum items to become the array contents
3265  * (NULL element values are not supported).
3266  * nelems: number of items
3267  * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
3268  *
3269  * A palloc'd 1-D array object is constructed and returned. Note that
3270  * elem values will be copied into the object even if pass-by-ref type.
3271  * Also note the result will be 0-D not 1-D if nelems = 0.
3272  *
3273  * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
3274  * from the system catalogs, given the elmtype. However, the caller is
3275  * in a better position to cache this info across multiple uses, or even
3276  * to hard-wire values if the element type is hard-wired.
3277  */
3278 ArrayType *
3279 construct_array(Datum *elems, int nelems,
3280  Oid elmtype,
3281  int elmlen, bool elmbyval, char elmalign)
3282 {
3283  int dims[1];
3284  int lbs[1];
3285 
3286  dims[0] = nelems;
3287  lbs[0] = 1;
3288 
3289  return construct_md_array(elems, NULL, 1, dims, lbs,
3290  elmtype, elmlen, elmbyval, elmalign);
3291 }
3292 
3293 /*
3294  * construct_md_array --- simple method for constructing an array object
3295  * with arbitrary dimensions and possible NULLs
3296  *
3297  * elems: array of Datum items to become the array contents
3298  * nulls: array of is-null flags (can be NULL if no nulls)
3299  * ndims: number of dimensions
3300  * dims: integer array with size of each dimension
3301  * lbs: integer array with lower bound of each dimension
3302  * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
3303  *
3304  * A palloc'd ndims-D array object is constructed and returned. Note that
3305  * elem values will be copied into the object even if pass-by-ref type.
3306  * Also note the result will be 0-D not ndims-D if any dims[i] = 0.
3307  *
3308  * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
3309  * from the system catalogs, given the elmtype. However, the caller is
3310  * in a better position to cache this info across multiple uses, or even
3311  * to hard-wire values if the element type is hard-wired.
3312  */
3313 ArrayType *
3315  bool *nulls,
3316  int ndims,
3317  int *dims,
3318  int *lbs,
3319  Oid elmtype, int elmlen, bool elmbyval, char elmalign)
3320 {
3321  ArrayType *result;
3322  bool hasnulls;
3323  int32 nbytes;
3324  int32 dataoffset;
3325  int i;
3326  int nelems;
3327 
3328  if (ndims < 0) /* we do allow zero-dimension arrays */
3329  ereport(ERROR,
3330  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3331  errmsg("invalid number of dimensions: %d", ndims)));
3332  if (ndims > MAXDIM)
3333  ereport(ERROR,
3334  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3335  errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
3336  ndims, MAXDIM)));
3337 
3338  nelems = ArrayGetNItems(ndims, dims);
3339 
3340  /* if ndims <= 0 or any dims[i] == 0, return empty array */
3341  if (nelems <= 0)
3342  return construct_empty_array(elmtype);
3343 
3344  /* compute required space */
3345  nbytes = 0;
3346  hasnulls = false;
3347  for (i = 0; i < nelems; i++)
3348  {
3349  if (nulls && nulls[i])
3350  {
3351  hasnulls = true;
3352  continue;
3353  }
3354  /* make sure data is not toasted */
3355  if (elmlen == -1)
3356  elems[i] = PointerGetDatum(PG_DETOAST_DATUM(elems[i]));
3357  nbytes = att_addlength_datum(nbytes, elmlen, elems[i]);
3358  nbytes = att_align_nominal(nbytes, elmalign);
3359  /* check for overflow of total request */
3360  if (!AllocSizeIsValid(nbytes))
3361  ereport(ERROR,
3362  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3363  errmsg("array size exceeds the maximum allowed (%d)",
3364  (int) MaxAllocSize)));
3365  }
3366 
3367  /* Allocate and initialize result array */
3368  if (hasnulls)
3369  {
3370  dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nelems);
3371  nbytes += dataoffset;
3372  }
3373  else
3374  {
3375  dataoffset = 0; /* marker for no null bitmap */
3376  nbytes += ARR_OVERHEAD_NONULLS(ndims);
3377  }
3378  result = (ArrayType *) palloc0(nbytes);
3379  SET_VARSIZE(result, nbytes);
3380  result->ndim = ndims;
3381  result->dataoffset = dataoffset;
3382  result->elemtype = elmtype;
3383  memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
3384  memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
3385 
3386  CopyArrayEls(result,
3387  elems, nulls, nelems,
3388  elmlen, elmbyval, elmalign,
3389  false);
3390 
3391  return result;
3392 }
3393 
3394 /*
3395  * construct_empty_array --- make a zero-dimensional array of given type
3396  */
3397 ArrayType *
3399 {
3400  ArrayType *result;
3401 
3402  result = (ArrayType *) palloc0(sizeof(ArrayType));
3403  SET_VARSIZE(result, sizeof(ArrayType));
3404  result->ndim = 0;
3405  result->dataoffset = 0;
3406  result->elemtype = elmtype;
3407  return result;
3408 }
3409 
3410 /*
3411  * construct_empty_expanded_array: make an empty expanded array
3412  * given only type information. (metacache can be NULL if not needed.)
3413  */
3416  MemoryContext parentcontext,
3417  ArrayMetaState *metacache)
3418 {
3419  ArrayType *array = construct_empty_array(element_type);
3420  Datum d;
3421 
3422  d = expand_array(PointerGetDatum(array), parentcontext, metacache);
3423  pfree(array);
3424  return (ExpandedArrayHeader *) DatumGetEOHP(d);
3425 }
3426 
3427 /*
3428  * deconstruct_array --- simple method for extracting data from an array
3429  *
3430  * array: array object to examine (must not be NULL)
3431  * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
3432  * elemsp: return value, set to point to palloc'd array of Datum values
3433  * nullsp: return value, set to point to palloc'd array of isnull markers
3434  * nelemsp: return value, set to number of extracted values
3435  *
3436  * The caller may pass nullsp == NULL if it does not support NULLs in the
3437  * array. Note that this produces a very uninformative error message,
3438  * so do it only in cases where a NULL is really not expected.
3439  *
3440  * If array elements are pass-by-ref data type, the returned Datums will
3441  * be pointers into the array object.
3442  *
3443  * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
3444  * from the system catalogs, given the elmtype. However, in most current
3445  * uses the type is hard-wired into the caller and so we can save a lookup
3446  * cycle by hard-wiring the type info as well.
3447  */
3448 void
3450  Oid elmtype,
3451  int elmlen, bool elmbyval, char elmalign,
3452  Datum **elemsp, bool **nullsp, int *nelemsp)
3453 {
3454  Datum *elems;
3455  bool *nulls;
3456  int nelems;
3457  char *p;
3458  bits8 *bitmap;
3459  int bitmask;
3460  int i;
3461 
3462  Assert(ARR_ELEMTYPE(array) == elmtype);
3463 
3464  nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
3465  *elemsp = elems = (Datum *) palloc(nelems * sizeof(Datum));
3466  if (nullsp)
3467  *nullsp = nulls = (bool *) palloc0(nelems * sizeof(bool));
3468  else
3469  nulls = NULL;
3470  *nelemsp = nelems;
3471 
3472  p = ARR_DATA_PTR(array);
3473  bitmap = ARR_NULLBITMAP(array);
3474  bitmask = 1;
3475 
3476  for (i = 0; i < nelems; i++)
3477  {
3478  /* Get source element, checking for NULL */
3479  if (bitmap && (*bitmap & bitmask) == 0)
3480  {
3481  elems[i] = (Datum) 0;
3482  if (nulls)
3483  nulls[i] = true;
3484  else
3485  ereport(ERROR,
3486  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
3487  errmsg("null array element not allowed in this context")));
3488  }
3489  else
3490  {
3491  elems[i] = fetch_att(p, elmbyval, elmlen);
3492  p = att_addlength_pointer(p, elmlen, p);
3493  p = (char *) att_align_nominal(p, elmalign);
3494  }
3495 
3496  /* advance bitmap pointer if any */
3497  if (bitmap)
3498  {
3499  bitmask <<= 1;
3500  if (bitmask == 0x100)
3501  {
3502  bitmap++;
3503  bitmask = 1;
3504  }
3505  }
3506  }
3507 }
3508 
3509 /*
3510  * array_contains_nulls --- detect whether an array has any null elements
3511  *
3512  * This gives an accurate answer, whereas testing ARR_HASNULL only tells
3513  * if the array *might* contain a null.
3514  */
3515 bool
3517 {
3518  int nelems;
3519  bits8 *bitmap;
3520  int bitmask;
3521 
3522  /* Easy answer if there's no null bitmap */
3523  if (!ARR_HASNULL(array))
3524  return false;
3525 
3526  nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
3527 
3528  bitmap = ARR_NULLBITMAP(array);
3529 
3530  /* check whole bytes of the bitmap byte-at-a-time */
3531  while (nelems >= 8)
3532  {
3533  if (*bitmap != 0xFF)
3534  return true;
3535  bitmap++;
3536  nelems -= 8;
3537  }
3538 
3539  /* check last partial byte */
3540  bitmask = 1;
3541  while (nelems > 0)
3542  {
3543  if ((*bitmap & bitmask) == 0)
3544  return true;
3545  bitmask <<= 1;
3546  nelems--;
3547  }
3548 
3549  return false;
3550 }
3551 
3552 
3553 /*
3554  * array_eq :
3555  * compares two arrays for equality
3556  * result :
3557  * returns true if the arrays are equal, false otherwise.
3558  *
3559  * Note: we do not use array_cmp here, since equality may be meaningful in
3560  * datatypes that don't have a total ordering (and hence no btree support).
3561  */
3562 Datum
3564 {
3565  AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
3566  AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
3567  Oid collation = PG_GET_COLLATION();
3568  int ndims1 = AARR_NDIM(array1);
3569  int ndims2 = AARR_NDIM(array2);
3570  int *dims1 = AARR_DIMS(array1);
3571  int *dims2 = AARR_DIMS(array2);
3572  int *lbs1 = AARR_LBOUND(array1);
3573  int *lbs2 = AARR_LBOUND(array2);
3574  Oid element_type = AARR_ELEMTYPE(array1);
3575  bool result = true;
3576  int nitems;
3577  TypeCacheEntry *typentry;
3578  int typlen;
3579  bool typbyval;
3580  char typalign;
3581  array_iter it1;
3582  array_iter it2;
3583  int i;
3584  FunctionCallInfoData locfcinfo;
3585 
3586  if (element_type != AARR_ELEMTYPE(array2))
3587  ereport(ERROR,
3588  (errcode(ERRCODE_DATATYPE_MISMATCH),
3589  errmsg("cannot compare arrays of different element types")));
3590 
3591  /* fast path if the arrays do not have the same dimensionality */
3592  if (ndims1 != ndims2 ||
3593  memcmp(dims1, dims2, ndims1 * sizeof(int)) != 0 ||
3594  memcmp(lbs1, lbs2, ndims1 * sizeof(int)) != 0)
3595  result = false;
3596  else
3597  {
3598  /*
3599  * We arrange to look up the equality function only once per series of
3600  * calls, assuming the element type doesn't change underneath us. The
3601  * typcache is used so that we have no memory leakage when being used
3602  * as an index support function.
3603  */
3604  typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3605  if (typentry == NULL ||
3606  typentry->type_id != element_type)
3607  {
3608  typentry = lookup_type_cache(element_type,
3610  if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
3611  ereport(ERROR,
3612  (errcode(ERRCODE_UNDEFINED_FUNCTION),
3613  errmsg("could not identify an equality operator for type %s",
3614  format_type_be(element_type))));
3615  fcinfo->flinfo->fn_extra = (void *) typentry;
3616  }
3617  typlen = typentry->typlen;
3618  typbyval = typentry->typbyval;
3619  typalign = typentry->typalign;
3620 
3621  /*
3622  * apply the operator to each pair of array elements.
3623  */
3624  InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
3625  collation, NULL, NULL);
3626 
3627  /* Loop over source data */
3628  nitems = ArrayGetNItems(ndims1, dims1);
3629  array_iter_setup(&it1, array1);
3630  array_iter_setup(&it2, array2);
3631 
3632  for (i = 0; i < nitems; i++)
3633  {
3634  Datum elt1;
3635  Datum elt2;
3636  bool isnull1;
3637  bool isnull2;
3638  bool oprresult;
3639 
3640  /* Get elements, checking for NULL */
3641  elt1 = array_iter_next(&it1, &isnull1, i,
3642  typlen, typbyval, typalign);
3643  elt2 = array_iter_next(&it2, &isnull2, i,
3644  typlen, typbyval, typalign);
3645 
3646  /*
3647  * We consider two NULLs equal; NULL and not-NULL are unequal.
3648  */
3649  if (isnull1 && isnull2)
3650  continue;
3651  if (isnull1 || isnull2)
3652  {
3653  result = false;
3654  break;
3655  }
3656 
3657  /*
3658  * Apply the operator to the element pair
3659  */
3660  locfcinfo.arg[0] = elt1;
3661  locfcinfo.arg[1] = elt2;
3662  locfcinfo.argnull[0] = false;
3663  locfcinfo.argnull[1] = false;
3664  locfcinfo.isnull = false;
3665  oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
3666  if (!oprresult)
3667  {
3668  result = false;
3669  break;
3670  }
3671  }
3672  }
3673 
3674  /* Avoid leaking memory when handed toasted input. */
3675  AARR_FREE_IF_COPY(array1, 0);
3676  AARR_FREE_IF_COPY(array2, 1);
3677 
3678  PG_RETURN_BOOL(result);
3679 }
3680 
3681 
3682 /*-----------------------------------------------------------------------------
3683  * array-array bool operators:
3684  * Given two arrays, iterate comparison operators
3685  * over the array. Uses logic similar to text comparison
3686  * functions, except element-by-element instead of
3687  * character-by-character.
3688  *----------------------------------------------------------------------------
3689  */
3690 
3691 Datum
3693 {
3695 }
3696 
3697 Datum
3699 {
3700  PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
3701 }
3702 
3703 Datum
3705 {
3706  PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
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_INT32(array_cmp(fcinfo));
3725 }
3726 
3727 /*
3728  * array_cmp()
3729  * Internal comparison function for arrays.
3730  *
3731  * Returns -1, 0 or 1
3732  */
3733 static int
3735 {
3736  AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
3737  AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
3738  Oid collation = PG_GET_COLLATION();
3739  int ndims1 = AARR_NDIM(array1);
3740  int ndims2 = AARR_NDIM(array2);
3741  int *dims1 = AARR_DIMS(array1);
3742  int *dims2 = AARR_DIMS(array2);
3743  int nitems1 = ArrayGetNItems(ndims1, dims1);
3744  int nitems2 = ArrayGetNItems(ndims2, dims2);
3745  Oid element_type = AARR_ELEMTYPE(array1);
3746  int result = 0;
3747  TypeCacheEntry *typentry;
3748  int typlen;
3749  bool typbyval;
3750  char typalign;
3751  int min_nitems;
3752  array_iter it1;
3753  array_iter it2;
3754  int i;
3755  FunctionCallInfoData locfcinfo;
3756 
3757  if (element_type != AARR_ELEMTYPE(array2))
3758  ereport(ERROR,
3759  (errcode(ERRCODE_DATATYPE_MISMATCH),
3760  errmsg("cannot compare arrays of different element types")));
3761 
3762  /*
3763  * We arrange to look up the comparison function only once per series of
3764  * calls, assuming the element type doesn't change underneath us. The
3765  * typcache is used so that we have no memory leakage when being used as
3766  * an index support function.
3767  */
3768  typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3769  if (typentry == NULL ||
3770  typentry->type_id != element_type)
3771  {
3772  typentry = lookup_type_cache(element_type,
3774  if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
3775  ereport(ERROR,
3776  (errcode(ERRCODE_UNDEFINED_FUNCTION),
3777  errmsg("could not identify a comparison function for type %s",
3778  format_type_be(element_type))));
3779  fcinfo->flinfo->fn_extra = (void *) typentry;
3780  }
3781  typlen = typentry->typlen;
3782  typbyval = typentry->typbyval;
3783  typalign = typentry->typalign;
3784 
3785  /*
3786  * apply the operator to each pair of array elements.
3787  */
3788  InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2,
3789  collation, NULL, NULL);
3790 
3791  /* Loop over source data */
3792  min_nitems = Min(nitems1, nitems2);
3793  array_iter_setup(&it1, array1);
3794  array_iter_setup(&it2, array2);
3795 
3796  for (i = 0; i < min_nitems; i++)
3797  {
3798  Datum elt1;
3799  Datum elt2;
3800  bool isnull1;
3801  bool isnull2;
3802  int32 cmpresult;
3803 
3804  /* Get elements, checking for NULL */
3805  elt1 = array_iter_next(&it1, &isnull1, i, typlen, typbyval, typalign);
3806  elt2 = array_iter_next(&it2, &isnull2, i, typlen, typbyval, typalign);
3807 
3808  /*
3809  * We consider two NULLs equal; NULL > not-NULL.
3810  */
3811  if (isnull1 && isnull2)
3812  continue;
3813  if (isnull1)
3814  {
3815  /* arg1 is greater than arg2 */
3816  result = 1;
3817  break;
3818  }
3819  if (isnull2)
3820  {
3821  /* arg1 is less than arg2 */
3822  result = -1;
3823  break;
3824  }
3825 
3826  /* Compare the pair of elements */
3827  locfcinfo.arg[0] = elt1;
3828  locfcinfo.arg[1] = elt2;
3829  locfcinfo.argnull[0] = false;
3830  locfcinfo.argnull[1] = false;
3831  locfcinfo.isnull = false;
3832  cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
3833 
3834  if (cmpresult == 0)
3835  continue; /* equal */
3836 
3837  if (cmpresult < 0)
3838  {
3839  /* arg1 is less than arg2 */
3840  result = -1;
3841  break;
3842  }
3843  else
3844  {
3845  /* arg1 is greater than arg2 */
3846  result = 1;
3847  break;
3848  }
3849  }
3850 
3851  /*
3852  * If arrays contain same data (up to end of shorter one), apply
3853  * additional rules to sort by dimensionality. The relative significance
3854  * of the different bits of information is historical; mainly we just care
3855  * that we don't say "equal" for arrays of different dimensionality.
3856  */
3857  if (result == 0)
3858  {
3859  if (nitems1 != nitems2)
3860  result = (nitems1 < nitems2) ? -1 : 1;
3861  else if (ndims1 != ndims2)
3862  result = (ndims1 < ndims2) ? -1 : 1;
3863  else
3864  {
3865  for (i = 0; i < ndims1; i++)
3866  {
3867  if (dims1[i] != dims2[i])
3868  {
3869  result = (dims1[i] < dims2[i]) ? -1 : 1;
3870  break;
3871  }
3872  }
3873  if (result == 0)
3874  {
3875  int *lbound1 = AARR_LBOUND(array1);
3876  int *lbound2 = AARR_LBOUND(array2);
3877 
3878  for (i = 0; i < ndims1; i++)
3879  {
3880  if (lbound1[i] != lbound2[i])
3881  {
3882  result = (lbound1[i] < lbound2[i]) ? -1 : 1;
3883  break;
3884  }
3885  }
3886  }
3887  }
3888  }
3889 
3890  /* Avoid leaking memory when handed toasted input. */
3891  AARR_FREE_IF_COPY(array1, 0);
3892  AARR_FREE_IF_COPY(array2, 1);
3893 
3894  return result;
3895 }
3896 
3897 
3898 /*-----------------------------------------------------------------------------
3899  * array hashing
3900  * Hash the elements and combine the results.
3901  *----------------------------------------------------------------------------
3902  */
3903 
3904 Datum
3906 {
3907  AnyArrayType *array = PG_GETARG_ANY_ARRAY_P(0);
3908  int ndims = AARR_NDIM(array);
3909  int *dims = AARR_DIMS(array);
3910  Oid element_type = AARR_ELEMTYPE(array);
3911  uint32 result = 1;
3912  int nitems;
3913  TypeCacheEntry *typentry;
3914  int typlen;
3915  bool typbyval;
3916  char typalign;
3917  int i;
3918  array_iter iter;
3919  FunctionCallInfoData locfcinfo;
3920 
3921  /*
3922  * We arrange to look up the hash function only once per series of calls,
3923  * assuming the element type doesn't change underneath us. The typcache
3924  * is used so that we have no memory leakage when being used as an index
3925  * support function.
3926  */
3927  typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3928  if (typentry == NULL ||
3929  typentry->type_id != element_type)
3930  {
3931  typentry = lookup_type_cache(element_type,
3933  if (!OidIsValid(typentry->hash_proc_finfo.fn_oid))
3934  ereport(ERROR,
3935  (errcode(ERRCODE_UNDEFINED_FUNCTION),
3936  errmsg("could not identify a hash function for type %s",
3937  format_type_be(element_type))));
3938  fcinfo->flinfo->fn_extra = (void *) typentry;
3939  }
3940  typlen = typentry->typlen;
3941  typbyval = typentry->typbyval;
3942  typalign = typentry->typalign;
3943 
3944  /*
3945  * apply the hash function to each array element.
3946  */
3947  InitFunctionCallInfoData(locfcinfo, &typentry->hash_proc_finfo, 1,
3948  InvalidOid, NULL, NULL);
3949 
3950  /* Loop over source data */
3951  nitems = ArrayGetNItems(ndims, dims);
3952  array_iter_setup(&iter, array);
3953 
3954  for (i = 0; i < nitems; i++)
3955  {
3956  Datum elt;
3957  bool isnull;
3958  uint32 elthash;
3959 
3960  /* Get element, checking for NULL */
3961  elt = array_iter_next(&iter, &isnull, i, typlen, typbyval, typalign);
3962 
3963  if (isnull)
3964  {
3965  /* Treat nulls as having hashvalue 0 */
3966  elthash = 0;
3967  }
3968  else
3969  {
3970  /* Apply the hash function */
3971  locfcinfo.arg[0] = elt;
3972  locfcinfo.argnull[0] = false;
3973  locfcinfo.isnull = false;
3974  elthash = DatumGetUInt32(FunctionCallInvoke(&locfcinfo));
3975  }
3976 
3977  /*
3978  * Combine hash values of successive elements by multiplying the
3979  * current value by 31 and adding on the new element's hash value.
3980  *
3981  * The result is a sum in which each element's hash value is
3982  * multiplied by a different power of 31. This is modulo 2^32
3983  * arithmetic, and the powers of 31 modulo 2^32 form a cyclic group of
3984  * order 2^27. So for arrays of up to 2^27 elements, each element's
3985  * hash value is multiplied by a different (odd) number, resulting in
3986  * a good mixing of all the elements' hash values.
3987  */
3988  result = (result << 5) - result + elthash;
3989  }
3990 
3991  /* Avoid leaking memory when handed toasted input. */
3992  AARR_FREE_IF_COPY(array, 0);
3993 
3994  PG_RETURN_UINT32(result);
3995 }
3996 
3997 /*
3998  * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
3999  * Otherwise, similar to hash_array.
4000  */
4001 Datum
4003 {
4004  AnyArrayType *array = PG_GETARG_ANY_ARRAY_P(0);
4005  uint64 seed = PG_GETARG_INT64(1);
4006  int ndims = AARR_NDIM(array);
4007  int *dims = AARR_DIMS(array);
4008  Oid element_type = AARR_ELEMTYPE(array);
4009  uint64 result = 1;
4010  int nitems;
4011  TypeCacheEntry *typentry;
4012  int typlen;
4013  bool typbyval;
4014  char typalign;
4015  int i;
4016  array_iter iter;
4017  FunctionCallInfoData locfcinfo;
4018 
4019  typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
4020  if (typentry == NULL ||
4021  typentry->type_id != element_type)
4022  {
4023  typentry = lookup_type_cache(element_type,
4025  if (!OidIsValid(typentry->hash_extended_proc_finfo.fn_oid))
4026  ereport(ERROR,
4027  (errcode(ERRCODE_UNDEFINED_FUNCTION),
4028  errmsg("could not identify an extended hash function for type %s",
4029  format_type_be(element_type))));
4030  fcinfo->flinfo->fn_extra = (void *) typentry;
4031  }
4032  typlen = typentry->typlen;
4033  typbyval = typentry->typbyval;
4034  typalign = typentry->typalign;
4035 
4036  InitFunctionCallInfoData(locfcinfo, &typentry->hash_extended_proc_finfo, 2,
4037  InvalidOid, NULL, NULL);
4038 
4039  /* Loop over source data */
4040  nitems = ArrayGetNItems(ndims, dims);
4041  array_iter_setup(&iter, array);
4042 
4043  for (i = 0; i < nitems; i++)
4044  {
4045  Datum elt;
4046  bool isnull;
4047  uint64 elthash;
4048 
4049  /* Get element, checking for NULL */
4050  elt = array_iter_next(&iter, &isnull, i, typlen, typbyval, typalign);
4051 
4052  if (isnull)
4053  {
4054  elthash = 0;
4055  }
4056  else
4057  {
4058  /* Apply the hash function */
4059  locfcinfo.arg[0] = elt;
4060  locfcinfo.arg[1] = Int64GetDatum(seed);
4061  locfcinfo.argnull[0] = false;
4062  locfcinfo.argnull[1] = false;
4063  locfcinfo.isnull = false;
4064  elthash = DatumGetUInt64(FunctionCallInvoke(&locfcinfo));
4065  }
4066 
4067  result = (result << 5) - result + elthash;
4068  }
4069 
4070  AARR_FREE_IF_COPY(array, 0);
4071 
4072  PG_RETURN_UINT64(result);
4073 }
4074 
4075 
4076 /*-----------------------------------------------------------------------------
4077  * array overlap/containment comparisons
4078  * These use the same methods of comparing array elements as array_eq.
4079  * We consider only the elements of the arrays, ignoring dimensionality.
4080  *----------------------------------------------------------------------------
4081  */
4082 
4083 /*
4084  * array_contain_compare :
4085  * compares two arrays for overlap/containment
4086  *
4087  * When matchall is true, return true if all members of array1 are in array2.
4088  * When matchall is false, return true if any members of array1 are in array2.
4089  */
4090 static bool
4092  bool matchall, void **fn_extra)
4093 {
4094  bool result = matchall;
4095  Oid element_type = AARR_ELEMTYPE(array1);
4096  TypeCacheEntry *typentry;
4097  int nelems1;
4098  Datum *values2;
4099  bool *nulls2;
4100  int nelems2;
4101  int typlen;
4102  bool typbyval;
4103  char typalign;
4104  int i;
4105  int j;
4106  array_iter it1;
4107  FunctionCallInfoData locfcinfo;
4108 
4109  if (element_type != AARR_ELEMTYPE(array2))
4110  ereport(ERROR,
4111  (errcode(ERRCODE_DATATYPE_MISMATCH),
4112  errmsg("cannot compare arrays of different element types")));
4113 
4114  /*
4115  * We arrange to look up the equality function only once per series of
4116  * calls, assuming the element type doesn't change underneath us. The
4117  * typcache is used so that we have no memory leakage when being used as
4118  * an index support function.
4119  */
4120  typentry = (TypeCacheEntry *) *fn_extra;
4121  if (typentry == NULL ||
4122  typentry->type_id != element_type)
4123  {
4124  typentry = lookup_type_cache(element_type,
4126  if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
4127  ereport(ERROR,
4128  (errcode(ERRCODE_UNDEFINED_FUNCTION),
4129  errmsg("could not identify an equality operator for type %s",
4130  format_type_be(element_type))));
4131  *fn_extra = (void *) typentry;
4132  }
4133  typlen = typentry->typlen;
4134  typbyval = typentry->typbyval;
4135  typalign = typentry->typalign;
4136 
4137  /*
4138  * Since we probably will need to scan array2 multiple times, it's
4139  * worthwhile to use deconstruct_array on it. We scan array1 the hard way
4140  * however, since we very likely won't need to look at all of it.
4141  */
4142  if (VARATT_IS_EXPANDED_HEADER(array2))
4143  {
4144  /* This should be safe even if input is read-only */
4145  deconstruct_expanded_array(&(array2->xpn));
4146  values2 = array2->xpn.dvalues;
4147  nulls2 = array2->xpn.dnulls;
4148  nelems2 = array2->xpn.nelems;
4149  }
4150  else
4151  deconstruct_array(&(array2->flt),
4152  element_type, typlen, typbyval, typalign,
4153  &values2, &nulls2, &nelems2);
4154 
4155  /*
4156  * Apply the comparison operator to each pair of array elements.
4157  */
4158  InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
4159  collation, NULL, NULL);
4160 
4161  /* Loop over source data */
4162  nelems1 = ArrayGetNItems(AARR_NDIM(array1), AARR_DIMS(array1));
4163  array_iter_setup(&it1, array1);
4164 
4165  for (i = 0; i < nelems1; i++)
4166  {
4167  Datum elt1;
4168  bool isnull1;
4169 
4170  /* Get element, checking for NULL */
4171  elt1 = array_iter_next(&it1, &isnull1, i, typlen, typbyval, typalign);
4172 
4173  /*
4174  * We assume that the comparison operator is strict, so a NULL can't
4175  * match anything. XXX this diverges from the "NULL=NULL" behavior of
4176  * array_eq, should we act like that?
4177  */
4178  if (isnull1)
4179  {
4180  if (matchall)
4181  {
4182  result = false;
4183  break;
4184  }
4185  continue;
4186  }
4187 
4188  for (j = 0; j < nelems2; j++)
4189  {
4190  Datum elt2 = values2[j];
4191  bool isnull2 = nulls2 ? nulls2[j] : false;
4192  bool oprresult;
4193 
4194  if (isnull2)
4195  continue; /* can't match */
4196 
4197  /*
4198  * Apply the operator to the element pair
4199  */
4200  locfcinfo.arg[0] = elt1;
4201  locfcinfo.arg[1] = elt2;
4202  locfcinfo.argnull[0] = false;
4203  locfcinfo.argnull[1] = false;
4204  locfcinfo.isnull = false;
4205  oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
4206  if (oprresult)
4207  break;
4208  }
4209 
4210  if (j < nelems2)
4211  {
4212  /* found a match for elt1 */
4213  if (!matchall)
4214  {
4215  result = true;
4216  break;
4217  }
4218  }
4219  else
4220  {
4221  /* no match for elt1 */
4222  if (matchall)
4223  {
4224  result = false;
4225  break;
4226  }
4227  }
4228  }
4229 
4230  return result;
4231 }
4232 
4233 Datum
4235 {
4236  AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
4237  AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
4238  Oid collation = PG_GET_COLLATION();
4239  bool result;
4240 
4241  result = array_contain_compare(array1, array2, collation, false,
4242  &fcinfo->flinfo->fn_extra);
4243 
4244  /* Avoid leaking memory when handed toasted input. */
4245  AARR_FREE_IF_COPY(array1, 0);
4246  AARR_FREE_IF_COPY(array2, 1);
4247 
4248  PG_RETURN_BOOL(result);
4249 }
4250 
4251 Datum
4253 {
4254  AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
4255  AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
4256  Oid collation = PG_GET_COLLATION();
4257  bool result;
4258 
4259  result = array_contain_compare(array2, array1, collation, true,
4260  &fcinfo->flinfo->fn_extra);
4261 
4262  /* Avoid leaking memory when handed toasted input. */
4263  AARR_FREE_IF_COPY(array1, 0);
4264  AARR_FREE_IF_COPY(array2, 1);
4265 
4266  PG_RETURN_BOOL(result);
4267 }
4268 
4269 Datum
4271 {
4272  AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
4273  AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
4274  Oid collation = PG_GET_COLLATION();
4275  bool result;
4276 
4277  result = array_contain_compare(array1, array2, collation, true,
4278  &fcinfo->flinfo->fn_extra);
4279 
4280  /* Avoid leaking memory when handed toasted input. */
4281  AARR_FREE_IF_COPY(array1, 0);
4282  AARR_FREE_IF_COPY(array2, 1);
4283 
4284  PG_RETURN_BOOL(result);
4285 }
4286 
4287 
4288 /*-----------------------------------------------------------------------------
4289  * Array iteration functions
4290  * These functions are used to iterate efficiently through arrays
4291  *-----------------------------------------------------------------------------
4292  */
4293 
4294 /*
4295  * array_create_iterator --- set up to iterate through an array
4296  *
4297  * If slice_ndim is zero, we will iterate element-by-element; the returned
4298  * datums are of the array's element type.
4299  *
4300  * If slice_ndim is 1..ARR_NDIM(arr), we will iterate by slices: the
4301  * returned datums are of the same array type as 'arr', but of size
4302  * equal to the rightmost N dimensions of 'arr'.
4303  *
4304  * The passed-in array must remain valid for the lifetime of the iterator.
4305  */
4308 {
4309  ArrayIterator iterator = palloc0(sizeof(ArrayIteratorData));
4310 
4311  /*
4312  * Sanity-check inputs --- caller should have got this right already
4313  */
4314  Assert(PointerIsValid(arr));
4315  if (slice_ndim < 0 || slice_ndim > ARR_NDIM(arr))
4316  elog(ERROR, "invalid arguments to array_create_iterator");
4317 
4318  /*
4319  * Remember basic info about the array and its element type
4320  */
4321  iterator->arr = arr;
4322  iterator->nullbitmap = ARR_NULLBITMAP(arr);
4323  iterator->nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
4324 
4325  if (mstate != NULL)
4326  {
4327  Assert(mstate->element_type == ARR_ELEMTYPE(arr));
4328 
4329  iterator->typlen = mstate->typlen;
4330  iterator->typbyval = mstate->typbyval;
4331  iterator->typalign = mstate->typalign;
4332  }
4333  else
4335  &iterator->typlen,
4336  &iterator->typbyval,
4337  &iterator->typalign);
4338 
4339  /*
4340  * Remember the slicing parameters.
4341  */
4342  iterator->slice_ndim = slice_ndim;
4343 
4344  if (slice_ndim > 0)
4345  {
4346  /*
4347  * Get pointers into the array's dims and lbound arrays to represent
4348  * the dims/lbound arrays of a slice. These are the same as the
4349  * rightmost N dimensions of the array.
4350  */
4351  iterator->slice_dims = ARR_DIMS(arr) + ARR_NDIM(arr) - slice_ndim;
4352  iterator->slice_lbound = ARR_LBOUND(arr) + ARR_NDIM(arr) - slice_ndim;
4353 
4354  /*
4355  * Compute number of elements in a slice.
4356  */
4357  iterator->slice_len = ArrayGetNItems(slice_ndim,
4358  iterator->slice_dims);
4359 
4360  /*
4361  * Create workspace for building sub-arrays.
4362  */
4363  iterator->slice_values = (Datum *)
4364  palloc(iterator->slice_len * sizeof(Datum));
4365  iterator->slice_nulls = (bool *)
4366  palloc(iterator->slice_len * sizeof(bool));
4367  }
4368 
4369  /*
4370  * Initialize our data pointer and linear element number. These will
4371  * advance through the array during array_iterate().
4372  */
4373  iterator->data_ptr = ARR_DATA_PTR(arr);
4374  iterator->current_item = 0;
4375 
4376  return iterator;
4377 }
4378 
4379 /*
4380  * Iterate through the array referenced by 'iterator'.
4381  *
4382  * As long as there is another element (or slice), return it into
4383  * *value / *isnull, and return true. Return false when no more data.
4384  */
4385 bool
4386 array_iterate(ArrayIterator iterator, Datum *value, bool *isnull)
4387 {
4388  /* Done if we have reached the end of the array */
4389  if (iterator->current_item >= iterator->nitems)
4390  return false;
4391 
4392  if (iterator->slice_ndim == 0)
4393  {
4394  /*
4395  * Scalar case: return one element.
4396  */
4397  if (array_get_isnull(iterator->nullbitmap, iterator->current_item++))
4398  {
4399  *isnull = true;
4400  *value = (Datum) 0;
4401  }
4402  else
4403  {
4404  /* non-NULL, so fetch the individual Datum to return */
4405  char *p = iterator->data_ptr;
4406 
4407  *isnull = false;
4408  *value = fetch_att(p, iterator->typbyval, iterator->typlen);
4409 
4410  /* Move our data pointer forward to the next element */
4411  p = att_addlength_pointer(p, iterator->typlen, p);
4412  p = (char *) att_align_nominal(p, iterator->typalign);
4413  iterator->data_ptr = p;
4414  }
4415  }
4416  else
4417  {
4418  /*
4419  * Slice case: build and return an array of the requested size.
4420  */
4421  ArrayType *result;
4422  Datum *values = iterator->slice_values;
4423  bool *nulls = iterator->slice_nulls;
4424  char *p = iterator->data_ptr;
4425  int i;
4426 
4427  for (i = 0; i < iterator->slice_len; i++)
4428  {
4429  if (array_get_isnull(iterator->nullbitmap,
4430  iterator->current_item++))
4431  {
4432  nulls[i] = true;
4433  values[i] = (Datum) 0;
4434  }
4435  else
4436  {
4437  nulls[i] = false;
4438  values[i] = fetch_att(p, iterator->typbyval, iterator->typlen);
4439 
4440  /* Move our data pointer forward to the next element */
4441  p = att_addlength_pointer(p, iterator->typlen, p);
4442  p = (char *) att_align_nominal(p, iterator->typalign);
4443  }
4444  }
4445 
4446  iterator->data_ptr = p;
4447 
4448  result = construct_md_array(values,
4449  nulls,
4450  iterator->slice_ndim,
4451  iterator->slice_dims,
4452  iterator->slice_lbound,
4453  ARR_ELEMTYPE(iterator->arr),
4454  iterator->typlen,
4455  iterator->typbyval,
4456  iterator->typalign);
4457 
4458  *isnull = false;
4459  *value = PointerGetDatum(result);
4460  }
4461 
4462  return true;
4463 }
4464 
4465 /*
4466  * Release an ArrayIterator data structure
4467  */
4468 void
4470 {
4471  if (iterator->slice_ndim > 0)
4472  {
4473  pfree(iterator->slice_values);
4474  pfree(iterator->slice_nulls);
4475  }
4476  pfree(iterator);
4477 }
4478 
4479 
4480 /***************************************************************************/
4481 /******************| Support Routines |*****************/
4482 /***************************************************************************/
4483 
4484 /*
4485  * Check whether a specific array element is NULL
4486  *
4487  * nullbitmap: pointer to array's null bitmap (NULL if none)
4488  * offset: 0-based linear element number of array element
4489  */
4490 static bool
4491 array_get_isnull(const bits8 *nullbitmap, int offset)
4492 {
4493  if (nullbitmap == NULL)
4494  return false; /* assume not null */
4495  if (nullbitmap[offset / 8] & (1 << (offset % 8)))
4496  return false; /* not null */
4497  return true;
4498 }
4499 
4500 /*
4501  * Set a specific array element's null-bitmap entry
4502  *
4503  * nullbitmap: pointer to array's null bitmap (mustn't be NULL)
4504  * offset: 0-based linear element number of array element
4505  * isNull: null status to set
4506  */
4507 static void
4508 array_set_isnull(bits8 *nullbitmap, int offset, bool isNull)
4509 {
4510  int bitmask;
4511 
4512  nullbitmap += offset / 8;
4513  bitmask = 1 << (offset % 8);
4514  if (isNull)
4515  *nullbitmap &= ~bitmask;
4516  else
4517  *nullbitmap |= bitmask;
4518 }
4519 
4520 /*
4521  * Fetch array element at pointer, converted correctly to a Datum
4522  *
4523  * Caller must have handled case of NULL element
4524  */
4525 static Datum
4526 ArrayCast(char *value, bool byval, int len)
4527 {
4528  return fetch_att(value, byval, len);
4529 }
4530 
4531 /*
4532  * Copy datum to *dest and return total space used (including align padding)
4533  *
4534  * Caller must have handled case of NULL element
4535  */
4536 static int
4538  int typlen,
4539  bool typbyval,
4540  char typalign,
4541  char *dest)
4542 {
4543  int inc;
4544 
4545  if (typlen > 0)
4546  {
4547  if (typbyval)
4548  store_att_byval(dest, src, typlen);
4549  else
4550  memmove(dest, DatumGetPointer(src), typlen);
4551  inc = att_align_nominal(typlen, typalign);
4552  }
4553  else
4554  {
4555  Assert(!typbyval);
4556  inc = att_addlength_datum(0, typlen, src);
4557  memmove(dest, DatumGetPointer(src), inc);
4558  inc = att_align_nominal(inc, typalign);
4559  }
4560 
4561  return inc;
4562 }
4563 
4564 /*
4565  * Advance ptr over nitems array elements
4566  *
4567  * ptr: starting location in array
4568  * offset: 0-based linear element number of first element (the one at *ptr)
4569  * nullbitmap: start of array's null bitmap, or NULL if none
4570  * nitems: number of array elements to advance over (>= 0)
4571  * typlen, typbyval, typalign: storage parameters of array element datatype
4572  *
4573  * It is caller's responsibility to ensure that nitems is within range
4574  */
4575 static char *
4576 array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
4577  int typlen, bool typbyval, char typalign)
4578 {
4579  int bitmask;
4580  int i;
4581 
4582  /* easy if fixed-size elements and no NULLs */
4583  if (typlen > 0 && !nullbitmap)
4584  return ptr + nitems * ((Size) att_align_nominal(typlen, typalign));
4585 
4586  /* seems worth having separate loops for NULL and no-NULLs cases */
4587  if (nullbitmap)
4588  {
4589  nullbitmap += offset / 8;
4590  bitmask = 1 << (offset % 8);
4591 
4592  for (i = 0; i < nitems; i++)
4593  {
4594  if (*nullbitmap & bitmask)
4595  {
4596  ptr = att_addlength_pointer(ptr, typlen, ptr);
4597  ptr = (char *) att_align_nominal(ptr, typalign);
4598  }
4599  bitmask <<= 1;
4600  if (bitmask == 0x100)
4601  {
4602  nullbitmap++;
4603  bitmask = 1;
4604  }
4605  }
4606  }
4607  else
4608  {
4609  for (i = 0; i < nitems; i++)
4610  {
4611  ptr = att_addlength_pointer(ptr, typlen, ptr);
4612  ptr = (char *) att_align_nominal(ptr, typalign);
4613  }
4614  }
4615  return ptr;
4616 }
4617 
4618 /*
4619  * Compute total size of the nitems array elements starting at *ptr
4620  *
4621  * Parameters same as for array_seek
4622  */
4623 static int
4624 array_nelems_size(char *ptr, int offset, bits8 *nullbitmap, int nitems,
4625  int typlen, bool typbyval, char typalign)
4626 {
4627  return array_seek(ptr, offset, nullbitmap, nitems,
4628  typlen, typbyval, typalign) - ptr;
4629 }
4630 
4631 /*
4632  * Copy nitems array elements from srcptr to destptr
4633  *
4634  * destptr: starting destination location (must be enough room!)
4635  * nitems: number of array elements to copy (>= 0)
4636  * srcptr: starting location in source array
4637  * offset: 0-based linear element number of first element (the one at *srcptr)
4638  * nullbitmap: start of source array's null bitmap, or NULL if none
4639  * typlen, typbyval, typalign: storage parameters of array element datatype
4640  *
4641  * Returns number of bytes copied
4642  *
4643  * NB: this does not take care of setting up the destination's null bitmap!
4644  */
4645 static int
4646 array_copy(char *destptr, int nitems,
4647  char *srcptr, int offset, bits8 *nullbitmap,
4648  int typlen, bool typbyval, char typalign)
4649 {
4650  int numbytes;
4651 
4652  numbytes = array_nelems_size(srcptr, offset, nullbitmap, nitems,
4653  typlen, typbyval, typalign);
4654  memcpy(destptr, srcptr, numbytes);
4655  return numbytes;
4656 }
4657 
4658 /*
4659  * Copy nitems null-bitmap bits from source to destination
4660  *
4661  * destbitmap: start of destination array's null bitmap (mustn't be NULL)
4662  * destoffset: 0-based linear element number of first dest element
4663  * srcbitmap: start of source array's null bitmap, or NULL if none
4664  * srcoffset: 0-based linear element number of first source element
4665  * nitems: number of bits to copy (>= 0)
4666  *
4667  * If srcbitmap is NULL then we assume the source is all-non-NULL and
4668  * fill 1's into the destination bitmap. Note that only the specified
4669  * bits in the destination map are changed, not any before or after.
4670  *
4671  * Note: this could certainly be optimized using standard bitblt methods.
4672  * However, it's not clear that the typical Postgres array has enough elements
4673  * to make it worth worrying too much. For the moment, KISS.
4674  */
4675 void
4676 array_bitmap_copy(bits8 *destbitmap, int destoffset,
4677  const bits8 *srcbitmap, int srcoffset,
4678  int nitems)
4679 {
4680  int destbitmask,
4681  destbitval,
4682  srcbitmask,
4683  srcbitval;
4684 
4685  Assert(destbitmap);
4686  if (nitems <= 0)
4687  return; /* don't risk fetch off end of memory */
4688  destbitmap += destoffset / 8;
4689  destbitmask = 1 << (destoffset % 8);
4690  destbitval = *destbitmap;
4691  if (srcbitmap)
4692  {
4693  srcbitmap += srcoffset / 8;
4694  srcbitmask = 1 << (srcoffset % 8);
4695  srcbitval = *srcbitmap;
4696  while (nitems-- > 0)
4697  {
4698  if (srcbitval & srcbitmask)
4699  destbitval |= destbitmask;
4700  else
4701  destbitval &= ~destbitmask;
4702  destbitmask <<= 1;
4703  if (destbitmask == 0x100)
4704  {
4705  *destbitmap++ = destbitval;
4706  destbitmask = 1;
4707  if (nitems > 0)
4708  destbitval = *destbitmap;
4709  }
4710  srcbitmask <<= 1;
4711  if (srcbitmask == 0x100)
4712  {
4713  srcbitmap++;
4714  srcbitmask = 1;
4715  if (nitems > 0)
4716  srcbitval = *srcbitmap;
4717  }
4718  }
4719  if (destbitmask != 1)
4720  *destbitmap = destbitval;
4721  }
4722  else
4723  {
4724  while (nitems-- > 0)
4725  {
4726  destbitval |= destbitmask;
4727  destbitmask <<= 1;
4728  if (destbitmask == 0x100)
4729  {
4730  *destbitmap++ = destbitval;
4731  destbitmask = 1;
4732  if (nitems > 0)
4733  destbitval = *destbitmap;
4734  }
4735  }
4736  if (destbitmask != 1)
4737  *destbitmap = destbitval;
4738  }
4739 }
4740 
4741 /*
4742  * Compute space needed for a slice of an array
4743  *
4744  * We assume the caller has verified that the slice coordinates are valid.
4745  */
4746 static int
4747 array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
4748  int ndim, int *dim, int *lb,
4749  int *st, int *endp,
4750  int typlen, bool typbyval, char typalign)
4751 {
4752  int src_offset,
4753  span[MAXDIM],
4754  prod[MAXDIM],
4755  dist[MAXDIM],
4756  indx[MAXDIM];
4757  char *ptr;
4758  int i,
4759  j,
4760  inc;
4761  int count = 0;
4762 
4763  mda_get_range(ndim, span, st, endp);
4764 
4765  /* Pretty easy for fixed element length without nulls ... */
4766  if (typlen > 0 && !arraynullsptr)
4767  return ArrayGetNItems(ndim, span) * att_align_nominal(typlen, typalign);
4768 
4769  /* Else gotta do it the hard way */
4770  src_offset = ArrayGetOffset(ndim, dim, lb, st);
4771  ptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
4772  typlen, typbyval, typalign);
4773  mda_get_prod(ndim, dim, prod);
4774  mda_get_offset_values(ndim, dist, prod, span);
4775  for (i = 0; i < ndim; i++)
4776  indx[i] = 0;
4777  j = ndim - 1;
4778  do
4779  {
4780  if (dist[j])
4781  {
4782  ptr = array_seek(ptr, src_offset, arraynullsptr, dist[j],
4783  typlen, typbyval, typalign);
4784  src_offset += dist[j];
4785  }
4786  if (!array_get_isnull(arraynullsptr, src_offset))
4787  {
4788  inc = att_addlength_pointer(0, typlen, ptr);
4789  inc = att_align_nominal(inc, typalign);
4790  ptr += inc;
4791  count += inc;
4792  }
4793  src_offset++;
4794  } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
4795  return count;
4796 }
4797 
4798 /*
4799  * Extract a slice of an array into consecutive elements in the destination
4800  * array.
4801  *
4802  * We assume the caller has verified that the slice coordinates are valid,
4803  * allocated enough storage for the result, and initialized the header
4804  * of the new array.
4805  */
4806 static void
4808  int ndim,
4809  int *dim,
4810  int *lb,
4811  char *arraydataptr,
4812  bits8 *arraynullsptr,
4813  int *st,
4814  int *endp,
4815  int typlen,
4816  bool typbyval,
4817  char typalign)
4818 {
4819  char *destdataptr = ARR_DATA_PTR(newarray);
4820  bits8 *destnullsptr = ARR_NULLBITMAP(newarray);
4821  char *srcdataptr;
4822  int src_offset,
4823  dest_offset,
4824  prod[MAXDIM],
4825  span[MAXDIM],
4826  dist[MAXDIM],
4827  indx[MAXDIM];
4828  int i,
4829  j,
4830  inc;
4831 
4832  src_offset = ArrayGetOffset(ndim, dim, lb, st);
4833  srcdataptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
4834  typlen, typbyval, typalign);
4835  mda_get_prod(ndim, dim, prod);
4836  mda_get_range(ndim, span, st, endp);
4837  mda_get_offset_values(ndim, dist, prod, span);
4838  for (i = 0; i < ndim; i++)
4839  indx[i] = 0;
4840  dest_offset = 0;
4841  j = ndim - 1;
4842  do
4843  {
4844  if (dist[j])
4845  {
4846  /* skip unwanted elements */
4847  srcdataptr = array_seek(srcdataptr, src_offset, arraynullsptr,
4848  dist[j],
4849  typlen, typbyval, typalign);
4850  src_offset += dist[j];
4851  }
4852  inc = array_copy(destdataptr, 1,
4853  srcdataptr, src_offset, arraynullsptr,
4854  typlen, typbyval, typalign);
4855  if (destnullsptr)
4856  array_bitmap_copy(destnullsptr, dest_offset,
4857  arraynullsptr, src_offset,
4858  1);
4859  destdataptr += inc;
4860  srcdataptr += inc;
4861  src_offset++;
4862  dest_offset++;
4863  } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
4864 }
4865 
4866 /*
4867  * Insert a slice into an array.
4868  *
4869  * ndim/dim[]/lb[] are dimensions of the original array. A new array with
4870  * those same dimensions is to be constructed. destArray must already
4871  * have been allocated and its header initialized.
4872  *
4873  * st[]/endp[] identify the slice to be replaced. Elements within the slice
4874  * volume are taken from consecutive elements of the srcArray; elements
4875  * outside it are copied from origArray.
4876  *
4877  * We assume the caller has verified that the slice coordinates are valid.
4878  */
4879 static void
4881  ArrayType *origArray,
4882  ArrayType *srcArray,
4883  int ndim,
4884  int *dim,
4885  int *lb,
4886  int *st,
4887  int *endp,
4888  int typlen,
4889  bool typbyval,
4890  char typalign)
4891 {
4892  char *destPtr = ARR_DATA_PTR(destArray);
4893  char *origPtr = ARR_DATA_PTR(origArray);
4894  char *srcPtr = ARR_DATA_PTR(srcArray);
4895  bits8 *destBitmap = ARR_NULLBITMAP(destArray);
4896  bits8 *origBitmap = ARR_NULLBITMAP(origArray);
4897  bits8 *srcBitmap = ARR_NULLBITMAP(srcArray);
4898  int orignitems = ArrayGetNItems(ARR_NDIM(origArray),
4899  ARR_DIMS(origArray));
4900  int dest_offset,
4901  orig_offset,
4902  src_offset,
4903  prod[MAXDIM],
4904  span[MAXDIM],
4905  dist[MAXDIM],
4906  indx[MAXDIM];
4907  int i,
4908  j,
4909  inc;
4910 
4911  dest_offset = ArrayGetOffset(ndim, dim, lb, st);
4912  /* copy items before the slice start */
4913  inc = array_copy(destPtr, dest_offset,
4914  origPtr, 0, origBitmap,
4915  typlen, typbyval, typalign);
4916  destPtr += inc;
4917  origPtr += inc;
4918  if (destBitmap)
4919  array_bitmap_copy(destBitmap, 0, origBitmap, 0, dest_offset);
4920  orig_offset = dest_offset;
4921  mda_get_prod(ndim, dim, prod);
4922  mda_get_range(ndim, span, st, endp);
4923  mda_get_offset_values(ndim, dist, prod, span);
4924  for (i = 0; i < ndim; i++)
4925  indx[i] = 0;
4926  src_offset = 0;
4927  j = ndim - 1;
4928  do
4929  {
4930  /* Copy/advance over elements between here and next part of slice */
4931  if (dist[j])
4932  {
4933  inc = array_copy(destPtr, dist[j],
4934  origPtr, orig_offset, origBitmap,
4935  typlen, typbyval, typalign);
4936  destPtr += inc;
4937  origPtr += inc;
4938  if (destBitmap)
4939  array_bitmap_copy(destBitmap, dest_offset,
4940  origBitmap, orig_offset,
4941  dist[j]);
4942  dest_offset += dist[j];
4943  orig_offset += dist[j];
4944  }
4945  /* Copy new element at this slice position */
4946  inc = array_copy(destPtr, 1,
4947  srcPtr, src_offset, srcBitmap,
4948  typlen, typbyval, typalign);
4949  if (destBitmap)
4950  array_bitmap_copy(destBitmap, dest_offset,
4951  srcBitmap, src_offset,
4952  1);
4953  destPtr += inc;
4954  srcPtr += inc;
4955  dest_offset++;
4956  src_offset++;
4957  /* Advance over old element at this slice position */
4958  origPtr = array_seek(origPtr, orig_offset, origBitmap, 1,
4959  typlen, typbyval, typalign);
4960  orig_offset++;
4961  } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
4962 
4963  /* don't miss any data at the end */
4964  array_copy(destPtr, orignitems - orig_offset,
4965  origPtr, orig_offset, origBitmap,
4966  typlen, typbyval, typalign);
4967  if (destBitmap)
4968  array_bitmap_copy(destBitmap, dest_offset,
4969  origBitmap, orig_offset,
4970  orignitems - orig_offset);
4971 }
4972 
4973 /*
4974  * initArrayResult - initialize an empty ArrayBuildState
4975  *
4976  * element_type is the array element type (must be a valid array element type)
4977  * rcontext is where to keep working state
4978  * subcontext is a flag determining whether to use a separate memory context
4979  *
4980  * Note: there are two common schemes for using accumArrayResult().
4981  * In the older scheme, you start with a NULL ArrayBuildState pointer, and
4982  * call accumArrayResult once per element. In this scheme you end up with
4983  * a NULL pointer if there were no elements, which you need to special-case.
4984  * In the newer scheme, call initArrayResult and then call accumArrayResult
4985  * once per element. In this scheme you always end with a non-NULL pointer
4986  * that you can pass to makeArrayResult; you get an empty array if there
4987  * were no elements. This is preferred if an empty array is what you want.
4988  *
4989  * It's possible to choose whether to create a separate memory context for the
4990  * array build state, or whether to allocate it directly within rcontext.
4991  *
4992  * When there are many concurrent small states (e.g. array_agg() using hash
4993  * aggregation of many small groups), using a separate memory context for each
4994  * one may result in severe memory bloat. In such cases, use the same memory
4995  * context to initialize all such array build states, and pass
4996  * subcontext=false.
4997  *
4998  * In cases when the array build states have different lifetimes, using a
4999  * single memory context is impractical. Instead, pass subcontext=true so that
5000  * the array build states can be freed individually.
5001  */
5003 initArrayResult(Oid element_type, MemoryContext rcontext, bool subcontext)
5004 {
5005  ArrayBuildState *astate;
5006  MemoryContext arr_context = rcontext;
5007 
5008  /* Make a temporary context to hold all the junk */
5009  if (subcontext)
5010  arr_context = AllocSetContextCreate(rcontext,
5011  "accumArrayResult",
5013 
5014  astate = (ArrayBuildState *)
5015  MemoryContextAlloc(arr_context, sizeof(ArrayBuildState));
5016  astate->mcontext = arr_context;
5017  astate->private_cxt = subcontext;
5018  astate->alen = (subcontext ? 64 : 8); /* arbitrary starting array size */
5019  astate->dvalues = (Datum *)
5020  MemoryContextAlloc(arr_context, astate->alen * sizeof(Datum));
5021  astate->dnulls = (bool *)
5022  MemoryContextAlloc(arr_context, astate->alen * sizeof(bool));
5023  astate->nelems = 0;
5024  astate->element_type = element_type;
5025  get_typlenbyvalalign(element_type,
5026  &astate->typlen,
5027  &astate->typbyval,
5028  &astate->typalign);
5029 
5030  return astate;
5031 }
5032 
5033 /*
5034  * accumArrayResult - accumulate one (more) Datum for an array result
5035  *
5036  * astate is working state (can be NULL on first call)
5037  * dvalue/disnull represent the new Datum to append to the array
5038  * element_type is the Datum's type (must be a valid array element type)
5039  * rcontext is where to keep working state
5040  */
5043  Datum dvalue, bool disnull,
5044  Oid element_type,
5045  MemoryContext rcontext)
5046 {
5047  MemoryContext oldcontext;
5048 
5049  if (astate == NULL)
5050  {
5051  /* First time through --- initialize */
5052  astate = initArrayResult(element_type, rcontext, true);
5053  }
5054  else
5055  {
5056  Assert(astate->element_type == element_type);
5057  }
5058 
5059  oldcontext = MemoryContextSwitchTo(astate->mcontext);
5060 
5061  /* enlarge dvalues[]/dnulls[] if needed */
5062  if (astate->nelems >= astate->alen)
5063  {
5064  astate->alen *= 2;
5065  astate->dvalues = (Datum *)
5066  repalloc(astate->dvalues, astate->alen * sizeof(Datum));
5067  astate->dnulls = (bool *)
5068  repalloc(astate->dnulls, astate->alen * sizeof(bool));
5069  }
5070 
5071  /*
5072  * Ensure pass-by-ref stuff is copied into mcontext; and detoast it too if
5073  * it's varlena. (You might think that detoasting is not needed here
5074  * because construct_md_array can detoast the array elements later.
5075  * However, we must not let construct_md_array modify the ArrayBuildState
5076  * because that would mean array_agg_finalfn damages its input, which is
5077  * verboten. Also, this way frequently saves one copying step.)
5078  */
5079  if (!disnull && !astate->typbyval)
5080  {
5081  if (astate->typlen == -1)
5082  dvalue = PointerGetDatum(PG_DETOAST_DATUM_COPY(dvalue));
5083  else
5084  dvalue = datumCopy(dvalue, astate->typbyval, astate->typlen);
5085  }
5086 
5087  astate->dvalues[astate->nelems] = dvalue;
5088  astate->dnulls[astate->nelems] = disnull;
5089  astate->nelems++;
5090 
5091  MemoryContextSwitchTo(oldcontext);
5092 
5093  return astate;
5094 }
5095 
5096 /*
5097  * makeArrayResult - produce 1-D final result of accumArrayResult
5098  *
5099  * Note: only releases astate if it was initialized within a separate memory
5100  * context (i.e. using subcontext=true when calling initArrayResult).
5101  *
5102  * astate is working state (must not be NULL)
5103  * rcontext is where to construct result
5104  */
5105 Datum
5107  MemoryContext rcontext)
5108 {
5109  int ndims;
5110  int dims[1];
5111  int lbs[1];
5112 
5113  /* If no elements were presented, we want to create an empty array */
5114  ndims = (astate->nelems > 0) ? 1 : 0;
5115  dims[0] = astate->nelems;
5116  lbs[0] = 1;
5117 
5118  return makeMdArrayResult(astate, ndims, dims, lbs, rcontext,
5119  astate->private_cxt);
5120 }
5121 
5122 /*
5123  * makeMdArrayResult - produce multi-D final result of accumArrayResult
5124  *
5125  * beware: no check that specified dimensions match the number of values
5126  * accumulated.
5127  *
5128  * Note: if the astate was not initialized within a separate memory context
5129  * (that is, initArrayResult was called with subcontext=false), then using
5130  * release=true is illegal. Instead, release astate along with the rest of its
5131  * context when appropriate.
5132  *
5133  * astate is working state (must not be NULL)
5134  * rcontext is where to construct result
5135  * release is true if okay to release working state
5136  */
5137 Datum
5139  int ndims,
5140  int *dims,
5141  int *lbs,
5142  MemoryContext rcontext,
5143  bool release)
5144 {
5145  ArrayType *result;
5146  MemoryContext oldcontext;
5147 
5148  /* Build the final array result in rcontext */
5149  oldcontext = MemoryContextSwitchTo(rcontext);
5150 
5151  result = construct_md_array(astate->dvalues,
5152  astate->dnulls,
5153  ndims,
5154  dims,
5155  lbs,
5156  astate->element_type,
5157  astate->typlen,
5158  astate->typbyval,
5159  astate->typalign);
5160 
5161  MemoryContextSwitchTo(oldcontext);
5162 
5163  /* Clean up all the junk */
5164  if (release)
5165  {
5166  Assert(astate->private_cxt);
5167  MemoryContextDelete(astate->mcontext);
5168  }
5169 
5170  return PointerGetDatum(result);
5171 }
5172 
5173 /*
5174  * The following three functions provide essentially the same API as
5175  * initArrayResult/accumArrayResult/makeArrayResult, but instead of accepting
5176  * inputs that are array elements, they accept inputs that are arrays and
5177  * produce an output array having N+1 dimensions. The inputs must all have
5178  * identical dimensionality as well as element type.
5179  */
5180 
5181 /*
5182  * initArrayResultArr - initialize an empty ArrayBuildStateArr
5183  *
5184  * array_type is the array type (must be a valid varlena array type)
5185  * element_type is the type of the array's elements (lookup if InvalidOid)
5186  * rcontext is where to keep working state
5187  * subcontext is a flag determining whether to use a separate memory context
5188  */
5190 initArrayResultArr(Oid array_type, Oid element_type, MemoryContext rcontext,
5191  bool subcontext)
5192 {
5193  ArrayBuildStateArr *astate;
5194  MemoryContext arr_context = rcontext; /* by default use the parent ctx */
5195 
5196  /* Lookup element type, unless element_type already provided */
5197  if (!OidIsValid(element_type))
5198  {
5199  element_type = get_element_type(array_type);
5200 
5201  if (!OidIsValid(element_type))
5202  ereport(ERROR,
5203  (errcode(ERRCODE_DATATYPE_MISMATCH),
5204  errmsg("data type %s is not an array type",
5205  format_type_be(array_type))));
5206  }
5207 
5208  /* Make a temporary context to hold all the junk */
5209  if (subcontext)
5210  arr_context = AllocSetContextCreate(rcontext,
5211  "accumArrayResultArr",
5213 
5214  /* Note we initialize all fields to zero */
5215  astate = (ArrayBuildStateArr *)
5216  MemoryContextAllocZero(arr_context, sizeof(ArrayBuildStateArr));
5217  astate->mcontext = arr_context;
5218  astate->private_cxt = subcontext;
5219 
5220  /* Save relevant datatype information */
5221  astate->array_type = array_type;
5222  astate->element_type = element_type;
5223 
5224  return astate;
5225 }
5226 
5227 /*
5228  * accumArrayResultArr - accumulate one (more) sub-array for an array result
5229  *
5230  * astate is working state (can be NULL on first call)
5231  * dvalue/disnull represent the new sub-array to append to the array
5232  * array_type is the array type (must be a valid varlena array type)
5233  * rcontext is where to keep working state
5234  */
5237  Datum dvalue, bool disnull,
5238  Oid array_type,
5239  MemoryContext rcontext)
5240 {
5241  ArrayType *arg;
5242  MemoryContext oldcontext;
5243  int *dims,
5244  *lbs,
5245  ndims,
5246  nitems,
5247  ndatabytes;
5248  char *data;
5249  int i;
5250 
5251  /*
5252  * We disallow accumulating null subarrays. Another plausible definition
5253  * is to ignore them, but callers that want that can just skip calling
5254  * this function.
5255  */
5256  if (disnull)
5257  ereport(ERROR,
5258  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5259  errmsg("cannot accumulate null arrays")));
5260 
5261  /* Detoast input array in caller's context */
5262  arg = DatumGetArrayTypeP(dvalue);
5263 
5264  if (astate == NULL)
5265  astate = initArrayResultArr(array_type, InvalidOid, rcontext, true);
5266  else
5267  Assert(astate->array_type == array_type);
5268 
5269  oldcontext = MemoryContextSwitchTo(astate->mcontext);
5270 
5271  /* Collect this input's dimensions */
5272  ndims = ARR_NDIM(arg);
5273  dims = ARR_DIMS(arg);
5274  lbs = ARR_LBOUND(arg);
5275  data = ARR_DATA_PTR(arg);
5276  nitems = ArrayGetNItems(ndims, dims);
5277  ndatabytes = ARR_SIZE(arg) - ARR_DATA_OFFSET(arg);
5278 
5279  if (astate->ndims == 0)
5280  {
5281  /* First input; check/save the dimensionality info */
5282 
5283  /* Should we allow empty inputs and just produce an empty output? */
5284  if (ndims == 0)
5285  ereport(ERROR,
5286  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5287  errmsg("cannot accumulate empty arrays")));
5288  if (ndims + 1 > MAXDIM)
5289  ereport(ERROR,
5290  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
5291  errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
5292  ndims + 1, MAXDIM)));
5293 
5294  /*
5295  * The output array will have n+1 dimensions, with the ones after the
5296  * first matching the input's dimensions.
5297  */
5298  astate->ndims = ndims + 1;
5299  astate->dims[0] = 0;
5300  memcpy(&astate->dims[1], dims, ndims * sizeof(int));
5301  astate->lbs[0] = 1;
5302  memcpy(&astate->lbs[1], lbs, ndims * sizeof(int));
5303 
5304  /* Allocate at least enough data space for this item */
5305  astate->abytes = 1024;
5306  while (astate->abytes <= ndatabytes)
5307  astate->abytes *= 2;
5308  astate->data = (char *) palloc(astate->abytes);
5309  }
5310  else
5311  {
5312  /* Second or later input: must match first input's dimensionality */
5313  if (astate->ndims != ndims + 1)
5314  ereport(ERROR,
5315  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5316  errmsg("cannot accumulate arrays of different dimensionality")));
5317  for (i = 0; i < ndims; i++)
5318  {
5319  if (astate->dims[i + 1] != dims[i] || astate->lbs[i + 1] != lbs[i])
5320  ereport(ERROR,
5321  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5322  errmsg("cannot accumulate arrays of different dimensionality")));
5323  }
5324 
5325  /* Enlarge data space if needed */
5326  if (astate->nbytes + ndatabytes > astate->abytes)
5327  {
5328  astate->abytes = Max(astate->abytes * 2,
5329  astate->nbytes + ndatabytes);
5330  astate->data = (char *) repalloc(astate->data, astate->abytes);
5331  }
5332  }
5333 
5334  /*
5335  * Copy the data portion of the sub-array. Note we assume that the
5336  * advertised data length of the sub-array is properly aligned. We do not
5337  * have to worry about detoasting elements since whatever's in the
5338  * sub-array should be OK already.
5339  */
5340  memcpy(astate->data + astate->nbytes, data, ndatabytes);
5341  astate->nbytes += ndatabytes;
5342 
5343  /* Deal with null bitmap if needed */
5344  if (astate->nullbitmap || ARR_HASNULL(arg))
5345  {
5346  int newnitems = astate->nitems + nitems;
5347 
5348  if (astate->nullbitmap == NULL)
5349  {
5350  /*
5351  * First input with nulls; we must retrospectively handle any
5352  * previous inputs by marking all their items non-null.
5353  */
5354  astate->aitems = 256;
5355  while (astate->aitems <= newnitems)
5356  astate->aitems *= 2;
5357  astate->nullbitmap = (bits8 *) palloc((astate->aitems + 7) / 8);
5358  array_bitmap_copy(astate->nullbitmap, 0,
5359  NULL, 0,
5360  astate->nitems);
5361  }
5362  else if (newnitems > astate->aitems)
5363  {
5364  astate->aitems = Max(astate->aitems * 2, newnitems);
5365  astate->nullbitmap = (bits8 *)
5366  repalloc(astate->nullbitmap, (astate->aitems + 7) / 8);
5367  }
5368  array_bitmap_copy(astate->nullbitmap, astate->nitems,
5369  ARR_NULLBITMAP(arg), 0,
5370  nitems);
5371  }
5372 
5373  astate->nitems += nitems;
5374  astate->dims[0] += 1;
5375 
5376  MemoryContextSwitchTo(oldcontext);
5377 
5378  /* Release detoasted copy if any */
5379  if ((Pointer) arg != DatumGetPointer(dvalue))
5380  pfree(arg);
5381 
5382  return astate;
5383 }
5384 
5385 /*
5386  * makeArrayResultArr - produce N+1-D final result of accumArrayResultArr
5387  *
5388  * astate is working state (must not be NULL)
5389  * rcontext is where to construct result
5390  * release is true if okay to release working state
5391  */
5392 Datum
5394  MemoryContext rcontext,
5395  bool release)
5396 {
5397  ArrayType *result;
5398  MemoryContext oldcontext;
5399 
5400  /* Build the final array result in rcontext */
5401  oldcontext = MemoryContextSwitchTo(rcontext);
5402 
5403  if (astate->ndims == 0)
5404  {
5405  /* No inputs, return empty array */
5406  result = construct_empty_array(astate->element_type);
5407  }
5408  else
5409  {
5410  int dataoffset,
5411  nbytes;
5412 
5413  /* Compute required space */
5414  nbytes = astate->nbytes;
5415  if (astate->nullbitmap != NULL)
5416  {
5417  dataoffset = ARR_OVERHEAD_WITHNULLS(astate->ndims, astate->nitems);
5418  nbytes += dataoffset;
5419  }
5420  else
5421  {
5422  dataoffset = 0;
5423  nbytes += ARR_OVERHEAD_NONULLS(astate->ndims);
5424  }
5425 
5426  result = (ArrayType *) palloc0(nbytes);
5427  SET_VARSIZE(result, nbytes);
5428  result->ndim = astate->ndims;
5429  result->dataoffset = dataoffset;
5430  result->elemtype = astate->element_type;
5431 
5432  memcpy(ARR_DIMS(result), astate->dims, astate->ndims * sizeof(int));
5433  memcpy(ARR_LBOUND(result), astate->lbs, astate->ndims * sizeof(int));
5434  memcpy(ARR_DATA_PTR(result), astate->data, astate->nbytes);
5435 
5436  if (astate->nullbitmap != NULL)
5437  array_bitmap_copy(ARR_NULLBITMAP(result), 0,
5438  astate->nullbitmap, 0,
5439  astate->nitems);
5440  }
5441 
5442  MemoryContextSwitchTo(oldcontext);
5443 
5444  /* Clean up all the junk */
5445  if (release)
5446  {
5447  Assert(astate->private_cxt);
5448  MemoryContextDelete(astate->mcontext);
5449  }
5450 
5451  return PointerGetDatum(result);
5452 }
5453 
5454 /*
5455  * The following three functions provide essentially the same API as
5456  * initArrayResult/accumArrayResult/makeArrayResult, but can accept either
5457  * scalar or array inputs, invoking the appropriate set of functions above.
5458  */
5459 
5460 /*
5461  * initArrayResultAny - initialize an empty ArrayBuildStateAny
5462  *
5463  * input_type is the input datatype (either element or array type)
5464  * rcontext is where to keep working state
5465  * subcontext is a flag determining whether to use a separate memory context
5466  */
5468 initArrayResultAny(Oid input_type, MemoryContext rcontext, bool subcontext)
5469 {
5470  ArrayBuildStateAny *astate;
5471  Oid element_type = get_element_type(input_type);
5472 
5473  if (OidIsValid(element_type))
5474  {
5475  /* Array case */
5476  ArrayBuildStateArr *arraystate;
5477 
5478  arraystate = initArrayResultArr(input_type, InvalidOid, rcontext, subcontext);
5479  astate = (ArrayBuildStateAny *)
5480  MemoryContextAlloc(arraystate->mcontext,
5481  sizeof(ArrayBuildStateAny));
5482  astate->scalarstate = NULL;
5483  astate->arraystate = arraystate;
5484  }
5485  else
5486  {
5487  /* Scalar case */
5488  ArrayBuildState *scalarstate;
5489 
5490  /* Let's just check that we have a type that can be put into arrays */
5491  Assert(OidIsValid(get_array_type(input_type)));
5492 
5493  scalarstate = initArrayResult(input_type, rcontext, subcontext);
5494  astate = (ArrayBuildStateAny *)
5495  MemoryContextAlloc(scalarstate->mcontext,
5496  sizeof(ArrayBuildStateAny));
5497  astate->scalarstate = scalarstate;
5498  astate->arraystate = NULL;
5499  }
5500 
5501  return astate;
5502 }
5503 
5504 /*
5505  * accumArrayResultAny - accumulate one (more) input for an array result
5506  *
5507  * astate is working state (can be NULL on first call)
5508  * dvalue/disnull represent the new input to append to the array
5509  * input_type is the input datatype (either element or array type)
5510  * rcontext is where to keep working state
5511  */
5514  Datum dvalue, bool disnull,
5515  Oid input_type,
5516  MemoryContext rcontext)
5517 {
5518  if (astate == NULL)
5519  astate = initArrayResultAny(input_type, rcontext, true);
5520 
5521  if (astate->scalarstate)
5522  (void) accumArrayResult(astate->scalarstate,
5523  dvalue, disnull,
5524  input_type, rcontext);
5525  else
5526  (void) accumArrayResultArr(astate->arraystate,
5527  dvalue, disnull,
5528  input_type, rcontext);
5529 
5530  return astate;
5531 }
5532 
5533 /*
5534  * makeArrayResultAny - produce final result of accumArrayResultAny
5535  *
5536  * astate is working state (must not be NULL)
5537  * rcontext is where to construct result
5538  * release is true if okay to release working state
5539  */
5540 Datum
5542  MemoryContext rcontext, bool release)
5543 {
5544  Datum result;
5545 
5546  if (astate->scalarstate)
5547  {
5548  /* Must use makeMdArrayResult to support "release" parameter */
5549  int ndims;
5550  int dims[1];
5551  int lbs[1];
5552 
5553  /* If no elements were presented, we want to create an empty array */
5554  ndims = (astate->scalarstate->nelems > 0) ? 1 : 0;
5555  dims[0] = astate->scalarstate->nelems;
5556  lbs[0] = 1;
5557 
5558  result = makeMdArrayResult(astate->scalarstate, ndims, dims, lbs,
5559  rcontext, release);
5560  }
5561  else
5562  {
5563  result = makeArrayResultArr(astate->arraystate,
5564  rcontext, release);
5565  }
5566  return result;
5567 }
5568 
5569 
5570 Datum
5572 {
5573  if (array_cmp(fcinfo) > 0)
5575  else
5577 }
5578 
5579 Datum
5581 {
5582  if (array_cmp(fcinfo) < 0)
5584  else
5586 }
5587 
5588 
5590 {
5593  bool reverse;
5595 
5596 /*
5597  * generate_subscripts(array anyarray, dim int [, reverse bool])
5598  * Returns all subscripts of the array for any dimension
5599  */
5600 Datum
5602 {
5603  FuncCallContext *funcctx;
5604  MemoryContext oldcontext;
5606 
5607  /* stuff done only on the first call of the function */
5608  if (SRF_IS_FIRSTCALL())
5609  {
5611  int reqdim = PG_GETARG_INT32(1);
5612  int *lb,
5613  *dimv;
5614 
5615  /* create a function context for cross-call persistence */
5616  funcctx = SRF_FIRSTCALL_INIT();
5617 
5618  /* Sanity check: does it look like an array at all? */
5619  if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
5620  SRF_RETURN_DONE(funcctx);
5621 
5622  /* Sanity check: was the requested dim valid */
5623  if (reqdim <= 0 || reqdim > AARR_NDIM(v))
5624  SRF_RETURN_DONE(funcctx);
5625 
5626  /*
5627  * switch to memory context appropriate for multiple function calls
5628  */
5629  oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
5631 
5632  lb = AARR_LBOUND(v);
5633  dimv = AARR_DIMS(v);
5634 
5635  fctx->lower = lb[reqdim - 1];
5636  fctx->upper = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
5637  fctx->reverse = (PG_NARGS() < 3) ? false : PG_GETARG_BOOL(2);
5638 
5639  funcctx->user_fctx = fctx;
5640 
5641  MemoryContextSwitchTo(oldcontext);
5642  }
5643 
5644  funcctx = SRF_PERCALL_SETUP();
5645 
5646  fctx = funcctx->user_fctx;
5647 
5648  if (fctx->lower <= fctx->upper)
5649  {
5650  if (!fctx->reverse)
5651  SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->lower++));
5652  else
5653  SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->upper--));
5654  }
5655  else
5656  /* done when there are no more elements left */
5657  SRF_RETURN_DONE(funcctx);
5658 }
5659 
5660 /*
5661  * generate_subscripts_nodir
5662  * Implements the 2-argument version of generate_subscripts
5663  */
5664 Datum
5666 {
5667  /* just call the other one -- it can handle both cases */
5668  return generate_subscripts(fcinfo);
5669 }
5670 
5671 /*
5672  * array_fill_with_lower_bounds
5673  * Create and fill array with defined lower bounds.
5674  */
5675 Datum
5677 {
5678  ArrayType *dims;
5679  ArrayType *lbs;
5680  ArrayType *result;
5681  Oid elmtype;
5682  Datum value;
5683  bool isnull;
5684 
5685  if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
5686  ereport(ERROR,
5687  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5688  errmsg("dimension array or low bound array cannot be null")));
5689 
5690  dims = PG_GETARG_ARRAYTYPE_P(1);
5691  lbs = PG_GETARG_ARRAYTYPE_P(2);
5692 
5693  if (!PG_ARGISNULL(0))
5694  {
5695  value = PG_GETARG_DATUM(0);
5696  isnull = false;
5697  }
5698  else
5699  {
5700  value = 0;
5701  isnull = true;
5702  }
5703 
5704  elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
5705  if (!OidIsValid(elmtype))
5706  elog(ERROR, "could not determine data type of input");
5707 
5708  result = array_fill_internal(dims, lbs, value, isnull, elmtype, fcinfo);
5709  PG_RETURN_ARRAYTYPE_P(result);
5710 }
5711 
5712 /*
5713  * array_fill
5714  * Create and fill array with default lower bounds.
5715  */
5716 Datum
5718 {
5719  ArrayType *dims;
5720  ArrayType *result;
5721  Oid elmtype;
5722  Datum value;
5723  bool isnull;
5724 
5725  if (PG_ARGISNULL(1))
5726  ereport(ERROR,
5727  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5728  errmsg("dimension array or low bound array cannot be null")));
5729 
5730  dims = PG_GETARG_ARRAYTYPE_P(1);
5731 
5732  if (!PG_ARGISNULL(0))
5733  {
5734  value = PG_GETARG_DATUM(0);
5735  isnull = false;
5736  }
5737  else
5738  {
5739  value = 0;
5740  isnull = true;
5741  }
5742 
5743  elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
5744  if (!OidIsValid(elmtype))
5745  elog(ERROR, "could not determine data type of input");
5746 
5747  result = array_fill_internal(dims, NULL, value, isnull, elmtype, fcinfo);
5748  PG_RETURN_ARRAYTYPE_P(result);
5749 }
5750 
5751 static ArrayType *
5752 create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
5753  Oid elmtype, int dataoffset)
5754 {
5755  ArrayType *result;
5756 
5757  result = (ArrayType *) palloc0(nbytes);
5758  SET_VARSIZE(result, nbytes);
5759  result->ndim = ndims;
5760  result->dataoffset = dataoffset;
5761  result->elemtype = elmtype;
5762  memcpy(ARR_DIMS(result), dimv, ndims * sizeof(int));
5763  memcpy(ARR_LBOUND(result), lbsv, ndims * sizeof(int));
5764 
5765  return result;
5766 }
5767 
5768 static ArrayType *
5770  Datum value, bool isnull, Oid elmtype,
5771  FunctionCallInfo fcinfo)
5772 {
5773  ArrayType *result;
5774  int *dimv;
5775  int *lbsv;
5776  int ndims;
5777  int nitems;
5778  int deflbs[MAXDIM];
5779  int16 elmlen;
5780  bool elmbyval;
5781  char elmalign;
5782  ArrayMetaState *my_extra;
5783 
5784  /*
5785  * Params checks
5786  */
5787  if (ARR_NDIM(dims) > 1)
5788  ereport(ERROR,
5789  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5790  errmsg("wrong number of array subscripts"),
5791  errdetail("Dimension array must be one dimensional.")));
5792 
5793  if (array_contains_nulls(dims))
5794  ereport(ERROR,
5795  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5796  errmsg("dimension values cannot be null")));
5797 
5798  dimv = (int *) ARR_DATA_PTR(dims);
5799  ndims = (ARR_NDIM(dims) > 0) ? ARR_DIMS(dims)[0] : 0;
5800 
5801  if (ndims < 0) /* we do allow zero-dimension arrays */
5802  ereport(ERROR,
5803  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
5804  errmsg("invalid number of dimensions: %d", ndims)));
5805  if (ndims > MAXDIM)
5806  ereport(ERROR,
5807  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
5808  errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
5809  ndims, MAXDIM)));
5810 
5811  if (lbs != NULL)
5812  {
5813  if (ARR_NDIM(lbs) > 1)
5814  ereport(ERROR,
5815  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5816  errmsg("wrong number of array subscripts"),
5817  errdetail("Dimension array must be one dimensional.")));
5818 
5819  if (array_contains_nulls(lbs))
5820  ereport(ERROR,
5821  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5822  errmsg("dimension values cannot be null")));
5823 
5824  if (ndims != ((ARR_NDIM(lbs) > 0) ? ARR_DIMS(lbs)[0] : 0))
5825  ereport(ERROR,
5826  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5827  errmsg("wrong number of array subscripts"),
5828  errdetail("Low bound array has different size than dimensions array.")));
5829 
5830  lbsv = (int *) ARR_DATA_PTR(lbs);
5831  }
5832  else
5833  {
5834  int i;
5835 
5836  for (i = 0; i < MAXDIM; i++)
5837  deflbs[i] = 1;
5838 
5839  lbsv = deflbs;
5840  }
5841 
5842  nitems = ArrayGetNItems(ndims, dimv);
5843 
5844  /* fast track for empty array */
5845  if (nitems <= 0)
5846  return construct_empty_array(elmtype);
5847 
5848  /*
5849  * We arrange to look up info about element type only once per series of
5850  * calls, assuming the element type doesn't change underneath us.
5851  */
5852  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
5853  if (my_extra == NULL)
5854  {
5855  fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
5856  sizeof(ArrayMetaState));
5857  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
5858  my_extra->element_type = InvalidOid;
5859  }
5860 
5861  if (my_extra->element_type != elmtype)
5862  {
5863  /* Get info about element type */
5864  get_typlenbyvalalign(elmtype,
5865  &my_extra->typlen,
5866  &my_extra->typbyval,
5867  &my_extra->typalign);
5868  my_extra->element_type = elmtype;
5869  }
5870 
5871  elmlen = my_extra->typlen;
5872  elmbyval = my_extra->typbyval;
5873  elmalign = my_extra->typalign;
5874 
5875  /* compute required space */
5876  if (!isnull)
5877  {
5878  int i;
5879  char *p;
5880  int nbytes;
5881  int totbytes;
5882 
5883  /* make sure data is not toasted */
5884  if (elmlen == -1)
5885  value = PointerGetDatum(PG_DETOAST_DATUM(value));
5886 
5887  nbytes = att_addlength_datum(0, elmlen, value);
5888  nbytes = att_align_nominal(nbytes, elmalign);
5889  Assert(nbytes > 0);
5890 
5891  totbytes = nbytes * nitems;
5892 
5893  /* check for overflow of multiplication or total request */
5894  if (totbytes / nbytes != nitems ||
5895  !AllocSizeIsValid(totbytes))
5896  ereport(ERROR,
5897  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
5898  errmsg("array size exceeds the maximum allowed (%d)",
5899  (int) MaxAllocSize)));
5900 
5901  /*
5902  * This addition can't overflow, but it might cause us to go past
5903  * MaxAllocSize. We leave it to palloc to complain in that case.
5904  */
5905  totbytes += ARR_OVERHEAD_NONULLS(ndims);
5906 
5907  result = create_array_envelope(ndims, dimv, lbsv, totbytes,
5908  elmtype, 0);
5909 
5910  p = ARR_DATA_PTR(result);
5911  for (i = 0; i < nitems; i++)
5912  p += ArrayCastAndSet(value, elmlen, elmbyval, elmalign, p);
5913  }
5914  else
5915  {
5916  int nbytes;
5917  int dataoffset;
5918 
5919  dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
5920  nbytes = dataoffset;
5921 
5922  result = create_array_envelope(ndims, dimv, lbsv, nbytes,
5923  elmtype, dataoffset);
5924 
5925  /* create_array_envelope already zeroed the bitmap, so we're done */
5926  }
5927 
5928  return result;
5929 }
5930 
5931 
5932 /*
5933  * UNNEST
5934  */
5935 Datum
5937 {
5938  typedef struct
5939  {
5940  array_iter iter;
5941  int nextelem;
5942  int numelems;
5943  int16 elmlen;
5944  bool elmbyval;
5945  char elmalign;
5946  } array_unnest_fctx;
5947 
5948  FuncCallContext *funcctx;
5949  array_unnest_fctx *fctx;
5950  MemoryContext oldcontext;
5951 
5952  /* stuff done only on the first call of the function */
5953  if (SRF_IS_FIRSTCALL())
5954  {
5955  AnyArrayType *arr;
5956 
5957  /* create a function context for cross-call persistence */
5958  funcctx = SRF_FIRSTCALL_INIT();
5959 
5960  /*
5961  * switch to memory context appropriate for multiple function calls
5962  */
5963  oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
5964 
5965  /*
5966  * Get the array value and detoast if needed. We can't do this
5967  * earlier because if we have to detoast, we want the detoasted copy
5968  * to be in multi_call_memory_ctx, so it will go away when we're done
5969  * and not before. (If no detoast happens, we assume the originally
5970  * passed array will stick around till then.)
5971  */
5972  arr = PG_GETARG_ANY_ARRAY_P(0);
5973 
5974  /* allocate memory for user context */
5975  fctx = (array_unnest_fctx *) palloc(sizeof(array_unnest_fctx));
5976 
5977  /* initialize state */
5978  array_iter_setup(&fctx->iter, arr);
5979  fctx->nextelem = 0;
5980  fctx->numelems = ArrayGetNItems(AARR_NDIM(arr), AARR_DIMS(arr));
5981 
5982  if (VARATT_IS_EXPANDED_HEADER(arr))
5983  {
5984  /* we can just grab the type data from expanded array */
5985  fctx->elmlen = arr->xpn.typlen;
5986  fctx->elmbyval = arr->xpn.typbyval;
5987  fctx->elmalign = arr->xpn.typalign;
5988  }
5989  else
5991  &fctx->elmlen,
5992  &fctx->elmbyval,
5993  &fctx->elmalign);
5994 
5995  funcctx->user_fctx = fctx;
5996  MemoryContextSwitchTo(oldcontext);
5997  }
5998 
5999  /* stuff done on every call of the function */
6000  funcctx = SRF_PERCALL_SETUP();
6001  fctx = funcctx->user_fctx;
6002 
6003  if (fctx->nextelem < fctx->numelems)
6004  {
6005  int offset = fctx->nextelem++;
6006  Datum elem;
6007 
6008  elem = array_iter_next(&fctx->iter, &fcinfo->isnull, offset,
6009  fctx->elmlen, fctx->elmbyval, fctx->elmalign);
6010 
6011  SRF_RETURN_NEXT(funcctx, elem);
6012  }
6013  else
6014  {
6015  /* do when there is no more left */
6016  SRF_RETURN_DONE(funcctx);
6017  }
6018 }
6019 
6020 
6021 /*
6022  * array_replace/array_remove support
6023  *
6024  * Find all array entries matching (not distinct from) search/search_isnull,
6025  * and delete them if remove is true, else replace them with
6026  * replace/replace_isnull. Comparisons are done using the specified
6027  * collation. fcinfo is passed only for caching purposes.
6028  */
6029 static ArrayType *
6031  Datum search, bool search_isnull,
6032  Datum replace, bool replace_isnull,
6033  bool remove, Oid collation,
6034  FunctionCallInfo fcinfo)
6035 {
6036  ArrayType *result;
6037  Oid element_type;
6038  Datum *values;
6039  bool *nulls;
6040  int *dim;
6041  int ndim;
6042  int nitems,
6043  nresult;
6044  int i;
6045  int32 nbytes = 0;
6046  int32 dataoffset;
6047  bool hasnulls;
6048  int typlen;
6049  bool typbyval;
6050  char typalign;
6051  char *arraydataptr;
6052  bits8 *bitmap;
6053  int bitmask;
6054  bool changed = false;
6055  TypeCacheEntry *typentry;
6056  FunctionCallInfoData locfcinfo;
6057 
6058  element_type = ARR_ELEMTYPE(array);
6059  ndim = ARR_NDIM(array);
6060  dim = ARR_DIMS(array);
6061  nitems = ArrayGetNItems(ndim, dim);
6062 
6063  /* Return input array unmodified if it is empty */
6064  if (nitems <= 0)
6065  return array;
6066 
6067  /*
6068  * We can't remove elements from multi-dimensional arrays, since the
6069  * result might not be rectangular.
6070  */
6071  if (remove && ndim > 1)
6072  ereport(ERROR,
6073  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6074  errmsg("removing elements from multidimensional arrays is not supported")));
6075 
6076  /*
6077  * We arrange to look up the equality function only once per series of
6078  * calls, assuming the element type doesn't change underneath us.
6079  */
6080  typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
6081  if (typentry == NULL ||
6082  typentry->type_id != element_type)
6083  {
6084  typentry = lookup_type_cache(element_type,
6086  if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
6087  ereport(ERROR,
6088  (errcode(ERRCODE_UNDEFINED_FUNCTION),
6089  errmsg("could not identify an equality operator for type %s",
6090  format_type_be(element_type))));
6091  fcinfo->flinfo->fn_extra = (void *) typentry;
6092  }
6093  typlen = typentry->typlen;
6094  typbyval = typentry->typbyval;
6095  typalign = typentry->typalign;
6096 
6097  /*
6098  * Detoast values if they are toasted. The replacement value must be
6099  * detoasted for insertion into the result array, while detoasting the
6100  * search value only once saves cycles.
6101  */
6102  if (typlen == -1)
6103  {
6104  if (!search_isnull)
6105  search = PointerGetDatum(PG_DETOAST_DATUM(search));
6106  if (!replace_isnull)
6107  replace = PointerGetDatum(PG_DETOAST_DATUM(replace));
6108  }
6109 
6110  /* Prepare to apply the comparison operator */
6111  InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
6112  collation, NULL, NULL);
6113 
6114  /* Allocate temporary arrays for new values */
6115  values = (Datum *) palloc(nitems * sizeof(Datum));
6116  nulls = (bool *) palloc(nitems * sizeof(bool));
6117 
6118  /* Loop over source data */
6119  arraydataptr =