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