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
4967  * size */
4968  astate->dvalues = (Datum *)
4969  MemoryContextAlloc(arr_context, astate->alen * sizeof(Datum));
4970  astate->dnulls = (bool *)
4971  MemoryContextAlloc(arr_context, astate->alen * sizeof(bool));
4972  astate->nelems = 0;
4973  astate->element_type = element_type;
4974  get_typlenbyvalalign(element_type,
4975  &astate->typlen,
4976  &astate->typbyval,
4977  &astate->typalign);
4978 
4979  return astate;
4980 }
4981 
4982 /*
4983  * accumArrayResult - accumulate one (more) Datum for an array result
4984  *
4985  * astate is working state (can be NULL on first call)
4986  * dvalue/disnull represent the new Datum to append to the array
4987  * element_type is the Datum's type (must be a valid array element type)
4988  * rcontext is where to keep working state
4989  */
4992  Datum dvalue, bool disnull,
4993  Oid element_type,
4994  MemoryContext rcontext)
4995 {
4996  MemoryContext oldcontext;
4997 
4998  if (astate == NULL)
4999  {
5000  /* First time through --- initialize */
5001  astate = initArrayResult(element_type, rcontext, true);
5002  }
5003  else
5004  {
5005  Assert(astate->element_type == element_type);
5006  }
5007 
5008  oldcontext = MemoryContextSwitchTo(astate->mcontext);
5009 
5010  /* enlarge dvalues[]/dnulls[] if needed */
5011  if (astate->nelems >= astate->alen)
5012  {
5013  astate->alen *= 2;
5014  astate->dvalues = (Datum *)
5015  repalloc(astate->dvalues, astate->alen * sizeof(Datum));
5016  astate->dnulls = (bool *)
5017  repalloc(astate->dnulls, astate->alen * sizeof(bool));
5018  }
5019 
5020  /*
5021  * Ensure pass-by-ref stuff is copied into mcontext; and detoast it too if
5022  * it's varlena. (You might think that detoasting is not needed here
5023  * because construct_md_array can detoast the array elements later.
5024  * However, we must not let construct_md_array modify the ArrayBuildState
5025  * because that would mean array_agg_finalfn damages its input, which is
5026  * verboten. Also, this way frequently saves one copying step.)
5027  */
5028  if (!disnull && !astate->typbyval)
5029  {
5030  if (astate->typlen == -1)
5031  dvalue = PointerGetDatum(PG_DETOAST_DATUM_COPY(dvalue));
5032  else
5033  dvalue = datumCopy(dvalue, astate->typbyval, astate->typlen);
5034  }
5035 
5036  astate->dvalues[astate->nelems] = dvalue;
5037  astate->dnulls[astate->nelems] = disnull;
5038  astate->nelems++;
5039 
5040  MemoryContextSwitchTo(oldcontext);
5041 
5042  return astate;
5043 }
5044 
5045 /*
5046  * makeArrayResult - produce 1-D final result of accumArrayResult
5047  *
5048  * Note: only releases astate if it was initialized within a separate memory
5049  * context (i.e. using subcontext=true when calling initArrayResult).
5050  *
5051  * astate is working state (must not be NULL)
5052  * rcontext is where to construct result
5053  */
5054 Datum
5056  MemoryContext rcontext)
5057 {
5058  int ndims;
5059  int dims[1];
5060  int lbs[1];
5061 
5062  /* If no elements were presented, we want to create an empty array */
5063  ndims = (astate->nelems > 0) ? 1 : 0;
5064  dims[0] = astate->nelems;
5065  lbs[0] = 1;
5066 
5067  return makeMdArrayResult(astate, ndims, dims, lbs, rcontext,
5068  astate->private_cxt);
5069 }
5070 
5071 /*
5072  * makeMdArrayResult - produce multi-D final result of accumArrayResult
5073  *
5074  * beware: no check that specified dimensions match the number of values
5075  * accumulated.
5076  *
5077  * Note: if the astate was not initialized within a separate memory context
5078  * (that is, initArrayResult was called with subcontext=false), then using
5079  * release=true is illegal. Instead, release astate along with the rest of its
5080  * context when appropriate.
5081  *
5082  * astate is working state (must not be NULL)
5083  * rcontext is where to construct result
5084  * release is true if okay to release working state
5085  */
5086 Datum
5088  int ndims,
5089  int *dims,
5090  int *lbs,
5091  MemoryContext rcontext,
5092  bool release)
5093 {
5094  ArrayType *result;
5095  MemoryContext oldcontext;
5096 
5097  /* Build the final array result in rcontext */
5098  oldcontext = MemoryContextSwitchTo(rcontext);
5099 
5100  result = construct_md_array(astate->dvalues,
5101  astate->dnulls,
5102  ndims,
5103  dims,
5104  lbs,
5105  astate->element_type,
5106  astate->typlen,
5107  astate->typbyval,
5108  astate->typalign);
5109 
5110  MemoryContextSwitchTo(oldcontext);
5111 
5112  /* Clean up all the junk */
5113  if (release)
5114  {
5115  Assert(astate->private_cxt);
5116  MemoryContextDelete(astate->mcontext);
5117  }
5118 
5119  return PointerGetDatum(result);
5120 }
5121 
5122 /*
5123  * The following three functions provide essentially the same API as
5124  * initArrayResult/accumArrayResult/makeArrayResult, but instead of accepting
5125  * inputs that are array elements, they accept inputs that are arrays and
5126  * produce an output array having N+1 dimensions. The inputs must all have
5127  * identical dimensionality as well as element type.
5128  */
5129 
5130 /*
5131  * initArrayResultArr - initialize an empty ArrayBuildStateArr
5132  *
5133  * array_type is the array type (must be a valid varlena array type)
5134  * element_type is the type of the array's elements (lookup if InvalidOid)
5135  * rcontext is where to keep working state
5136  * subcontext is a flag determining whether to use a separate memory context
5137  */
5139 initArrayResultArr(Oid array_type, Oid element_type, MemoryContext rcontext,
5140  bool subcontext)
5141 {
5142  ArrayBuildStateArr *astate;
5143  MemoryContext arr_context = rcontext; /* by default use the parent
5144  * ctx */
5145 
5146  /* Lookup element type, unless element_type already provided */
5147  if (!OidIsValid(element_type))
5148  {
5149  element_type = get_element_type(array_type);
5150 
5151  if (!OidIsValid(element_type))
5152  ereport(ERROR,
5153  (errcode(ERRCODE_DATATYPE_MISMATCH),
5154  errmsg("data type %s is not an array type",
5155  format_type_be(array_type))));
5156  }
5157 
5158  /* Make a temporary context to hold all the junk */
5159  if (subcontext)
5160  arr_context = AllocSetContextCreate(rcontext,
5161  "accumArrayResultArr",
5163 
5164  /* Note we initialize all fields to zero */
5165  astate = (ArrayBuildStateArr *)
5166  MemoryContextAllocZero(arr_context, sizeof(ArrayBuildStateArr));
5167  astate->mcontext = arr_context;
5168  astate->private_cxt = subcontext;
5169 
5170  /* Save relevant datatype information */
5171  astate->array_type = array_type;
5172  astate->element_type = element_type;
5173 
5174  return astate;
5175 }
5176 
5177 /*
5178  * accumArrayResultArr - accumulate one (more) sub-array for an array result
5179  *
5180  * astate is working state (can be NULL on first call)
5181  * dvalue/disnull represent the new sub-array to append to the array
5182  * array_type is the array type (must be a valid varlena array type)
5183  * rcontext is where to keep working state
5184  */
5187  Datum dvalue, bool disnull,
5188  Oid array_type,
5189  MemoryContext rcontext)
5190 {
5191  ArrayType *arg;
5192  MemoryContext oldcontext;
5193  int *dims,
5194  *lbs,
5195  ndims,
5196  nitems,
5197  ndatabytes;
5198  char *data;
5199  int i;
5200 
5201  /*
5202  * We disallow accumulating null subarrays. Another plausible definition
5203  * is to ignore them, but callers that want that can just skip calling
5204  * this function.
5205  */
5206  if (disnull)
5207  ereport(ERROR,
5208  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5209  errmsg("cannot accumulate null arrays")));
5210 
5211  /* Detoast input array in caller's context */
5212  arg = DatumGetArrayTypeP(dvalue);
5213 
5214  if (astate == NULL)
5215  astate = initArrayResultArr(array_type, InvalidOid, rcontext, true);
5216  else
5217  Assert(astate->array_type == array_type);
5218 
5219  oldcontext = MemoryContextSwitchTo(astate->mcontext);
5220 
5221  /* Collect this input's dimensions */
5222  ndims = ARR_NDIM(arg);
5223  dims = ARR_DIMS(arg);
5224  lbs = ARR_LBOUND(arg);
5225  data = ARR_DATA_PTR(arg);
5226  nitems = ArrayGetNItems(ndims, dims);
5227  ndatabytes = ARR_SIZE(arg) - ARR_DATA_OFFSET(arg);
5228 
5229  if (astate->ndims == 0)
5230  {
5231  /* First input; check/save the dimensionality info */
5232 
5233  /* Should we allow empty inputs and just produce an empty output? */
5234  if (ndims == 0)
5235  ereport(ERROR,
5236  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5237  errmsg("cannot accumulate empty arrays")));
5238  if (ndims + 1 > MAXDIM)
5239  ereport(ERROR,
5240  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
5241  errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
5242  ndims + 1, MAXDIM)));
5243 
5244  /*
5245  * The output array will have n+1 dimensions, with the ones after the
5246  * first matching the input's dimensions.
5247  */
5248  astate->ndims = ndims + 1;
5249  astate->dims[0] = 0;
5250  memcpy(&astate->dims[1], dims, ndims * sizeof(int));
5251  astate->lbs[0] = 1;
5252  memcpy(&astate->lbs[1], lbs, ndims * sizeof(int));
5253 
5254  /* Allocate at least enough data space for this item */
5255  astate->abytes = 1024;
5256  while (astate->abytes <= ndatabytes)
5257  astate->abytes *= 2;
5258  astate->data = (char *) palloc(astate->abytes);
5259  }
5260  else
5261  {
5262  /* Second or later input: must match first input's dimensionality */
5263  if (astate->ndims != ndims + 1)
5264  ereport(ERROR,
5265  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5266  errmsg("cannot accumulate arrays of different dimensionality")));
5267  for (i = 0; i < ndims; i++)
5268  {
5269  if (astate->dims[i + 1] != dims[i] || astate->lbs[i + 1] != lbs[i])
5270  ereport(ERROR,
5271  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5272  errmsg("cannot accumulate arrays of different dimensionality")));
5273  }
5274 
5275  /* Enlarge data space if needed */
5276  if (astate->nbytes + ndatabytes > astate->abytes)
5277  {
5278  astate->abytes = Max(astate->abytes * 2,
5279  astate->nbytes + ndatabytes);
5280  astate->data = (char *) repalloc(astate->data, astate->abytes);
5281  }
5282  }
5283 
5284  /*
5285  * Copy the data portion of the sub-array. Note we assume that the
5286  * advertised data length of the sub-array is properly aligned. We do not
5287  * have to worry about detoasting elements since whatever's in the
5288  * sub-array should be OK already.
5289  */
5290  memcpy(astate->data + astate->nbytes, data, ndatabytes);
5291  astate->nbytes += ndatabytes;
5292 
5293  /* Deal with null bitmap if needed */
5294  if (astate->nullbitmap || ARR_HASNULL(arg))
5295  {
5296  int newnitems = astate->nitems + nitems;
5297 
5298  if (astate->nullbitmap == NULL)
5299  {
5300  /*
5301  * First input with nulls; we must retrospectively handle any
5302  * previous inputs by marking all their items non-null.
5303  */
5304  astate->aitems = 256;
5305  while (astate->aitems <= newnitems)
5306  astate->aitems *= 2;
5307  astate->nullbitmap = (bits8 *) palloc((astate->aitems + 7) / 8);
5308  array_bitmap_copy(astate->nullbitmap, 0,
5309  NULL, 0,
5310  astate->nitems);
5311  }
5312  else if (newnitems > astate->aitems)
5313  {
5314  astate->aitems = Max(astate->aitems * 2, newnitems);
5315  astate->nullbitmap = (bits8 *)
5316  repalloc(astate->nullbitmap, (astate->aitems + 7) / 8);
5317  }
5318  array_bitmap_copy(astate->nullbitmap, astate->nitems,
5319  ARR_NULLBITMAP(arg), 0,
5320  nitems);
5321  }
5322 
5323  astate->nitems += nitems;
5324  astate->dims[0] += 1;
5325 
5326  MemoryContextSwitchTo(oldcontext);
5327 
5328  /* Release detoasted copy if any */
5329  if ((Pointer) arg != DatumGetPointer(dvalue))
5330  pfree(arg);
5331 
5332  return astate;
5333 }
5334 
5335 /*
5336  * makeArrayResultArr - produce N+1-D final result of accumArrayResultArr
5337  *
5338  * astate is working state (must not be NULL)
5339  * rcontext is where to construct result
5340  * release is true if okay to release working state
5341  */
5342 Datum
5344  MemoryContext rcontext,
5345  bool release)
5346 {
5347  ArrayType *result;
5348  MemoryContext oldcontext;
5349 
5350  /* Build the final array result in rcontext */
5351  oldcontext = MemoryContextSwitchTo(rcontext);
5352 
5353  if (astate->ndims == 0)
5354  {
5355  /* No inputs, return empty array */
5356  result = construct_empty_array(astate->element_type);
5357  }
5358  else
5359  {
5360  int dataoffset,
5361  nbytes;
5362 
5363  /* Compute required space */
5364  nbytes = astate->nbytes;
5365  if (astate->nullbitmap != NULL)
5366  {
5367  dataoffset = ARR_OVERHEAD_WITHNULLS(astate->ndims, astate->nitems);
5368  nbytes += dataoffset;
5369  }
5370  else
5371  {
5372  dataoffset = 0;
5373  nbytes += ARR_OVERHEAD_NONULLS(astate->ndims);
5374  }
5375 
5376  result = (ArrayType *) palloc0(nbytes);
5377  SET_VARSIZE(result, nbytes);
5378  result->ndim = astate->ndims;
5379  result->dataoffset = dataoffset;
5380  result->elemtype = astate->element_type;
5381 
5382  memcpy(ARR_DIMS(result), astate->dims, astate->ndims * sizeof(int));
5383  memcpy(ARR_LBOUND(result), astate->lbs, astate->ndims * sizeof(int));
5384  memcpy(ARR_DATA_PTR(result), astate->data, astate->nbytes);
5385 
5386  if (astate->nullbitmap != NULL)
5387  array_bitmap_copy(ARR_NULLBITMAP(result), 0,
5388  astate->nullbitmap, 0,
5389  astate->nitems);
5390  }
5391 
5392  MemoryContextSwitchTo(oldcontext);
5393 
5394  /* Clean up all the junk */
5395  if (release)
5396  {
5397  Assert(astate->private_cxt);
5398  MemoryContextDelete(astate->mcontext);
5399  }
5400 
5401  return PointerGetDatum(result);
5402 }
5403 
5404 /*
5405  * The following three functions provide essentially the same API as
5406  * initArrayResult/accumArrayResult/makeArrayResult, but can accept either
5407  * scalar or array inputs, invoking the appropriate set of functions above.
5408  */
5409 
5410 /*
5411  * initArrayResultAny - initialize an empty ArrayBuildStateAny
5412  *
5413  * input_type is the input datatype (either element or array type)
5414  * rcontext is where to keep working state
5415  * subcontext is a flag determining whether to use a separate memory context
5416  */
5418 initArrayResultAny(Oid input_type, MemoryContext rcontext, bool subcontext)
5419 {
5420  ArrayBuildStateAny *astate;
5421  Oid element_type = get_element_type(input_type);
5422 
5423  if (OidIsValid(element_type))
5424  {
5425  /* Array case */
5426  ArrayBuildStateArr *arraystate;
5427 
5428  arraystate = initArrayResultArr(input_type, InvalidOid, rcontext, subcontext);
5429  astate = (ArrayBuildStateAny *)
5430  MemoryContextAlloc(arraystate->mcontext,
5431  sizeof(ArrayBuildStateAny));
5432  astate->scalarstate = NULL;
5433  astate->arraystate = arraystate;
5434  }
5435  else
5436  {
5437  /* Scalar case */
5438  ArrayBuildState *scalarstate;
5439 
5440  /* Let's just check that we have a type that can be put into arrays */
5441  Assert(OidIsValid(get_array_type(input_type)));
5442 
5443  scalarstate = initArrayResult(input_type, rcontext, subcontext);
5444  astate = (ArrayBuildStateAny *)
5445  MemoryContextAlloc(scalarstate->mcontext,
5446  sizeof(ArrayBuildStateAny));
5447  astate->scalarstate = scalarstate;
5448  astate->arraystate = NULL;
5449  }
5450 
5451  return astate;
5452 }
5453 
5454 /*
5455  * accumArrayResultAny - accumulate one (more) input for an array result
5456  *
5457  * astate is working state (can be NULL on first call)
5458  * dvalue/disnull represent the new input to append to the array
5459  * input_type is the input datatype (either element or array type)
5460  * rcontext is where to keep working state
5461  */
5464  Datum dvalue, bool disnull,
5465  Oid input_type,
5466  MemoryContext rcontext)
5467 {
5468  if (astate == NULL)
5469  astate = initArrayResultAny(input_type, rcontext, true);
5470 
5471  if (astate->scalarstate)
5472  (void) accumArrayResult(astate->scalarstate,
5473  dvalue, disnull,
5474  input_type, rcontext);
5475  else
5476  (void) accumArrayResultArr(astate->arraystate,
5477  dvalue, disnull,
5478  input_type, rcontext);
5479 
5480  return astate;
5481 }
5482 
5483 /*
5484  * makeArrayResultAny - produce final result of accumArrayResultAny
5485  *
5486  * astate is working state (must not be NULL)
5487  * rcontext is where to construct result
5488  * release is true if okay to release working state
5489  */
5490 Datum
5492  MemoryContext rcontext, bool release)
5493 {
5494  Datum result;
5495 
5496  if (astate->scalarstate)
5497  {
5498  /* Must use makeMdArrayResult to support "release" parameter */
5499  int ndims;
5500  int dims[1];
5501  int lbs[1];
5502 
5503  /* If no elements were presented, we want to create an empty array */
5504  ndims = (astate->scalarstate->nelems > 0) ? 1 : 0;
5505  dims[0] = astate->scalarstate->nelems;
5506  lbs[0] = 1;
5507 
5508  result = makeMdArrayResult(astate->scalarstate, ndims, dims, lbs,
5509  rcontext, release);
5510  }
5511  else
5512  {
5513  result = makeArrayResultArr(astate->arraystate,
5514  rcontext, release);
5515  }
5516  return result;
5517 }
5518 
5519 
5520 Datum
5522 {
5523  if (array_cmp(fcinfo) > 0)
5525  else
5527 }
5528 
5529 Datum
5531 {
5532  if (array_cmp(fcinfo) < 0)
5534  else
5536 }
5537 
5538 
5540 {
5543  bool reverse;
5545 
5546 /*
5547  * generate_subscripts(array anyarray, dim int [, reverse bool])
5548  * Returns all subscripts of the array for any dimension
5549  */
5550 Datum
5552 {
5553  FuncCallContext *funcctx;
5554  MemoryContext oldcontext;
5556 
5557  /* stuff done only on the first call of the function */
5558  if (SRF_IS_FIRSTCALL())
5559  {
5561  int reqdim = PG_GETARG_INT32(1);
5562  int *lb,
5563  *dimv;
5564 
5565  /* create a function context for cross-call persistence */
5566  funcctx = SRF_FIRSTCALL_INIT();
5567 
5568  /* Sanity check: does it look like an array at all? */
5569  if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
5570  SRF_RETURN_DONE(funcctx);
5571 
5572  /* Sanity check: was the requested dim valid */
5573  if (reqdim <= 0 || reqdim > AARR_NDIM(v))
5574  SRF_RETURN_DONE(funcctx);
5575 
5576  /*
5577  * switch to memory context appropriate for multiple function calls
5578  */
5579  oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
5581 
5582  lb = AARR_LBOUND(v);
5583  dimv = AARR_DIMS(v);
5584 
5585  fctx->lower = lb[reqdim - 1];
5586  fctx->upper = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
5587  fctx->reverse = (PG_NARGS() < 3) ? false : PG_GETARG_BOOL(2);
5588 
5589  funcctx->user_fctx = fctx;
5590 
5591  MemoryContextSwitchTo(oldcontext);
5592  }
5593 
5594  funcctx = SRF_PERCALL_SETUP();
5595 
5596  fctx = funcctx->user_fctx;
5597 
5598  if (fctx->lower <= fctx->upper)
5599  {
5600  if (!fctx->reverse)
5601  SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->lower++));
5602  else
5603  SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->upper--));
5604  }
5605  else
5606  /* done when there are no more elements left */
5607  SRF_RETURN_DONE(funcctx);
5608 }
5609 
5610 /*
5611  * generate_subscripts_nodir
5612  * Implements the 2-argument version of generate_subscripts
5613  */
5614 Datum
5616 {
5617  /* just call the other one -- it can handle both cases */
5618  return generate_subscripts(fcinfo);
5619 }
5620 
5621 /*
5622  * array_fill_with_lower_bounds
5623  * Create and fill array with defined lower bounds.
5624  */
5625 Datum
5627 {
5628  ArrayType *dims;
5629  ArrayType *lbs;
5630  ArrayType *result;
5631  Oid elmtype;
5632  Datum value;
5633  bool isnull;
5634 
5635  if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
5636  ereport(ERROR,
5637  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5638  errmsg("dimension array or low bound array cannot be null")));
5639 
5640  dims = PG_GETARG_ARRAYTYPE_P(1);
5641  lbs = PG_GETARG_ARRAYTYPE_P(2);
5642 
5643  if (!PG_ARGISNULL(0))
5644  {
5645  value = PG_GETARG_DATUM(0);
5646  isnull = false;
5647  }
5648  else
5649  {
5650  value = 0;
5651  isnull = true;
5652  }
5653 
5654  elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
5655  if (!OidIsValid(elmtype))
5656  elog(ERROR, "could not determine data type of input");
5657 
5658  result = array_fill_internal(dims, lbs, value, isnull, elmtype, fcinfo);
5659  PG_RETURN_ARRAYTYPE_P(result);
5660 }
5661 
5662 /*
5663  * array_fill
5664  * Create and fill array with default lower bounds.
5665  */
5666 Datum
5668 {
5669  ArrayType *dims;
5670  ArrayType *result;
5671  Oid elmtype;
5672  Datum value;
5673  bool isnull;
5674 
5675  if (PG_ARGISNULL(1))
5676  ereport(ERROR,
5677  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5678  errmsg("dimension array or low bound array cannot be null")));
5679 
5680  dims = PG_GETARG_ARRAYTYPE_P(1);
5681 
5682  if (!PG_ARGISNULL(0))
5683  {
5684  value = PG_GETARG_DATUM(0);
5685  isnull = false;
5686  }
5687  else
5688  {
5689  value = 0;
5690  isnull = true;
5691  }
5692 
5693  elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
5694  if (!OidIsValid(elmtype))
5695  elog(ERROR, "could not determine data type of input");
5696 
5697  result = array_fill_internal(dims, NULL, value, isnull, elmtype, fcinfo);
5698  PG_RETURN_ARRAYTYPE_P(result);
5699 }
5700 
5701 static ArrayType *
5702 create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
5703  Oid elmtype, int dataoffset)
5704 {
5705  ArrayType *result;
5706 
5707  result = (ArrayType *) palloc0(nbytes);
5708  SET_VARSIZE(result, nbytes);
5709  result->ndim = ndims;
5710  result->dataoffset = dataoffset;
5711  result->elemtype = elmtype;
5712  memcpy(ARR_DIMS(result), dimv, ndims * sizeof(int));
5713  memcpy(ARR_LBOUND(result), lbsv, ndims * sizeof(int));
5714 
5715  return result;
5716 }
5717 
5718 static ArrayType *
5720  Datum value, bool isnull, Oid elmtype,
5721  FunctionCallInfo fcinfo)
5722 {
5723  ArrayType *result;
5724  int *dimv;
5725  int *lbsv;
5726  int ndims;
5727  int nitems;
5728  int deflbs[MAXDIM];
5729  int16 elmlen;
5730  bool elmbyval;
5731  char elmalign;
5732  ArrayMetaState *my_extra;
5733 
5734  /*
5735  * Params checks
5736  */
5737  if (ARR_NDIM(dims) > 1)
5738  ereport(ERROR,
5739  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5740  errmsg("wrong number of array subscripts"),
5741  errdetail("Dimension array must be one dimensional.")));
5742 
5743  if (array_contains_nulls(dims))
5744  ereport(ERROR,
5745  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5746  errmsg("dimension values cannot be null")));
5747 
5748  dimv = (int *) ARR_DATA_PTR(dims);
5749  ndims = (ARR_NDIM(dims) > 0) ? ARR_DIMS(dims)[0] : 0;
5750 
5751  if (ndims < 0) /* we do allow zero-dimension arrays */
5752  ereport(ERROR,
5753  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
5754  errmsg("invalid number of dimensions: %d", ndims)));
5755  if (ndims > MAXDIM)
5756  ereport(ERROR,
5757  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
5758  errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
5759  ndims, MAXDIM)));
5760 
5761  if (lbs != NULL)
5762  {
5763  if (ARR_NDIM(lbs) > 1)
5764  ereport(ERROR,
5765  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5766  errmsg("wrong number of array subscripts"),
5767  errdetail("Dimension array must be one dimensional.")));
5768 
5769  if (array_contains_nulls(lbs))
5770  ereport(ERROR,
5771  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5772  errmsg("dimension values cannot be null")));
5773 
5774  if (ndims != ((ARR_NDIM(lbs) > 0) ? ARR_DIMS(lbs)[0] : 0))
5775  ereport(ERROR,
5776  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5777  errmsg("wrong number of array subscripts"),
5778  errdetail("Low bound array has different size than dimensions array.")));
5779 
5780  lbsv = (int *) ARR_DATA_PTR(lbs);
5781  }
5782  else
5783  {
5784  int i;
5785 
5786  for (i = 0; i < MAXDIM; i++)
5787  deflbs[i] = 1;
5788 
5789  lbsv = deflbs;
5790  }
5791 
5792  nitems = ArrayGetNItems(ndims, dimv);
5793 
5794  /* fast track for empty array */
5795  if (nitems <= 0)
5796  return construct_empty_array(elmtype);
5797 
5798  /*
5799  * We arrange to look up info about element type only once per series of
5800  * calls, assuming the element type doesn't change underneath us.
5801  */
5802  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
5803  if (my_extra == NULL)
5804  {
5805  fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
5806  sizeof(ArrayMetaState));
5807  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
5808  my_extra->element_type = InvalidOid;
5809  }
5810 
5811  if (my_extra->element_type != elmtype)
5812  {
5813  /* Get info about element type */
5814  get_typlenbyvalalign(elmtype,
5815  &my_extra->typlen,
5816  &my_extra->typbyval,
5817  &my_extra->typalign);
5818  my_extra->element_type = elmtype;
5819  }
5820 
5821  elmlen = my_extra->typlen;
5822  elmbyval = my_extra->typbyval;
5823  elmalign = my_extra->typalign;
5824 
5825  /* compute required space */
5826  if (!isnull)
5827  {
5828  int i;
5829  char *p;
5830  int nbytes;
5831  int totbytes;
5832 
5833  /* make sure data is not toasted */
5834  if (elmlen == -1)
5835  value = PointerGetDatum(PG_DETOAST_DATUM(value));
5836 
5837  nbytes = att_addlength_datum(0, elmlen, value);
5838  nbytes = att_align_nominal(nbytes, elmalign);
5839  Assert(nbytes > 0);
5840 
5841  totbytes = nbytes * nitems;
5842 
5843  /* check for overflow of multiplication or total request */
5844  if (totbytes / nbytes != nitems ||
5845  !AllocSizeIsValid(totbytes))
5846  ereport(ERROR,
5847  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
5848  errmsg("array size exceeds the maximum allowed (%d)",
5849  (int) MaxAllocSize)));
5850 
5851  /*
5852  * This addition can't overflow, but it might cause us to go past
5853  * MaxAllocSize. We leave it to palloc to complain in that case.
5854  */
5855  totbytes += ARR_OVERHEAD_NONULLS(ndims);
5856 
5857  result = create_array_envelope(ndims, dimv, lbsv, totbytes,
5858  elmtype, 0);
5859 
5860  p = ARR_DATA_PTR(result);
5861  for (i = 0; i < nitems; i++)
5862  p += ArrayCastAndSet(value, elmlen, elmbyval, elmalign, p);
5863  }
5864  else
5865  {
5866  int nbytes;
5867  int dataoffset;
5868 
5869  dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
5870  nbytes = dataoffset;
5871 
5872  result = create_array_envelope(ndims, dimv, lbsv, nbytes,
5873  elmtype, dataoffset);
5874 
5875  /* create_array_envelope already zeroed the bitmap, so we're done */
5876  }
5877 
5878  return result;
5879 }
5880 
5881 
5882 /*
5883  * UNNEST
5884  */
5885 Datum
5887 {
5888  typedef struct
5889  {
5890  array_iter iter;
5891  int nextelem;
5892  int numelems;
5893  int16 elmlen;
5894  bool elmbyval;
5895  char elmalign;
5896  } array_unnest_fctx;
5897 
5898  FuncCallContext *funcctx;
5899  array_unnest_fctx *fctx;
5900  MemoryContext oldcontext;
5901 
5902  /* stuff done only on the first call of the function */
5903  if (SRF_IS_FIRSTCALL())
5904  {
5905  AnyArrayType *arr;
5906 
5907  /* create a function context for cross-call persistence */
5908  funcctx = SRF_FIRSTCALL_INIT();
5909 
5910  /*
5911  * switch to memory context appropriate for multiple function calls
5912  */
5913  oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
5914 
5915  /*
5916  * Get the array value and detoast if needed. We can't do this
5917  * earlier because if we have to detoast, we want the detoasted copy
5918  * to be in multi_call_memory_ctx, so it will go away when we're done
5919  * and not before. (If no detoast happens, we assume the originally
5920  * passed array will stick around till then.)
5921  */
5922  arr = PG_GETARG_ANY_ARRAY(0);
5923 
5924  /* allocate memory for user context */
5925  fctx = (array_unnest_fctx *) palloc(sizeof(array_unnest_fctx));
5926 
5927  /* initialize state */
5928  array_iter_setup(&fctx->iter, arr);
5929  fctx->nextelem = 0;
5930  fctx->numelems = ArrayGetNItems(AARR_NDIM(arr), AARR_DIMS(arr));
5931 
5932  if (VARATT_IS_EXPANDED_HEADER(arr))
5933  {
5934  /* we can just grab the type data from expanded array */
5935  fctx->elmlen = arr->xpn.typlen;
5936  fctx->elmbyval = arr->xpn.typbyval;
5937  fctx->elmalign = arr->xpn.typalign;
5938  }
5939  else
5941  &fctx->elmlen,
5942  &fctx->elmbyval,
5943  &fctx->elmalign);
5944 
5945  funcctx->user_fctx = fctx;
5946  MemoryContextSwitchTo(oldcontext);
5947  }
5948 
5949  /* stuff done on every call of the function */
5950  funcctx = SRF_PERCALL_SETUP();
5951  fctx = funcctx->user_fctx;
5952 
5953  if (fctx->nextelem < fctx->numelems)
5954  {
5955  int offset = fctx->nextelem++;
5956  Datum elem;
5957 
5958  elem = array_iter_next(&fctx->iter, &fcinfo->isnull, offset,
5959  fctx->elmlen, fctx->elmbyval, fctx->elmalign);
5960 
5961  SRF_RETURN_NEXT(funcctx, elem);
5962  }
5963  else
5964  {
5965  /* do when there is no more left */
5966  SRF_RETURN_DONE(funcctx);
5967  }
5968 }
5969 
5970 
5971 /*
5972  * array_replace/array_remove support
5973  *
5974  * Find all array entries matching (not distinct from) search/search_isnull,
5975  * and delete them if remove is true, else replace them with
5976  * replace/replace_isnull. Comparisons are done using the specified
5977  * collation. fcinfo is passed only for caching purposes.
5978  */
5979 static ArrayType *
5981  Datum search, bool search_isnull,
5982  Datum replace, bool replace_isnull,
5983  bool remove, Oid collation,
5984  FunctionCallInfo fcinfo)
5985 {
5986  ArrayType *result;
5987  Oid element_type;
5988  Datum *values;
5989  bool *nulls;
5990  int *dim;
5991  int ndim;
5992  int nitems,
5993  nresult;
5994  int i;
5995  int32 nbytes = 0;
5996  int32 dataoffset;
5997  bool hasnulls;
5998  int typlen;
5999  bool typbyval;
6000  char typalign;
6001  char *arraydataptr;
6002  bits8 *bitmap;
6003  int bitmask;
6004  bool changed = false;
6005  TypeCacheEntry *typentry;
6006  FunctionCallInfoData locfcinfo;
6007 
6008  element_type = ARR_ELEMTYPE(array);
6009  ndim = ARR_NDIM(array);
6010  dim = ARR_DIMS(array);
6011  nitems = ArrayGetNItems(ndim, dim);
6012 
6013  /* Return input array unmodified if it is empty */
6014  if (nitems <= 0)
6015  return array;
6016 
6017  /*
6018  * We can't remove elements from multi-dimensional arrays, since the
6019  * result might not be rectangular.
6020  */
6021  if (remove && ndim > 1)
6022  ereport(ERROR,
6023  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6024  errmsg("removing elements from multidimensional arrays is not supported")));
6025 
6026  /*
6027  * We arrange to look up the equality function only once per series of
6028  * calls, assuming the element type doesn't change underneath us.
6029  */
6030  typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
6031  if (typentry == NULL ||
6032  typentry->type_id != element_type)
6033  {
6034  typentry = lookup_type_cache(element_type,
6036  if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
6037  ereport(ERROR,
6038  (errcode(ERRCODE_UNDEFINED_FUNCTION),
6039  errmsg("could not identify an equality operator for type %s",
6040  format_type_be(element_type))));
6041  fcinfo->flinfo->fn_extra = (void *) typentry;
6042  }
6043  typlen = typentry->typlen;
6044  typbyval = typentry->typbyval;
6045  typalign = typentry->typalign;
6046 
6047  /*
6048  * Detoast values if they are toasted. The replacement value must be
6049  * detoasted for insertion into the result array, while detoasting the
6050  * search value only once saves cycles.
6051  */
6052  if (typlen == -1)
6053  {
6054  if (!search_isnull)
6055  search = PointerGetDatum(PG_DETOAST_DATUM(search));
6056  if (!replace_isnull)
6057  replace = PointerGetDatum(PG_DETOAST_DATUM(replace));
6058  }
6059 
6060  /* Prepare to apply the comparison operator */
6061  InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
6062  collation, NULL, NULL);
6063 
6064  /* Allocate temporary arrays for new values */
6065  values = (Datum *) palloc(nitems * sizeof(Datum));
6066  nulls = (bool *) palloc(nitems * sizeof(bool));
6067 
6068  /* Loop over source data */
6069  arraydataptr = ARR_DATA_PTR(array);
6070  bitmap = ARR_NULLBITMAP(array);
6071  bitmask = 1;
6072  hasnulls = false;
6073  nresult = 0;
6074 
6075  for (i = 0; i < nitems; i++)
6076  {
6077  Datum elt;
6078  bool isNull;
6079  bool oprresult;
6080  bool skip = false;
6081 
6082  /* Get source element, checking for NULL */
6083  if (bitmap && (*bitmap & bitmask) == 0)
6084  {
6085  isNull = true;
6086  /* If searching for NULL, we have a match */
6087  if (search_isnull)
6088  {
6089  if (remove)
6090  {
6091  skip = true;
6092  changed = true;
6093  }
6094  else if (!replace_isnull)
6095  {
6096  values[nresult] = replace;
6097  isNull = false;
6098  changed = true;
6099  }
6100  }
6101  }
6102  else
6103  {
6104  isNull = false;
6105  elt = fetch_att(arraydataptr, typbyval, typlen);
6106  arraydataptr = att_addlength_datum(arraydataptr, typlen, elt);
6107  arraydataptr = (char *) att_align_nominal(arraydataptr, typalign);
6108 
6109  if (search_isnull)
6110  {
6111  /* no match possible, keep element */
6112  values[nresult] = elt;
6113  }
6114  else
6115  {
6116  /*
6117  * Apply the operator to the element pair
6118  */
6119  locfcinfo.arg[0] = elt;
6120  locfcinfo.arg[1] = search;
6121  locfcinfo.argnull[0] = false;
6122  locfcinfo.argnull[1] = false;
6123  locfcinfo.isnull = false;
6124  oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
6125  if (!oprresult)
6126  {
6127