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
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 (!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  locfcinfo->isnull = false;
3845  cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
3846 
3847  if (cmpresult == 0)
3848  continue; /* equal */
3849 
3850  if (cmpresult < 0)
3851  {
3852  /* arg1 is less than arg2 */
3853  result = -1;
3854  break;
3855  }
3856  else
3857  {
3858  /* arg1 is greater than arg2 */
3859  result = 1;
3860  break;
3861  }
3862  }
3863 
3864  /*
3865  * If arrays contain same data (up to end of shorter one), apply
3866  * additional rules to sort by dimensionality. The relative significance
3867  * of the different bits of information is historical; mainly we just care
3868  * that we don't say "equal" for arrays of different dimensionality.
3869  */
3870  if (result == 0)
3871  {
3872  if (nitems1 != nitems2)
3873  result = (nitems1 < nitems2) ? -1 : 1;
3874  else if (ndims1 != ndims2)
3875  result = (ndims1 < ndims2) ? -1 : 1;
3876  else
3877  {
3878  for (i = 0; i < ndims1; i++)
3879  {
3880  if (dims1[i] != dims2[i])
3881  {
3882  result = (dims1[i] < dims2[i]) ? -1 : 1;
3883  break;
3884  }
3885  }
3886  if (result == 0)
3887  {
3888  int *lbound1 = AARR_LBOUND(array1);
3889  int *lbound2 = AARR_LBOUND(array2);
3890 
3891  for (i = 0; i < ndims1; i++)
3892  {
3893  if (lbound1[i] != lbound2[i])
3894  {
3895  result = (lbound1[i] < lbound2[i]) ? -1 : 1;
3896  break;
3897  }
3898  }
3899  }
3900  }
3901  }
3902 
3903  /* Avoid leaking memory when handed toasted input. */
3904  AARR_FREE_IF_COPY(array1, 0);
3905  AARR_FREE_IF_COPY(array2, 1);
3906 
3907  return result;
3908 }
3909 
3910 
3911 /*-----------------------------------------------------------------------------
3912  * array hashing
3913  * Hash the elements and combine the results.
3914  *----------------------------------------------------------------------------
3915  */
3916 
3917 Datum
3919 {
3920  LOCAL_FCINFO(locfcinfo, 1);
3921  AnyArrayType *array = PG_GETARG_ANY_ARRAY_P(0);
3922  int ndims = AARR_NDIM(array);
3923  int *dims = AARR_DIMS(array);
3924  Oid element_type = AARR_ELEMTYPE(array);
3925  uint32 result = 1;
3926  int nitems;
3927  TypeCacheEntry *typentry;
3928  int typlen;
3929  bool typbyval;
3930  char typalign;
3931  int i;
3932  array_iter iter;
3933 
3934  /*
3935  * We arrange to look up the hash function only once per series of calls,
3936  * assuming the element type doesn't change underneath us. The typcache
3937  * is used so that we have no memory leakage when being used as an index
3938  * support function.
3939  */
3940  typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3941  if (typentry == NULL ||
3942  typentry->type_id != element_type)
3943  {
3944  typentry = lookup_type_cache(element_type,
3946  if (!OidIsValid(typentry->hash_proc_finfo.fn_oid))
3947  ereport(ERROR,
3948  (errcode(ERRCODE_UNDEFINED_FUNCTION),
3949  errmsg("could not identify a hash function for type %s",
3950  format_type_be(element_type))));
3951  fcinfo->flinfo->fn_extra = (void *) typentry;
3952  }
3953  typlen = typentry->typlen;
3954  typbyval = typentry->typbyval;
3955  typalign = typentry->typalign;
3956 
3957  /*
3958  * apply the hash function to each array element.
3959  */
3960  InitFunctionCallInfoData(*locfcinfo, &typentry->hash_proc_finfo, 1,
3961  PG_GET_COLLATION(), NULL, NULL);
3962 
3963  /* Loop over source data */
3964  nitems = ArrayGetNItems(ndims, dims);
3965  array_iter_setup(&iter, array);
3966 
3967  for (i = 0; i < nitems; i++)
3968  {
3969  Datum elt;
3970  bool isnull;
3971  uint32 elthash;
3972 
3973  /* Get element, checking for NULL */
3974  elt = array_iter_next(&iter, &isnull, i, typlen, typbyval, typalign);
3975 
3976  if (isnull)
3977  {
3978  /* Treat nulls as having hashvalue 0 */
3979  elthash = 0;
3980  }
3981  else
3982  {
3983  /* Apply the hash function */
3984  locfcinfo->args[0].value = elt;
3985  locfcinfo->args[0].isnull = false;
3986  locfcinfo->isnull = false;
3987  elthash = DatumGetUInt32(FunctionCallInvoke(locfcinfo));
3988  }
3989 
3990  /*
3991  * Combine hash values of successive elements by multiplying the
3992  * current value by 31 and adding on the new element's hash value.
3993  *
3994  * The result is a sum in which each element's hash value is
3995  * multiplied by a different power of 31. This is modulo 2^32
3996  * arithmetic, and the powers of 31 modulo 2^32 form a cyclic group of
3997  * order 2^27. So for arrays of up to 2^27 elements, each element's
3998  * hash value is multiplied by a different (odd) number, resulting in
3999  * a good mixing of all the elements' hash values.
4000  */
4001  result = (result << 5) - result + elthash;
4002  }
4003 
4004  /* Avoid leaking memory when handed toasted input. */
4005  AARR_FREE_IF_COPY(array, 0);
4006 
4007  PG_RETURN_UINT32(result);
4008 }
4009 
4010 /*
4011  * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
4012  * Otherwise, similar to hash_array.
4013  */
4014 Datum
4016 {
4017  LOCAL_FCINFO(locfcinfo, 2);
4018  AnyArrayType *array = PG_GETARG_ANY_ARRAY_P(0);
4019  uint64 seed = PG_GETARG_INT64(1);
4020  int ndims = AARR_NDIM(array);
4021  int *dims = AARR_DIMS(array);
4022  Oid element_type = AARR_ELEMTYPE(array);
4023  uint64 result = 1;
4024  int nitems;
4025  TypeCacheEntry *typentry;
4026  int typlen;
4027  bool typbyval;
4028  char typalign;
4029  int i;
4030  array_iter iter;
4031 
4032  typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
4033  if (typentry == NULL ||
4034  typentry->type_id != element_type)
4035  {
4036  typentry = lookup_type_cache(element_type,
4038  if (!OidIsValid(typentry->hash_extended_proc_finfo.fn_oid))
4039  ereport(ERROR,
4040  (errcode(ERRCODE_UNDEFINED_FUNCTION),
4041  errmsg("could not identify an extended hash function for type %s",
4042  format_type_be(element_type))));
4043  fcinfo->flinfo->fn_extra = (void *) typentry;
4044  }
4045  typlen = typentry->typlen;
4046  typbyval = typentry->typbyval;
4047  typalign = typentry->typalign;
4048 
4049  InitFunctionCallInfoData(*locfcinfo, &typentry->hash_extended_proc_finfo, 2,
4050  InvalidOid, NULL, NULL);
4051 
4052  /* Loop over source data */
4053  nitems = ArrayGetNItems(ndims, dims);
4054  array_iter_setup(&iter, array);
4055 
4056  for (i = 0; i < nitems; i++)
4057  {
4058  Datum elt;
4059  bool isnull;
4060  uint64 elthash;
4061 
4062  /* Get element, checking for NULL */
4063  elt = array_iter_next(&iter, &isnull, i, typlen, typbyval, typalign);
4064 
4065  if (isnull)
4066  {
4067  elthash = 0;
4068  }
4069  else
4070  {
4071  /* Apply the hash function */
4072  locfcinfo->args[0].value = elt;
4073  locfcinfo->args[0].isnull = false;
4074  locfcinfo->args[1].value = Int64GetDatum(seed);
4075  locfcinfo->args[1].isnull = false;
4076  elthash = DatumGetUInt64(FunctionCallInvoke(locfcinfo));
4077  }
4078 
4079  result = (result << 5) - result + elthash;
4080  }
4081 
4082  AARR_FREE_IF_COPY(array, 0);
4083 
4084  PG_RETURN_UINT64(result);
4085 }
4086 
4087 
4088 /*-----------------------------------------------------------------------------
4089  * array overlap/containment comparisons
4090  * These use the same methods of comparing array elements as array_eq.
4091  * We consider only the elements of the arrays, ignoring dimensionality.
4092  *----------------------------------------------------------------------------
4093  */
4094 
4095 /*
4096  * array_contain_compare :
4097  * compares two arrays for overlap/containment
4098  *
4099  * When matchall is true, return true if all members of array1 are in array2.
4100  * When matchall is false, return true if any members of array1 are in array2.
4101  */
4102 static bool
4104  bool matchall, void **fn_extra)
4105 {
4106  LOCAL_FCINFO(locfcinfo, 2);
4107  bool result = matchall;
4108  Oid element_type = AARR_ELEMTYPE(array1);
4109  TypeCacheEntry *typentry;
4110  int nelems1;
4111  Datum *values2;
4112  bool *nulls2;
4113  int nelems2;
4114  int typlen;
4115  bool typbyval;
4116  char typalign;
4117  int i;
4118  int j;
4119  array_iter it1;
4120 
4121  if (element_type != AARR_ELEMTYPE(array2))
4122  ereport(ERROR,
4123  (errcode(ERRCODE_DATATYPE_MISMATCH),
4124  errmsg("cannot compare arrays of different element types")));
4125 
4126  /*
4127  * We arrange to look up the equality function only once per series of
4128  * calls, assuming the element type doesn't change underneath us. The
4129  * typcache is used so that we have no memory leakage when being used as
4130  * an index support function.
4131  */
4132  typentry = (TypeCacheEntry *) *fn_extra;
4133  if (typentry == NULL ||
4134  typentry->type_id != element_type)
4135  {
4136  typentry = lookup_type_cache(element_type,
4138  if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
4139  ereport(ERROR,
4140  (errcode(ERRCODE_UNDEFINED_FUNCTION),
4141  errmsg("could not identify an equality operator for type %s",
4142  format_type_be(element_type))));
4143  *fn_extra = (void *) typentry;
4144  }
4145  typlen = typentry->typlen;
4146  typbyval = typentry->typbyval;
4147  typalign = typentry->typalign;
4148 
4149  /*
4150  * Since we probably will need to scan array2 multiple times, it's
4151  * worthwhile to use deconstruct_array on it. We scan array1 the hard way
4152  * however, since we very likely won't need to look at all of it.
4153  */
4154  if (VARATT_IS_EXPANDED_HEADER(array2))
4155  {
4156  /* This should be safe even if input is read-only */
4157  deconstruct_expanded_array(&(array2->xpn));
4158  values2 = array2->xpn.dvalues;
4159  nulls2 = array2->xpn.dnulls;
4160  nelems2 = array2->xpn.nelems;
4161  }
4162  else
4163  deconstruct_array((ArrayType *) array2,
4164  element_type, typlen, typbyval, typalign,
4165  &values2, &nulls2, &nelems2);
4166 
4167  /*
4168  * Apply the comparison operator to each pair of array elements.
4169  */
4170  InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
4171  collation, NULL, NULL);
4172 
4173  /* Loop over source data */
4174  nelems1 = ArrayGetNItems(AARR_NDIM(array1), AARR_DIMS(array1));
4175  array_iter_setup(&it1, array1);
4176 
4177  for (i = 0; i < nelems1; i++)
4178  {
4179  Datum elt1;
4180  bool isnull1;
4181 
4182  /* Get element, checking for NULL */
4183  elt1 = array_iter_next(&it1, &isnull1, i, typlen, typbyval, typalign);
4184 
4185  /*
4186  * We assume that the comparison operator is strict, so a NULL can't
4187  * match anything. XXX this diverges from the "NULL=NULL" behavior of
4188  * array_eq, should we act like that?
4189  */
4190  if (isnull1)
4191  {
4192  if (matchall)
4193  {
4194  result = false;
4195  break;
4196  }
4197  continue;
4198  }
4199 
4200  for (j = 0; j < nelems2; j++)
4201  {
4202  Datum elt2 = values2[j];
4203  bool isnull2 = nulls2 ? nulls2[j] : false;
4204  bool oprresult;
4205 
4206  if (isnull2)
4207  continue; /* can't match */
4208 
4209  /*
4210  * Apply the operator to the element pair
4211  */
4212  locfcinfo->args[0].value = elt1;
4213  locfcinfo->args[0].isnull = false;
4214  locfcinfo->args[1].value = elt2;
4215  locfcinfo->args[1].isnull = false;
4216  locfcinfo->isnull = false;
4217  oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
4218  if (oprresult)
4219  break;
4220  }
4221 
4222  if (j < nelems2)
4223  {
4224  /* found a match for elt1 */
4225  if (!matchall)
4226  {
4227  result = true;
4228  break;
4229  }
4230  }
4231  else
4232  {
4233  /* no match for elt1 */
4234  if (matchall)
4235  {
4236  result = false;
4237  break;
4238  }
4239  }
4240  }
4241 
4242  return result;
4243 }
4244 
4245 Datum
4247 {
4248  AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
4249  AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
4250  Oid collation = PG_GET_COLLATION();
4251  bool result;
4252 
4253  result = array_contain_compare(array1, array2, collation, false,
4254  &fcinfo->flinfo->fn_extra);
4255 
4256  /* Avoid leaking memory when handed toasted input. */
4257  AARR_FREE_IF_COPY(array1, 0);
4258  AARR_FREE_IF_COPY(array2, 1);
4259 
4260  PG_RETURN_BOOL(result);
4261 }
4262 
4263 Datum
4265 {
4266  AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
4267  AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
4268  Oid collation = PG_GET_COLLATION();
4269  bool result;
4270 
4271  result = array_contain_compare(array2, array1, collation, true,
4272  &fcinfo->flinfo->fn_extra);
4273 
4274  /* Avoid leaking memory when handed toasted input. */
4275  AARR_FREE_IF_COPY(array1, 0);
4276  AARR_FREE_IF_COPY(array2, 1);
4277 
4278  PG_RETURN_BOOL(result);
4279 }
4280 
4281 Datum
4283 {
4284  AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
4285  AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
4286  Oid collation = PG_GET_COLLATION();
4287  bool result;
4288 
4289  result = array_contain_compare(array1, array2, collation, true,
4290  &fcinfo->flinfo->fn_extra);
4291 
4292  /* Avoid leaking memory when handed toasted input. */
4293  AARR_FREE_IF_COPY(array1, 0);
4294  AARR_FREE_IF_COPY(array2, 1);
4295 
4296  PG_RETURN_BOOL(result);
4297 }
4298 
4299 
4300 /*-----------------------------------------------------------------------------
4301  * Array iteration functions
4302  * These functions are used to iterate efficiently through arrays
4303  *-----------------------------------------------------------------------------
4304  */
4305 
4306 /*
4307  * array_create_iterator --- set up to iterate through an array
4308  *
4309  * If slice_ndim is zero, we will iterate element-by-element; the returned
4310  * datums are of the array's element type.
4311  *
4312  * If slice_ndim is 1..ARR_NDIM(arr), we will iterate by slices: the
4313  * returned datums are of the same array type as 'arr', but of size
4314  * equal to the rightmost N dimensions of 'arr'.
4315  *
4316  * The passed-in array must remain valid for the lifetime of the iterator.
4317  */
4320 {
4321  ArrayIterator iterator = palloc0(sizeof(ArrayIteratorData));
4322 
4323  /*
4324  * Sanity-check inputs --- caller should have got this right already
4325  */
4326  Assert(PointerIsValid(arr));
4327  if (slice_ndim < 0 || slice_ndim > ARR_NDIM(arr))
4328  elog(ERROR, "invalid arguments to array_create_iterator");
4329 
4330  /*
4331  * Remember basic info about the array and its element type
4332  */
4333  iterator->arr = arr;
4334  iterator->nullbitmap = ARR_NULLBITMAP(arr);
4335  iterator->nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
4336 
4337  if (mstate != NULL)
4338  {
4339  Assert(mstate->element_type == ARR_ELEMTYPE(arr));
4340 
4341  iterator->typlen = mstate->typlen;
4342  iterator->typbyval = mstate->typbyval;
4343  iterator->typalign = mstate->typalign;
4344  }
4345  else
4347  &iterator->typlen,
4348  &iterator->typbyval,
4349  &iterator->typalign);
4350 
4351  /*
4352  * Remember the slicing parameters.
4353  */
4354  iterator->slice_ndim = slice_ndim;
4355 
4356  if (slice_ndim > 0)
4357  {
4358  /*
4359  * Get pointers into the array's dims and lbound arrays to represent
4360  * the dims/lbound arrays of a slice. These are the same as the
4361  * rightmost N dimensions of the array.
4362  */
4363  iterator->slice_dims = ARR_DIMS(arr) + ARR_NDIM(arr) - slice_ndim;
4364  iterator->slice_lbound = ARR_LBOUND(arr) + ARR_NDIM(arr) - slice_ndim;
4365 
4366  /*
4367  * Compute number of elements in a slice.
4368  */
4369  iterator->slice_len = ArrayGetNItems(slice_ndim,
4370  iterator->slice_dims);
4371 
4372  /*
4373  * Create workspace for building sub-arrays.
4374  */
4375  iterator->slice_values = (Datum *)
4376  palloc(iterator->slice_len * sizeof(Datum));
4377  iterator->slice_nulls = (bool *)
4378  palloc(iterator->slice_len * sizeof(bool));
4379  }
4380 
4381  /*
4382  * Initialize our data pointer and linear element number. These will
4383  * advance through the array during array_iterate().
4384  */
4385  iterator->data_ptr = ARR_DATA_PTR(arr);
4386  iterator->current_item = 0;
4387 
4388  return iterator;
4389 }
4390 
4391 /*
4392  * Iterate through the array referenced by 'iterator'.
4393  *
4394  * As long as there is another element (or slice), return it into
4395  * *value / *isnull, and return true. Return false when no more data.
4396  */
4397 bool
4398 array_iterate(ArrayIterator iterator, Datum *value, bool *isnull)
4399 {
4400  /* Done if we have reached the end of the array */
4401  if (iterator->current_item >= iterator->nitems)
4402  return false;
4403 
4404  if (iterator->slice_ndim == 0)
4405  {
4406  /*
4407  * Scalar case: return one element.
4408  */
4409  if (array_get_isnull(iterator->nullbitmap, iterator->current_item++))
4410  {
4411  *isnull = true;
4412  *value = (Datum) 0;
4413  }
4414  else
4415  {
4416  /* non-NULL, so fetch the individual Datum to return */
4417  char *p = iterator->data_ptr;
4418 
4419  *isnull = false;
4420  *value = fetch_att(p, iterator->typbyval, iterator->typlen);
4421 
4422  /* Move our data pointer forward to the next element */
4423  p = att_addlength_pointer(p, iterator->typlen, p);
4424  p = (char *) att_align_nominal(p, iterator->typalign);
4425  iterator->data_ptr = p;
4426  }
4427  }
4428  else
4429  {
4430  /*
4431  * Slice case: build and return an array of the requested size.
4432  */
4433  ArrayType *result;
4434  Datum *values = iterator->slice_values;
4435  bool *nulls = iterator->slice_nulls;
4436  char *p = iterator->data_ptr;
4437  int i;
4438 
4439  for (i = 0; i < iterator->slice_len; i++)
4440  {
4441  if (array_get_isnull(iterator->nullbitmap,
4442  iterator->current_item++))
4443  {
4444  nulls[i] = true;
4445  values[i] = (Datum) 0;
4446  }
4447  else
4448  {
4449  nulls[i] = false;
4450  values[i] = fetch_att(p, iterator->typbyval, iterator->typlen);
4451 
4452  /* Move our data pointer forward to the next element */
4453  p = att_addlength_pointer(p, iterator->typlen, p);
4454  p = (char *) att_align_nominal(p, iterator->typalign);
4455  }
4456  }
4457 
4458  iterator->data_ptr = p;
4459 
4460  result = construct_md_array(values,
4461  nulls,
4462  iterator->slice_ndim,
4463  iterator->slice_dims,
4464  iterator->slice_lbound,
4465  ARR_ELEMTYPE(iterator->arr),
4466  iterator->typlen,
4467  iterator->typbyval,
4468  iterator->typalign);
4469 
4470  *isnull = false;
4471  *value = PointerGetDatum(result);
4472  }
4473 
4474  return true;
4475 }
4476 
4477 /*
4478  * Release an ArrayIterator data structure
4479  */
4480 void
4482 {
4483  if (iterator->slice_ndim > 0)
4484  {
4485  pfree(iterator->slice_values);
4486  pfree(iterator->slice_nulls);
4487  }
4488  pfree(iterator);
4489 }
4490 
4491 
4492 /***************************************************************************/
4493 /******************| Support Routines |*****************/
4494 /***************************************************************************/
4495 
4496 /*
4497  * Check whether a specific array element is NULL
4498  *
4499  * nullbitmap: pointer to array's null bitmap (NULL if none)
4500  * offset: 0-based linear element number of array element
4501  */
4502 static bool
4503 array_get_isnull(const bits8 *nullbitmap, int offset)
4504 {
4505  if (nullbitmap == NULL)
4506  return false; /* assume not null */
4507  if (nullbitmap[offset / 8] & (1 << (offset % 8)))
4508  return false; /* not null */
4509  return true;
4510 }
4511 
4512 /*
4513  * Set a specific array element's null-bitmap entry
4514  *
4515  * nullbitmap: pointer to array's null bitmap (mustn't be NULL)
4516  * offset: 0-based linear element number of array element
4517  * isNull: null status to set
4518  */
4519 static void
4520 array_set_isnull(bits8 *nullbitmap, int offset, bool isNull)
4521 {
4522  int bitmask;
4523 
4524  nullbitmap += offset / 8;
4525  bitmask = 1 << (offset % 8);
4526  if (isNull)
4527  *nullbitmap &= ~bitmask;
4528  else
4529  *nullbitmap |= bitmask;
4530 }
4531 
4532 /*
4533  * Fetch array element at pointer, converted correctly to a Datum
4534  *
4535  * Caller must have handled case of NULL element
4536  */
4537 static Datum
4538 ArrayCast(char *value, bool byval, int len)
4539 {
4540  return fetch_att(value, byval, len);
4541 }
4542 
4543 /*
4544  * Copy datum to *dest and return total space used (including align padding)
4545  *
4546  * Caller must have handled case of NULL element
4547  */
4548 static int
4550  int typlen,
4551  bool typbyval,
4552  char typalign,
4553  char *dest)
4554 {
4555  int inc;
4556 
4557  if (typlen > 0)
4558  {
4559  if (typbyval)
4560  store_att_byval(dest, src, typlen);
4561  else
4562  memmove(dest, DatumGetPointer(src), typlen);
4563  inc = att_align_nominal(typlen, typalign);
4564  }
4565  else
4566  {
4567  Assert(!typbyval);
4568  inc = att_addlength_datum(0, typlen, src);
4569  memmove(dest, DatumGetPointer(src), inc);
4570  inc = att_align_nominal(inc, typalign);
4571  }
4572 
4573  return inc;
4574 }
4575 
4576 /*
4577  * Advance ptr over nitems array elements
4578  *
4579  * ptr: starting location in array
4580  * offset: 0-based linear element number of first element (the one at *ptr)
4581  * nullbitmap: start of array's null bitmap, or NULL if none
4582  * nitems: number of array elements to advance over (>= 0)
4583  * typlen, typbyval, typalign: storage parameters of array element datatype
4584  *
4585  * It is caller's responsibility to ensure that nitems is within range
4586  */
4587 static char *
4588 array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
4589  int typlen, bool typbyval, char typalign)
4590 {
4591  int bitmask;
4592  int i;
4593 
4594  /* easy if fixed-size elements and no NULLs */
4595  if (typlen > 0 && !nullbitmap)
4596  return ptr + nitems * ((Size) att_align_nominal(typlen, typalign));
4597 
4598  /* seems worth having separate loops for NULL and no-NULLs cases */
4599  if (nullbitmap)
4600  {
4601  nullbitmap += offset / 8;
4602  bitmask = 1 << (offset % 8);
4603 
4604  for (i = 0; i < nitems; i++)
4605  {
4606  if (*nullbitmap & bitmask)
4607  {
4608  ptr = att_addlength_pointer(ptr, typlen, ptr);
4609  ptr = (char *) att_align_nominal(ptr, typalign);
4610  }
4611  bitmask <<= 1;
4612  if (bitmask == 0x100)
4613  {
4614  nullbitmap++;
4615  bitmask = 1;
4616  }
4617  }
4618  }
4619  else
4620  {
4621  for (i = 0; i < nitems; i++)
4622  {
4623  ptr = att_addlength_pointer(ptr, typlen, ptr);
4624  ptr = (char *) att_align_nominal(ptr, typalign);
4625  }
4626  }
4627  return ptr;
4628 }
4629 
4630 /*
4631  * Compute total size of the nitems array elements starting at *ptr
4632  *
4633  * Parameters same as for array_seek
4634  */
4635 static int
4636 array_nelems_size(char *ptr, int offset, bits8 *nullbitmap, int nitems,
4637  int typlen, bool typbyval, char typalign)
4638 {
4639  return array_seek(ptr, offset, nullbitmap, nitems,
4640  typlen, typbyval, typalign) - ptr;
4641 }
4642 
4643 /*
4644  * Copy nitems array elements from srcptr to destptr
4645  *
4646  * destptr: starting destination location (must be enough room!)
4647  * nitems: number of array elements to copy (>= 0)
4648  * srcptr: starting location in source array
4649  * offset: 0-based linear element number of first element (the one at *srcptr)
4650  * nullbitmap: start of source array's null bitmap, or NULL if none
4651  * typlen, typbyval, typalign: storage parameters of array element datatype
4652  *
4653  * Returns number of bytes copied
4654  *
4655  * NB: this does not take care of setting up the destination's null bitmap!
4656  */
4657 static int
4658 array_copy(char *destptr, int nitems,
4659  char *srcptr, int offset, bits8 *nullbitmap,
4660  int typlen, bool typbyval, char typalign)
4661 {
4662  int numbytes;
4663 
4664  numbytes = array_nelems_size(srcptr, offset, nullbitmap, nitems,
4665  typlen, typbyval, typalign);
4666  memcpy(destptr, srcptr, numbytes);
4667  return numbytes;
4668 }
4669 
4670 /*
4671  * Copy nitems null-bitmap bits from source to destination
4672  *
4673  * destbitmap: start of destination array's null bitmap (mustn't be NULL)
4674  * destoffset: 0-based linear element number of first dest element
4675  * srcbitmap: start of source array's null bitmap, or NULL if none
4676  * srcoffset: 0-based linear element number of first source element
4677  * nitems: number of bits to copy (>= 0)
4678  *
4679  * If srcbitmap is NULL then we assume the source is all-non-NULL and
4680  * fill 1's into the destination bitmap. Note that only the specified
4681  * bits in the destination map are changed, not any before or after.
4682  *
4683  * Note: this could certainly be optimized using standard bitblt methods.
4684  * However, it's not clear that the typical Postgres array has enough elements
4685  * to make it worth worrying too much. For the moment, KISS.
4686  */
4687 void
4688 array_bitmap_copy(bits8 *destbitmap, int destoffset,
4689  const bits8 *srcbitmap, int srcoffset,
4690  int nitems)
4691 {
4692  int destbitmask,
4693  destbitval,
4694  srcbitmask,
4695  srcbitval;
4696 
4697  Assert(destbitmap);
4698  if (nitems <= 0)
4699  return; /* don't risk fetch off end of memory */
4700  destbitmap += destoffset / 8;
4701  destbitmask = 1 << (destoffset % 8);
4702  destbitval = *destbitmap;
4703  if (srcbitmap)
4704  {
4705  srcbitmap += srcoffset / 8;
4706  srcbitmask = 1 << (srcoffset % 8);
4707  srcbitval = *srcbitmap;
4708  while (nitems-- > 0)
4709  {
4710  if (srcbitval & srcbitmask)
4711  destbitval |= destbitmask;
4712  else
4713  destbitval &= ~destbitmask;
4714  destbitmask <<= 1;
4715  if (destbitmask == 0x100)
4716  {
4717  *destbitmap++ = destbitval;
4718  destbitmask = 1;
4719  if (nitems > 0)
4720  destbitval = *destbitmap;
4721  }
4722  srcbitmask <<= 1;
4723  if (srcbitmask == 0x100)
4724  {
4725  srcbitmap++;
4726  srcbitmask = 1;
4727  if (nitems > 0)
4728  srcbitval = *srcbitmap;
4729  }
4730  }
4731  if (destbitmask != 1)
4732  *destbitmap = destbitval;
4733  }
4734  else
4735  {
4736  while (nitems-- > 0)
4737  {
4738  destbitval |= destbitmask;
4739  destbitmask <<= 1;
4740  if (destbitmask == 0x100)
4741  {
4742  *destbitmap++ = destbitval;
4743  destbitmask = 1;
4744  if (nitems > 0)
4745  destbitval = *destbitmap;
4746  }
4747  }
4748  if (destbitmask != 1)
4749  *destbitmap = destbitval;
4750  }
4751 }
4752 
4753 /*
4754  * Compute space needed for a slice of an array
4755  *
4756  * We assume the caller has verified that the slice coordinates are valid.
4757  */
4758 static int
4759 array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
4760  int ndim, int *dim, int *lb,
4761  int *st, int *endp,
4762  int typlen, bool typbyval, char typalign)
4763 {
4764  int src_offset,
4765  span[MAXDIM],
4766  prod[MAXDIM],
4767  dist[MAXDIM],
4768  indx[MAXDIM];
4769  char *ptr;
4770  int i,
4771  j,
4772  inc;
4773  int count = 0;
4774 
4775  mda_get_range(ndim, span, st, endp);
4776 
4777  /* Pretty easy for fixed element length without nulls ... */
4778  if (typlen > 0 && !arraynullsptr)
4779  return ArrayGetNItems(ndim, span) * att_align_nominal(typlen, typalign);
4780 
4781  /* Else gotta do it the hard way */
4782  src_offset = ArrayGetOffset(ndim, dim, lb, st);
4783  ptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
4784  typlen, typbyval, typalign);
4785  mda_get_prod(ndim, dim, prod);
4786  mda_get_offset_values(ndim, dist, prod, span);
4787  for (i = 0; i < ndim; i++)
4788  indx[i] = 0;
4789  j = ndim - 1;
4790  do
4791  {
4792  if (dist[j])
4793  {
4794  ptr = array_seek(ptr, src_offset, arraynullsptr, dist[j],
4795  typlen, typbyval, typalign);
4796  src_offset += dist[j];
4797  }
4798  if (!array_get_isnull(arraynullsptr, src_offset))
4799  {
4800  inc = att_addlength_pointer(0, typlen, ptr);
4801  inc = att_align_nominal(inc, typalign);
4802  ptr += inc;
4803  count += inc;
4804  }
4805  src_offset++;
4806  } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
4807  return count;
4808 }
4809 
4810 /*
4811  * Extract a slice of an array into consecutive elements in the destination
4812  * array.
4813  *
4814  * We assume the caller has verified that the slice coordinates are valid,
4815  * allocated enough storage for the result, and initialized the header
4816  * of the new array.
4817  */
4818 static void
4820  int ndim,
4821  int *dim,
4822  int *lb,
4823  char *arraydataptr,
4824  bits8 *arraynullsptr,
4825  int *st,
4826  int *endp,
4827  int typlen,
4828  bool typbyval,
4829  char typalign)
4830 {
4831  char *destdataptr = ARR_DATA_PTR(newarray);
4832  bits8 *destnullsptr = ARR_NULLBITMAP(newarray);
4833  char *srcdataptr;
4834  int src_offset,
4835  dest_offset,
4836  prod[MAXDIM],
4837  span[MAXDIM],
4838  dist[MAXDIM],
4839  indx[MAXDIM];
4840  int i,
4841  j,
4842  inc;
4843 
4844  src_offset = ArrayGetOffset(ndim, dim, lb, st);
4845  srcdataptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
4846  typlen, typbyval, typalign);
4847  mda_get_prod(ndim, dim, prod);
4848  mda_get_range(ndim, span, st, endp);
4849  mda_get_offset_values(ndim, dist, prod, span);
4850  for (i = 0; i < ndim; i++)
4851  indx[i] = 0;
4852  dest_offset = 0;
4853  j = ndim - 1;
4854  do
4855  {
4856  if (dist[j])
4857  {
4858  /* skip unwanted elements */
4859  srcdataptr = array_seek(srcdataptr, src_offset, arraynullsptr,
4860  dist[j],
4861  typlen, typbyval, typalign);
4862  src_offset += dist[j];
4863  }
4864  inc = array_copy(destdataptr, 1,
4865  srcdataptr, src_offset, arraynullsptr,
4866  typlen, typbyval, typalign);
4867  if (destnullsptr)
4868  array_bitmap_copy(destnullsptr, dest_offset,
4869  arraynullsptr, src_offset,
4870  1);
4871  destdataptr += inc;
4872  srcdataptr += inc;
4873  src_offset++;
4874  dest_offset++;
4875  } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
4876 }
4877 
4878 /*
4879  * Insert a slice into an array.
4880  *
4881  * ndim/dim[]/lb[] are dimensions of the original array. A new array with
4882  * those same dimensions is to be constructed. destArray must already
4883  * have been allocated and its header initialized.
4884  *
4885  * st[]/endp[] identify the slice to be replaced. Elements within the slice
4886  * volume are taken from consecutive elements of the srcArray; elements
4887  * outside it are copied from origArray.
4888  *
4889  * We assume the caller has verified that the slice coordinates are valid.
4890  */
4891 static void
4893  ArrayType *origArray,
4894  ArrayType *srcArray,
4895  int ndim,
4896  int *dim,
4897  int *lb,
4898  int *st,
4899  int *endp,
4900  int typlen,
4901  bool typbyval,
4902  char typalign)
4903 {
4904  char *destPtr = ARR_DATA_PTR(destArray);
4905  char *origPtr = ARR_DATA_PTR(origArray);
4906  char *srcPtr = ARR_DATA_PTR(srcArray);
4907  bits8 *destBitmap = ARR_NULLBITMAP(destArray);
4908  bits8 *origBitmap = ARR_NULLBITMAP(origArray);
4909  bits8 *srcBitmap = ARR_NULLBITMAP(srcArray);
4910  int orignitems = ArrayGetNItems(ARR_NDIM(origArray),
4911  ARR_DIMS(origArray));
4912  int dest_offset,
4913  orig_offset,
4914  src_offset,
4915  prod[MAXDIM],
4916  span[MAXDIM],
4917  dist[MAXDIM],
4918  indx[MAXDIM];
4919  int i,
4920  j,
4921  inc;
4922 
4923  dest_offset = ArrayGetOffset(ndim, dim, lb, st);
4924  /* copy items before the slice start */
4925  inc = array_copy(destPtr, dest_offset,
4926  origPtr, 0, origBitmap,
4927  typlen, typbyval, typalign);
4928  destPtr += inc;
4929  origPtr += inc;
4930  if (destBitmap)
4931  array_bitmap_copy(destBitmap, 0, origBitmap, 0, dest_offset);
4932  orig_offset = dest_offset;
4933  mda_get_prod(ndim, dim, prod);
4934  mda_get_range(ndim, span, st, endp);
4935  mda_get_offset_values(ndim, dist, prod, span);
4936  for (i = 0; i < ndim; i++)
4937  indx[i] = 0;
4938  src_offset = 0;
4939  j = ndim - 1;
4940  do
4941  {
4942  /* Copy/advance over elements between here and next part of slice */
4943  if (dist[j])
4944  {
4945  inc = array_copy(destPtr, dist[j],
4946  origPtr, orig_offset, origBitmap,
4947  typlen, typbyval, typalign);
4948  destPtr += inc;
4949  origPtr += inc;
4950  if (destBitmap)
4951  array_bitmap_copy(destBitmap, dest_offset,
4952  origBitmap, orig_offset,
4953  dist[j]);
4954  dest_offset += dist[j];
4955  orig_offset += dist[j];
4956  }
4957  /* Copy new element at this slice position */
4958  inc = array_copy(destPtr, 1,
4959  srcPtr, src_offset, srcBitmap,
4960  typlen, typbyval, typalign);
4961  if (destBitmap)
4962  array_bitmap_copy(destBitmap, dest_offset,
4963  srcBitmap, src_offset,
4964  1);
4965  destPtr += inc;
4966  srcPtr += inc;
4967  dest_offset++;
4968  src_offset++;
4969  /* Advance over old element at this slice position */
4970  origPtr = array_seek(origPtr, orig_offset, origBitmap, 1,
4971  typlen, typbyval, typalign);
4972  orig_offset++;
4973  } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
4974 
4975  /* don't miss any data at the end */
4976  array_copy(destPtr, orignitems - orig_offset,
4977  origPtr, orig_offset, origBitmap,
4978  typlen, typbyval, typalign);
4979  if (destBitmap)
4980  array_bitmap_copy(destBitmap, dest_offset,
4981  origBitmap, orig_offset,
4982  orignitems - orig_offset);
4983 }
4984 
4985 /*
4986  * initArrayResult - initialize an empty ArrayBuildState
4987  *
4988  * element_type is the array element type (must be a valid array element type)
4989  * rcontext is where to keep working state
4990  * subcontext is a flag determining whether to use a separate memory context
4991  *
4992  * Note: there are two common schemes for using accumArrayResult().
4993  * In the older scheme, you start with a NULL ArrayBuildState pointer, and
4994  * call accumArrayResult once per element. In this scheme you end up with
4995  * a NULL pointer if there were no elements, which you need to special-case.
4996  * In the newer scheme, call initArrayResult and then call accumArrayResult
4997  * once per element. In this scheme you always end with a non-NULL pointer
4998  * that you can pass to makeArrayResult; you get an empty array if there
4999  * were no elements. This is preferred if an empty array is what you want.
5000  *
5001  * It's possible to choose whether to create a separate memory context for the
5002  * array build state, or whether to allocate it directly within rcontext.
5003  *
5004  * When there are many concurrent small states (e.g. array_agg() using hash
5005  * aggregation of many small groups), using a separate memory context for each
5006  * one may result in severe memory bloat. In such cases, use the same memory
5007  * context to initialize all such array build states, and pass
5008  * subcontext=false.
5009  *
5010  * In cases when the array build states have different lifetimes, using a
5011  * single memory context is impractical. Instead, pass subcontext=true so that
5012  * the array build states can be freed individually.
5013  */
5015 initArrayResult(Oid element_type, MemoryContext rcontext, bool subcontext)
5016 {
5017  ArrayBuildState *astate;
5018  MemoryContext arr_context = rcontext;
5019 
5020  /* Make a temporary context to hold all the junk */
5021  if (subcontext)
5022  arr_context = AllocSetContextCreate(rcontext,
5023  "accumArrayResult",
5025 
5026  astate = (ArrayBuildState *)
5027  MemoryContextAlloc(arr_context, sizeof(ArrayBuildState));
5028  astate->mcontext = arr_context;
5029  astate->private_cxt = subcontext;
5030  astate->alen = (subcontext ? 64 : 8); /* arbitrary starting array size */
5031  astate->dvalues = (Datum *)
5032  MemoryContextAlloc(arr_context, astate->alen * sizeof(Datum));
5033  astate->dnulls = (bool *)
5034  MemoryContextAlloc(arr_context, astate->alen * sizeof(bool));
5035  astate->nelems = 0;
5036  astate->element_type = element_type;
5037  get_typlenbyvalalign(element_type,
5038  &astate->typlen,
5039  &astate->typbyval,
5040  &astate->typalign);
5041 
5042  return astate;
5043 }
5044 
5045 /*
5046  * accumArrayResult - accumulate one (more) Datum for an array result
5047  *
5048  * astate is working state (can be NULL on first call)
5049  * dvalue/disnull represent the new Datum to append to the array
5050  * element_type is the Datum's type (must be a valid array element type)
5051  * rcontext is where to keep working state
5052  */
5055  Datum dvalue, bool disnull,
5056  Oid element_type,
5057  MemoryContext rcontext)
5058 {
5059  MemoryContext oldcontext;
5060 
5061  if (astate == NULL)
5062  {
5063  /* First time through --- initialize */
5064  astate = initArrayResult(element_type, rcontext, true);
5065  }
5066  else
5067  {
5068  Assert(astate->element_type == element_type);
5069  }
5070 
5071  oldcontext = MemoryContextSwitchTo(astate->mcontext);
5072 
5073  /* enlarge dvalues[]/dnulls[] if needed */
5074  if (astate->nelems >= astate->alen)
5075  {
5076  astate->alen *= 2;
5077  astate->dvalues = (Datum *)
5078  repalloc(astate->dvalues, astate->alen * sizeof(Datum));
5079  astate->dnulls = (bool *)
5080  repalloc(astate->dnulls, astate->alen * sizeof(bool));
5081  }
5082 
5083  /*
5084  * Ensure pass-by-ref stuff is copied into mcontext; and detoast it too if
5085  * it's varlena. (You might think that detoasting is not needed here
5086  * because construct_md_array can detoast the array elements later.
5087  * However, we must not let construct_md_array modify the ArrayBuildState
5088  * because that would mean array_agg_finalfn damages its input, which is
5089  * verboten. Also, this way frequently saves one copying step.)
5090  */
5091  if (!disnull && !astate->typbyval)
5092  {
5093  if (astate->typlen == -1)
5094  dvalue = PointerGetDatum(PG_DETOAST_DATUM_COPY(dvalue));
5095  else
5096  dvalue = datumCopy(dvalue, astate->typbyval, astate->typlen);
5097  }
5098 
5099  astate->dvalues[astate->nelems] = dvalue;
5100  astate->dnulls[astate->nelems] = disnull;
5101  astate->nelems++;
5102 
5103  MemoryContextSwitchTo(oldcontext);
5104 
5105  return astate;
5106 }
5107 
5108 /*
5109  * makeArrayResult - produce 1-D final result of accumArrayResult
5110  *
5111  * Note: only releases astate if it was initialized within a separate memory
5112  * context (i.e. using subcontext=true when calling initArrayResult).
5113  *
5114  * astate is working state (must not be NULL)
5115  * rcontext is where to construct result
5116  */
5117 Datum
5119  MemoryContext rcontext)
5120 {
5121  int ndims;
5122  int dims[1];
5123  int lbs[1];
5124 
5125  /* If no elements were presented, we want to create an empty array */
5126  ndims = (astate->nelems > 0) ? 1 : 0;
5127  dims[0] = astate->nelems;
5128  lbs[0] = 1;
5129 
5130  return makeMdArrayResult(astate, ndims, dims, lbs, rcontext,
5131  astate->private_cxt);
5132 }
5133 
5134 /*
5135  * makeMdArrayResult - produce multi-D final result of accumArrayResult
5136  *
5137  * beware: no check that specified dimensions match the number of values
5138  * accumulated.
5139  *
5140  * Note: if the astate was not initialized within a separate memory context
5141  * (that is, initArrayResult was called with subcontext=false), then using
5142  * release=true is illegal. Instead, release astate along with the rest of its
5143  * context when appropriate.
5144  *
5145  * astate is working state (must not be NULL)
5146  * rcontext is where to construct result
5147  * release is true if okay to release working state
5148  */
5149 Datum
5151  int ndims,
5152  int *dims,
5153  int *lbs,
5154  MemoryContext rcontext,
5155  bool release)
5156 {
5157  ArrayType *result;
5158  MemoryContext oldcontext;
5159 
5160  /* Build the final array result in rcontext */
5161  oldcontext = MemoryContextSwitchTo(rcontext);
5162 
5163  result = construct_md_array(astate->dvalues,
5164  astate->dnulls,
5165  ndims,
5166  dims,
5167  lbs,
5168  astate->element_type,
5169  astate->typlen,
5170  astate->typbyval,
5171  astate->typalign);
5172 
5173  MemoryContextSwitchTo(oldcontext);
5174 
5175  /* Clean up all the junk */
5176  if (release)
5177  {
5178  Assert(astate->private_cxt);
5179  MemoryContextDelete(astate->mcontext);
5180  }
5181 
5182  return PointerGetDatum(result);
5183 }
5184 
5185 /*
5186  * The following three functions provide essentially the same API as
5187  * initArrayResult/accumArrayResult/makeArrayResult, but instead of accepting
5188  * inputs that are array elements, they accept inputs that are arrays and
5189  * produce an output array having N+1 dimensions. The inputs must all have
5190  * identical dimensionality as well as element type.
5191  */
5192 
5193 /*
5194  * initArrayResultArr - initialize an empty ArrayBuildStateArr
5195  *
5196  * array_type is the array type (must be a valid varlena array type)
5197  * element_type is the type of the array's elements (lookup if InvalidOid)
5198  * rcontext is where to keep working state
5199  * subcontext is a flag determining whether to use a separate memory context
5200  */
5202 initArrayResultArr(Oid array_type, Oid element_type, MemoryContext rcontext,
5203  bool subcontext)
5204 {
5205  ArrayBuildStateArr *astate;
5206  MemoryContext arr_context = rcontext; /* by default use the parent ctx */
5207 
5208  /* Lookup element type, unless element_type already provided */
5209  if (!OidIsValid(element_type))
5210  {
5211  element_type = get_element_type(array_type);
5212 
5213  if (!OidIsValid(element_type))
5214  ereport(ERROR,
5215  (errcode(ERRCODE_DATATYPE_MISMATCH),
5216  errmsg("data type %s is not an array type",
5217  format_type_be(array_type))));
5218  }
5219 
5220  /* Make a temporary context to hold all the junk */
5221  if (subcontext)
5222  arr_context = AllocSetContextCreate(rcontext,
5223  "accumArrayResultArr",
5225 
5226  /* Note we initialize all fields to zero */
5227  astate = (ArrayBuildStateArr *)
5228  MemoryContextAllocZero(arr_context, sizeof(ArrayBuildStateArr));
5229  astate->mcontext = arr_context;
5230  astate->private_cxt = subcontext;
5231 
5232  /* Save relevant datatype information */
5233  astate->array_type = array_type;
5234  astate->element_type = element_type;
5235 
5236  return astate;
5237 }
5238 
5239 /*
5240  * accumArrayResultArr - accumulate one (more) sub-array for an array result
5241  *
5242  * astate is working state (can be NULL on first call)
5243  * dvalue/disnull represent the new sub-array to append to the array
5244  * array_type is the array type (must be a valid varlena array type)
5245  * rcontext is where to keep working state
5246  */
5249  Datum dvalue, bool disnull,
5250  Oid array_type,
5251  MemoryContext rcontext)
5252 {
5253  ArrayType *arg;
5254  MemoryContext oldcontext;
5255  int *dims,
5256  *lbs,
5257  ndims,
5258  nitems,
5259  ndatabytes;
5260  char *data;
5261  int i;
5262 
5263  /*
5264  * We disallow accumulating null subarrays. Another plausible definition
5265  * is to ignore them, but callers that want that can just skip calling
5266  * this function.
5267  */
5268  if (disnull)
5269  ereport(ERROR,
5270  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5271  errmsg("cannot accumulate null arrays")));
5272 
5273  /* Detoast input array in caller's context */
5274  arg = DatumGetArrayTypeP(dvalue);
5275 
5276  if (astate == NULL)
5277  astate = initArrayResultArr(array_type, InvalidOid, rcontext, true);
5278  else
5279  Assert(astate->array_type == array_type);
5280 
5281  oldcontext = MemoryContextSwitchTo(astate->mcontext);
5282 
5283  /* Collect this input's dimensions */
5284  ndims = ARR_NDIM(arg);
5285  dims = ARR_DIMS(arg);
5286  lbs = ARR_LBOUND(arg);
5287  data = ARR_DATA_PTR(arg);
5288  nitems = ArrayGetNItems(ndims, dims);
5289  ndatabytes = ARR_SIZE(arg) - ARR_DATA_OFFSET(arg);
5290 
5291  if (astate->ndims == 0)
5292  {
5293  /* First input; check/save the dimensionality info */
5294 
5295  /* Should we allow empty inputs and just produce an empty output? */
5296  if (ndims == 0)
5297  ereport(ERROR,
5298  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5299  errmsg("cannot accumulate empty arrays")));
5300  if (ndims + 1 > MAXDIM)
5301  ereport(ERROR,
5302  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
5303  errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
5304  ndims + 1, MAXDIM)));
5305 
5306  /*
5307  * The output array will have n+1 dimensions, with the ones after the
5308  * first matching the input's dimensions.
5309  */
5310  astate->ndims = ndims + 1;
5311  astate->dims[0] = 0;
5312  memcpy(&astate->dims[1], dims, ndims * sizeof(int));
5313  astate->lbs[0] = 1;
5314  memcpy(&astate->lbs[1], lbs, ndims * sizeof(int));
5315 
5316  /* Allocate at least enough data space for this item */
5317  astate->abytes = pg_nextpower2_32(Max(1024, ndatabytes + 1));
5318  astate->data = (char *) palloc(astate->abytes);
5319  }
5320  else
5321  {
5322  /* Second or later input: must match first input's dimensionality */
5323  if (astate->ndims != ndims + 1)
5324  ereport(ERROR,
5325  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5326  errmsg("cannot accumulate arrays of different dimensionality")));
5327  for (i = 0; i < ndims; i++)
5328  {
5329  if (astate->dims[i + 1] != dims[i] || astate->lbs[i + 1] != lbs[i])
5330  ereport(ERROR,
5331  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5332  errmsg("cannot accumulate arrays of different dimensionality")));
5333  }
5334 
5335  /* Enlarge data space if needed */
5336  if (astate->nbytes + ndatabytes > astate->abytes)
5337  {
5338  astate->abytes = Max(astate->abytes * 2,
5339  astate->nbytes + ndatabytes);
5340  astate->data = (char *) repalloc(astate->data, astate->abytes);
5341  }
5342  }
5343 
5344  /*
5345  * Copy the data portion of the sub-array. Note we assume that the
5346  * advertised data length of the sub-array is properly aligned. We do not
5347  * have to worry about detoasting elements since whatever's in the
5348  * sub-array should be OK already.
5349  */
5350  memcpy(astate->data + astate->nbytes, data, ndatabytes);
5351  astate->nbytes += ndatabytes;
5352 
5353  /* Deal with null bitmap if needed */
5354  if (astate->nullbitmap || ARR_HASNULL(arg))
5355  {
5356  int newnitems = astate->nitems + nitems;
5357 
5358  if (astate->nullbitmap == NULL)
5359  {
5360  /*
5361  * First input with nulls; we must retrospectively handle any
5362  * previous inputs by marking all their items non-null.
5363  */
5364  astate->aitems = pg_nextpower2_32(Max(256, newnitems + 1));
5365  astate->nullbitmap = (bits8 *) palloc((astate->aitems + 7) / 8);
5366  array_bitmap_copy(astate->nullbitmap, 0,
5367  NULL, 0,
5368  astate->nitems);
5369  }
5370  else if (newnitems > astate->aitems)
5371  {
5372  astate->aitems = Max(astate->aitems * 2, newnitems);
5373  astate->nullbitmap = (bits8 *)
5374  repalloc(astate->nullbitmap, (astate->aitems + 7) / 8);
5375  }
5376  array_bitmap_copy(astate->nullbitmap, astate->nitems,
5377  ARR_NULLBITMAP(arg), 0,
5378  nitems);
5379  }
5380 
5381  astate->nitems += nitems;
5382  astate->dims[0] += 1;
5383 
5384  MemoryContextSwitchTo(oldcontext);
5385 
5386  /* Release detoasted copy if any */
5387  if ((Pointer) arg != DatumGetPointer(dvalue))
5388  pfree(arg);
5389 
5390  return astate;
5391 }
5392 
5393 /*
5394  * makeArrayResultArr - produce N+1-D final result of accumArrayResultArr
5395  *
5396  * astate is working state (must not be NULL)
5397  * rcontext is where to construct result
5398  * release is true if okay to release working state
5399  */
5400 Datum
5402  MemoryContext rcontext,
5403  bool release)
5404 {
5405  ArrayType *result;
5406  MemoryContext oldcontext;
5407 
5408  /* Build the final array result in rcontext */
5409  oldcontext = MemoryContextSwitchTo(rcontext);
5410 
5411  if (astate->ndims == 0)
5412  {
5413  /* No inputs, return empty array */
5414  result = construct_empty_array(astate->element_type);
5415  }
5416  else
5417  {
5418  int dataoffset,
5419  nbytes;
5420 
5421  /* Compute required space */
5422  nbytes = astate->nbytes;
5423  if (astate->nullbitmap != NULL)
5424  {
5425  dataoffset = ARR_OVERHEAD_WITHNULLS(astate->ndims, astate->nitems);
5426  nbytes += dataoffset;
5427  }
5428  else
5429  {
5430  dataoffset = 0;
5431  nbytes += ARR_OVERHEAD_NONULLS(astate->ndims);
5432  }
5433 
5434  result = (ArrayType *) palloc0(nbytes);
5435  SET_VARSIZE(result, nbytes);
5436  result->ndim = astate->ndims;
5437  result->dataoffset = dataoffset;
5438  result->elemtype = astate->element_type;
5439 
5440  memcpy(ARR_DIMS(result), astate->dims, astate->ndims * sizeof(int));
5441  memcpy(ARR_LBOUND(result), astate->lbs, astate->ndims * sizeof(int));
5442  memcpy(ARR_DATA_PTR(result), astate->data, astate->nbytes);
5443 
5444  if (astate->nullbitmap != NULL)
5445  array_bitmap_copy(ARR_NULLBITMAP(result), 0,
5446  astate->nullbitmap, 0,
5447  astate->nitems);
5448  }
5449 
5450  MemoryContextSwitchTo(oldcontext);
5451 
5452  /* Clean up all the junk */
5453  if (release)
5454  {
5455  Assert(astate->private_cxt);
5456  MemoryContextDelete(astate->mcontext);
5457  }
5458 
5459  return PointerGetDatum(result);
5460 }
5461 
5462 /*
5463  * The following three functions provide essentially the same API as
5464  * initArrayResult/accumArrayResult/makeArrayResult, but can accept either
5465  * scalar or array inputs, invoking the appropriate set of functions above.
5466  */
5467 
5468 /*
5469  * initArrayResultAny - initialize an empty ArrayBuildStateAny
5470  *
5471  * input_type is the input datatype (either element or array type)
5472  * rcontext is where to keep working state
5473  * subcontext is a flag determining whether to use a separate memory context
5474  */
5476 initArrayResultAny(Oid input_type, MemoryContext rcontext, bool subcontext)
5477 {
5478  ArrayBuildStateAny *astate;
5479  Oid element_type = get_element_type(input_type);
5480 
5481  if (OidIsValid(element_type))
5482  {
5483  /* Array case */
5484  ArrayBuildStateArr *arraystate;
5485 
5486  arraystate = initArrayResultArr(input_type, InvalidOid, rcontext, subcontext);
5487  astate = (ArrayBuildStateAny *)
5488  MemoryContextAlloc(arraystate->mcontext,
5489  sizeof(ArrayBuildStateAny));
5490  astate->scalarstate = NULL;
5491  astate->arraystate = arraystate;
5492  }
5493  else
5494  {
5495  /* Scalar case */
5496  ArrayBuildState *scalarstate;
5497 
5498  /* Let's just check that we have a type that can be put into arrays */
5499  Assert(OidIsValid(get_array_type(input_type)));
5500 
5501  scalarstate = initArrayResult(input_type, rcontext, subcontext);
5502  astate = (ArrayBuildStateAny *)
5503  MemoryContextAlloc(scalarstate->mcontext,
5504  sizeof(ArrayBuildStateAny));
5505  astate->scalarstate = scalarstate;
5506  astate->arraystate = NULL;
5507  }
5508 
5509  return astate;
5510 }
5511 
5512 /*
5513  * accumArrayResultAny - accumulate one (more) input for an array result
5514  *
5515  * astate is working state (can be NULL on first call)
5516  * dvalue/disnull represent the new input to append to the array
5517  * input_type is the input datatype (either element or array type)
5518  * rcontext is where to keep working state
5519  */
5522  Datum dvalue, bool disnull,
5523  Oid input_type,
5524  MemoryContext rcontext)
5525 {
5526  if (astate == NULL)
5527  astate = initArrayResultAny(input_type, rcontext, true);
5528 
5529  if (astate->scalarstate)
5530  (void) accumArrayResult(astate->scalarstate,
5531  dvalue, disnull,
5532  input_type, rcontext);
5533  else
5534  (void) accumArrayResultArr(astate->arraystate,
5535  dvalue, disnull,
5536  input_type, rcontext);
5537 
5538  return astate;
5539 }
5540 
5541 /*
5542  * makeArrayResultAny - produce final result of accumArrayResultAny
5543  *
5544  * astate is working state (must not be NULL)
5545  * rcontext is where to construct result
5546  * release is true if okay to release working state
5547  */
5548 Datum
5550  MemoryContext rcontext, bool release)
5551 {
5552  Datum result;
5553 
5554  if (astate->scalarstate)
5555  {
5556  /* Must use makeMdArrayResult to support "release" parameter */
5557  int ndims;
5558  int dims[1];
5559  int lbs[1];
5560 
5561  /* If no elements were presented, we want to create an empty array */
5562  ndims = (astate->scalarstate->nelems > 0) ? 1 : 0;
5563  dims[0] = astate->scalarstate->nelems;
5564  lbs[0] = 1;
5565 
5566  result = makeMdArrayResult(astate->scalarstate, ndims, dims, lbs,
5567  rcontext, release);
5568  }
5569  else
5570  {
5571  result = makeArrayResultArr(astate->arraystate,
5572  rcontext, release);
5573  }
5574  return result;
5575 }
5576 
5577 
5578 Datum
5580 {
5581  if (array_cmp(fcinfo) > 0)
5583  else
5585 }
5586 
5587 Datum
5589 {
5590  if (array_cmp(fcinfo) < 0)
5592  else
5594 }
5595 
5596 
5598 {
5601  bool reverse;
5603 
5604 /*
5605  * generate_subscripts(array anyarray, dim int [, reverse bool])
5606  * Returns all subscripts of the array for any dimension
5607  */
5608 Datum
5610 {
5611  FuncCallContext *funcctx;
5612  MemoryContext oldcontext;
5614 
5615  /* stuff done only on the first call of the function */
5616  if (SRF_IS_FIRSTCALL())
5617  {
5619  int reqdim = PG_GETARG_INT32(1);
5620  int *lb,
5621  *dimv;
5622 
5623  /* create a function context for cross-call persistence */
5624  funcctx = SRF_FIRSTCALL_INIT();
5625 
5626  /* Sanity check: does it look like an array at all? */
5627  if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
5628  SRF_RETURN_DONE(funcctx);
5629 
5630  /* Sanity check: was the requested dim valid */
5631  if (reqdim <= 0 || reqdim > AARR_NDIM(v))
5632  SRF_RETURN_DONE(funcctx);
5633 
5634  /*
5635  * switch to memory context appropriate for multiple function calls
5636  */
5637  oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
5639 
5640  lb = AARR_LBOUND(v);
5641  dimv = AARR_DIMS(v);
5642 
5643  fctx->lower = lb[reqdim - 1];
5644  fctx->upper = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
5645  fctx->reverse = (PG_NARGS() < 3) ? false : PG_GETARG_BOOL(2);
5646 
5647  funcctx->user_fctx = fctx;
5648 
5649  MemoryContextSwitchTo(oldcontext);
5650  }
5651 
5652  funcctx = SRF_PERCALL_SETUP();
5653 
5654  fctx = funcctx->user_fctx;
5655 
5656  if (fctx->lower <= fctx->upper)
5657  {
5658  if (!fctx->reverse)
5659  SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->lower++));
5660  else
5661  SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->upper--));
5662  }
5663  else
5664  /* done when there are no more elements left */
5665  SRF_RETURN_DONE(funcctx);
5666 }
5667 
5668 /*
5669  * generate_subscripts_nodir
5670  * Implements the 2-argument version of generate_subscripts
5671  */
5672 Datum
5674 {
5675  /* just call the other one -- it can handle both cases */
5676  return generate_subscripts(fcinfo);
5677 }
5678 
5679 /*
5680  * array_fill_with_lower_bounds
5681  * Create and fill array with defined lower bounds.
5682  */
5683 Datum
5685 {
5686  ArrayType *dims;
5687  ArrayType *lbs;
5688  ArrayType *result;
5689  Oid elmtype;
5690  Datum value;
5691  bool isnull;
5692 
5693  if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
5694  ereport(ERROR,
5695  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5696  errmsg("dimension array or low bound array cannot be null")));
5697 
5698  dims = PG_GETARG_ARRAYTYPE_P(1);
5699  lbs = PG_GETARG_ARRAYTYPE_P(2);
5700 
5701  if (!PG_ARGISNULL(0))
5702  {
5703  value = PG_GETARG_DATUM(0);
5704  isnull = false;
5705  }
5706  else
5707  {
5708  value = 0;
5709  isnull = true;
5710  }
5711 
5712  elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
5713  if (!OidIsValid(elmtype))
5714  elog(ERROR, "could not determine data type of input");
5715 
5716  result = array_fill_internal(dims, lbs, value, isnull, elmtype, fcinfo);
5717  PG_RETURN_ARRAYTYPE_P(result);
5718 }
5719 
5720 /*
5721  * array_fill
5722  * Create and fill array with default lower bounds.
5723  */
5724 Datum
5726 {
5727  ArrayType *dims;
5728  ArrayType *result;
5729  Oid elmtype;
5730  Datum value;
5731  bool isnull;
5732 
5733  if (PG_ARGISNULL(1))
5734  ereport(ERROR,
5735  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5736  errmsg("dimension array or low bound array cannot be null")));
5737 
5738  dims = PG_GETARG_ARRAYTYPE_P(1);
5739 
5740  if (!PG_ARGISNULL(0))
5741  {
5742  value = PG_GETARG_DATUM(0);
5743  isnull = false;
5744  }
5745  else
5746  {
5747  value = 0;
5748  isnull = true;
5749  }
5750 
5751  elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
5752  if (!OidIsValid(elmtype))
5753  elog(ERROR, "could not determine data type of input");
5754 
5755  result = array_fill_internal(dims, NULL, value, isnull, elmtype, fcinfo);
5756  PG_RETURN_ARRAYTYPE_P(result);
5757 }
5758 
5759 static ArrayType *
5760 create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
5761  Oid elmtype, int dataoffset)
5762 {
5763  ArrayType *result;
5764 
5765  result = (ArrayType *) palloc0(nbytes);
5766  SET_VARSIZE(result, nbytes);
5767  result->ndim = ndims;
5768  result->dataoffset = dataoffset;
5769  result->elemtype = elmtype;
5770  memcpy(ARR_DIMS(result), dimv, ndims * sizeof(int));
5771  memcpy(ARR_LBOUND(result), lbsv, ndims * sizeof(int));
5772 
5773  return result;
5774 }
5775 
5776 static ArrayType *
5778  Datum value, bool isnull, Oid elmtype,
5779  FunctionCallInfo fcinfo)
5780 {
5781  ArrayType *result;
5782  int *dimv;
5783  int *lbsv;
5784  int ndims;
5785  int nitems;
5786  int deflbs[MAXDIM];
5787  int16 elmlen;
5788  bool elmbyval;
5789  char elmalign;
5790  ArrayMetaState *my_extra;
5791 
5792  /*
5793  * Params checks
5794  */
5795  if (ARR_NDIM(dims) > 1)
5796  ereport(ERROR,
5797  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5798  errmsg("wrong number of array subscripts"),
5799  errdetail("Dimension array must be one dimensional.")));
5800 
5801  if (array_contains_nulls(dims))
5802  ereport(ERROR,
5803  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5804  errmsg("dimension values cannot be null")));
5805 
5806  dimv = (int *) ARR_DATA_PTR(dims);
5807  ndims = (ARR_NDIM(dims) > 0) ? ARR_DIMS(dims)[0] : 0;
5808 
5809  if (ndims < 0) /* we do allow zero-dimension arrays */
5810  ereport(ERROR,
5811  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
5812  errmsg("invalid number of dimensions: %d", ndims)));
5813  if (ndims > MAXDIM)
5814  ereport(ERROR,
5815  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
5816  errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
5817  ndims, MAXDIM)));
5818 
5819  if (lbs != NULL)
5820  {
5821  if (ARR_NDIM(lbs) > 1)
5822  ereport(ERROR,
5823  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5824  errmsg("wrong number of array subscripts"),
5825  errdetail("Dimension array must be one dimensional.")));
5826 
5827  if (array_contains_nulls(lbs))
5828  ereport(ERROR,
5829  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5830  errmsg("dimension values cannot be null")));
5831 
5832  if (ndims != ((ARR_NDIM(lbs) > 0) ? ARR_DIMS(lbs)[0] : 0))
5833  ereport(ERROR,
5834  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5835  errmsg("wrong number of array subscripts"),
5836  errdetail("Low bound array has different size than dimensions array.")));
5837 
5838  lbsv = (int *) ARR_DATA_PTR(lbs);
5839  }
5840  else
5841  {
5842  int i;
5843 
5844  for (i = 0; i < MAXDIM; i++)
5845  deflbs[i] = 1;
5846 
5847  lbsv = deflbs;
5848  }
5849 
5850  nitems = ArrayGetNItems(ndims, dimv);
5851 
5852  /* fast track for empty array */
5853  if (nitems <= 0)
5854  return construct_empty_array(elmtype);
5855 
5856  /*
5857  * We arrange to look up info about element type only once per series of
5858  * calls, assuming the element type doesn't change underneath us.
5859  */
5860  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
5861  if (my_extra == NULL)
5862  {
5863  fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
5864  sizeof(ArrayMetaState));
5865  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
5866  my_extra->element_type = InvalidOid;
5867  }
5868 
5869  if (my_extra->element_type != elmtype)
5870  {
5871  /* Get info about element type */
5872  get_typlenbyvalalign(elmtype,
5873  &my_extra->typlen,
5874  &my_extra->typbyval,
5875  &my_extra->typalign);
5876  my_extra->element_type = elmtype;
5877  }
5878 
5879  elmlen = my_extra->typlen;
5880  elmbyval = my_extra->typbyval;
5881  elmalign = my_extra->typalign;
5882 
5883  /* compute required space */
5884  if (!isnull)
5885  {
5886  int i;
5887  char *p;
5888  int nbytes;
5889  int totbytes;
5890 
5891  /* make sure data is not toasted */
5892  if (elmlen == -1)
5893  value = PointerGetDatum(PG_DETOAST_DATUM(value));
5894 
5895  nbytes = att_addlength_datum(0, elmlen, value);
5896  nbytes = att_align_nominal(nbytes, elmalign);
5897  Assert(nbytes > 0);
5898 
5899  totbytes = nbytes * nitems;
5900 
5901  /* check for overflow of multiplication or total request */
5902  if (totbytes / nbytes != nitems ||
5903  !AllocSizeIsValid(totbytes))
5904  ereport(ERROR,
5905  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
5906  errmsg("array size exceeds the maximum allowed (%d)",
5907  (int) MaxAllocSize)));
5908 
5909  /*
5910  * This addition can't overflow, but it might cause us to go past
5911  * MaxAllocSize. We leave it to palloc to complain in that case.
5912  */
5913  totbytes += ARR_OVERHEAD_NONULLS(ndims);
5914 
5915  result = create_array_envelope(ndims, dimv, lbsv, totbytes,
5916  elmtype, 0);
5917 
5918  p = ARR_DATA_PTR(result);
5919  for (i = 0; i < nitems; i++)
5920  p += ArrayCastAndSet(value, elmlen, elmbyval, elmalign, p);
5921  }
5922  else
5923  {
5924  int nbytes;
5925  int dataoffset;
5926 
5927  dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
5928  nbytes = dataoffset;
5929 
5930  result = create_array_envelope(ndims, dimv, lbsv, nbytes,
5931  elmtype, dataoffset);
5932 
5933  /* create_array_envelope already zeroed the bitmap, so we're done */
5934  }
5935 
5936  return result;
5937 }
5938 
5939 
5940 /*
5941  * UNNEST
5942  */
5943 Datum
5945 {
5946  typedef struct
5947  {
5948  array_iter iter;
5949  int nextelem;
5950  int numelems;
5951  int16 elmlen;
5952  bool elmbyval;
5953  char elmalign;
5954  } array_unnest_fctx;
5955 
5956  FuncCallContext *funcctx;
5957  array_unnest_fctx *fctx;
5958  MemoryContext oldcontext;
5959 
5960  /* stuff done only on the first call of the function */
5961  if (SRF_IS_FIRSTCALL())
5962  {
5963  AnyArrayType *arr;
5964 
5965  /* create a function context for cross-call persistence */
5966  funcctx = SRF_FIRSTCALL_INIT();
5967 
5968  /*
5969  * switch to memory context appropriate for multiple function calls
5970  */
5971  oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
5972 
5973  /*
5974  * Get the array value and detoast if needed. We can't do this
5975  * earlier because if we have to detoast, we want the detoasted copy
5976  * to be in multi_call_memory_ctx, so it will go away when we're done
5977  * and not before. (If no detoast happens, we assume the originally
5978  * passed array will stick around till then.)
5979  */
5980  arr = PG_GETARG_ANY_ARRAY_P(0);
5981 
5982  /* allocate memory for user context */
5983  fctx = (array_unnest_fctx *) palloc(sizeof(array_unnest_fctx));
5984 
5985  /* initialize state */
5986  array_iter_setup(&fctx->iter, arr);
5987  fctx->nextelem = 0;
5988  fctx->numelems = ArrayGetNItems(AARR_NDIM(arr), AARR_DIMS(arr));
5989 
5990  if (VARATT_IS_EXPANDED_HEADER(arr))
5991  {
5992  /* we can just grab the type data from expanded array */
5993  fctx->elmlen = arr->xpn.typlen;
5994  fctx->elmbyval = arr->xpn.typbyval;
5995  fctx->elmalign = arr->xpn.typalign;
5996  }
5997  else
5999  &fctx->elmlen,
6000  &fctx->elmbyval,
6001  &fctx->elmalign);
6002 
6003  funcctx->user_fctx = fctx;
6004  MemoryContextSwitchTo(oldcontext);
6005  }
6006 
6007  /* stuff done on every call of the function */
6008  funcctx = SRF_PERCALL_SETUP();
6009  fctx = funcctx->user_fctx;
6010 
6011  if (fctx->nextelem < fctx->numelems)
6012  {
6013  int offset = fctx->nextelem++;
6014  Datum elem;
6015 
6016  elem = array_iter_next(&fctx->iter, &fcinfo->isnull, offset,
6017  fctx->elmlen, fctx->elmbyval, fctx->elmalign);
6018 
6019  SRF_RETURN_NEXT(funcctx, elem);
6020  }
6021  else
6022  {
6023  /* do when there is no more left */
6024  SRF_RETURN_DONE(funcctx);
6025  }
6026 }
6027 
6028 /*
6029  * Planner support function for array_unnest(anyarray)
6030  */
6031 Datum
6033 {
6034  Node *rawreq = (Node *) PG_GETARG_POINTER(0);
6035  Node *ret = NULL;
6036 
6037  if (IsA(rawreq, SupportRequestRows))
6038  {
6039  /* Try to estimate the number of rows returned */
6040  SupportRequestRows *req = (SupportRequestRows *) rawreq;
6041 
6042  if (is_funcclause(req->node)) /* be paranoid */
6043  {
6044  List *args = ((FuncExpr *) req->node)->args;
6045  Node *arg1;
6046 
6047  /* We can use estimated argument values here */
6048  arg1 = estimate_expression_value(req->root, linitial(args));
6049 
6050  req->rows = estimate_array_length(arg1);
6051  ret = (Node *) req;
6052  }
6053  }
6054 
6055  PG_RETURN_POINTER(ret);
6056 }
6057 
6058 
6059 /*
6060  * array_replace/array_remove support
6061  *
6062  * Find all array entries matching (not distinct from) search/search_isnull,
6063  * and delete them if remove is true, else replace them with
6064  * replace/replace_isnull. Comparisons are done using the specified
6065  * collation. fcinfo is passed only for caching purposes.
6066  */
6067 static ArrayType *
6069  Datum search, bool search_isnull,
6070  Datum replace, bool replace_isnull,
6071  bool remove, Oid collation,
6072  FunctionCallInfo fcinfo)
6073 {
6074  LOCAL_FCINFO(locfcinfo, 2);
6075  ArrayType *result;
6076  Oid element_type;
6077  Datum *values;
6078  bool *nulls;
6079  int *dim;
6080  int ndim;
6081  int nitems,
6082  nresult;
6083  int i;
6084  int32 nbytes = 0;
6085  int32 dataoffset;
6086  bool hasnulls;
6087  int typlen;
6088  bool typbyval;
6089  char typalign;
6090  char *arraydataptr;
6091  bits8 *bitmap;
6092  int bitmask;
6093  bool changed = false;
6094  TypeCacheEntry *typentry;
6095 
6096  element_type = ARR_ELEMTYPE(array);
6097  ndim = ARR_NDIM(array);
6098  dim = ARR_DIMS(array);
6099  nitems = ArrayGetNItems(ndim, dim);
6100 
6101  /* Return input array unmodified if it is empty */
6102  if (nitems <= 0)
6103  return array;
6104 
6105  /*
6106  * We can't remove elements from multi-dimensional arrays, since the
6107  * result might not be rectangular.
6108  */
6109  if (remove && ndim > 1)
6110  ereport(ERROR,
6111  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6112  errmsg("removing elements from multidimensional arrays is not supported")));
6113 
6114  /*
6115  * We arrange to look up the equality function only once per series of
6116  * calls, assuming the element type doesn't change underneath us.
6117  */
6118  typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
6119  if (typentry == NULL ||
6120  typentry->type_id != element_type)
6121  {
6122  typentry = lookup_type_cache(element_type,
6124  if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
6125  ereport(ERROR,
6126  (errcode(ERRCODE_UNDEFINED_FUNCTION),
6127