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