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