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