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