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