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