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