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