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