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