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  dim[i] = 1 + upperIndx[i] - lowerIndx[i];
2891  lb[i] = lowerIndx[i];
2892  }
2893 
2894  /* complain if too few source items; we ignore extras, however */
2895  if (nelems < ArrayGetNItems(nSubscripts, dim))
2896  ereport(ERROR,
2897  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2898  errmsg("source array too small")));
2899 
2900  return PointerGetDatum(construct_md_array(dvalues, dnulls, nSubscripts,
2901  dim, lb, elmtype,
2902  elmlen, elmbyval, elmalign));
2903  }
2904 
2905  if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
2906  ereport(ERROR,
2907  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2908  errmsg("wrong number of array subscripts")));
2909 
2910  /* copy dim/lb since we may modify them */
2911  memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
2912  memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
2913 
2914  newhasnulls = (ARR_HASNULL(array) || ARR_HASNULL(srcArray));
2915  addedbefore = addedafter = 0;
2916 
2917  /*
2918  * Check subscripts. We assume the existing subscripts passed
2919  * ArrayCheckBounds, so that dim[i] + lb[i] can be computed without
2920  * overflow. But we must beware of other overflows in our calculations of
2921  * new dim[] values.
2922  */
2923  if (ndim == 1)
2924  {
2925  Assert(nSubscripts == 1);
2926  if (!lowerProvided[0])
2927  lowerIndx[0] = lb[0];
2928  if (!upperProvided[0])
2929  upperIndx[0] = dim[0] + lb[0] - 1;
2930  if (lowerIndx[0] > upperIndx[0])
2931  ereport(ERROR,
2932  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2933  errmsg("upper bound cannot be less than lower bound")));
2934  if (lowerIndx[0] < lb[0])
2935  {
2936  /* addedbefore = lb[0] - lowerIndx[0]; */
2937  /* dim[0] += addedbefore; */
2938  if (pg_sub_s32_overflow(lb[0], lowerIndx[0], &addedbefore) ||
2939  pg_add_s32_overflow(dim[0], addedbefore, &dim[0]))
2940  ereport(ERROR,
2941  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2942  errmsg("array size exceeds the maximum allowed (%d)",
2943  (int) MaxArraySize)));
2944  lb[0] = lowerIndx[0];
2945  if (addedbefore > 1)
2946  newhasnulls = true; /* will insert nulls */
2947  }
2948  if (upperIndx[0] >= (dim[0] + lb[0]))
2949  {
2950  /* addedafter = upperIndx[0] - (dim[0] + lb[0]) + 1; */
2951  /* dim[0] += addedafter; */
2952  if (pg_sub_s32_overflow(upperIndx[0], dim[0] + lb[0], &addedafter) ||
2953  pg_add_s32_overflow(addedafter, 1, &addedafter) ||
2954  pg_add_s32_overflow(dim[0], addedafter, &dim[0]))
2955  ereport(ERROR,
2956  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2957  errmsg("array size exceeds the maximum allowed (%d)",
2958  (int) MaxArraySize)));
2959  if (addedafter > 1)
2960  newhasnulls = true; /* will insert nulls */
2961  }
2962  }
2963  else
2964  {
2965  /*
2966  * XXX currently we do not support extending multi-dimensional arrays
2967  * during assignment
2968  */
2969  for (i = 0; i < nSubscripts; i++)
2970  {
2971  if (!lowerProvided[i])
2972  lowerIndx[i] = lb[i];
2973  if (!upperProvided[i])
2974  upperIndx[i] = dim[i] + lb[i] - 1;
2975  if (lowerIndx[i] > upperIndx[i])
2976  ereport(ERROR,
2977  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2978  errmsg("upper bound cannot be less than lower bound")));
2979  if (lowerIndx[i] < lb[i] ||
2980  upperIndx[i] >= (dim[i] + lb[i]))
2981  ereport(ERROR,
2982  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2983  errmsg("array subscript out of range")));
2984  }
2985  /* fill any missing subscript positions with full array range */
2986  for (; i < ndim; i++)
2987  {
2988  lowerIndx[i] = lb[i];
2989  upperIndx[i] = dim[i] + lb[i] - 1;
2990  if (lowerIndx[i] > upperIndx[i])
2991  ereport(ERROR,
2992  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2993  errmsg("upper bound cannot be less than lower bound")));
2994  }
2995  }
2996 
2997  /* Do this mainly to check for overflow */
2998  nitems = ArrayGetNItems(ndim, dim);
2999  ArrayCheckBounds(ndim, dim, lb);
3000 
3001  /*
3002  * Make sure source array has enough entries. Note we ignore the shape of
3003  * the source array and just read entries serially.
3004  */
3005  mda_get_range(ndim, span, lowerIndx, upperIndx);
3006  nsrcitems = ArrayGetNItems(ndim, span);
3007  if (nsrcitems > ArrayGetNItems(ARR_NDIM(srcArray), ARR_DIMS(srcArray)))
3008  ereport(ERROR,
3009  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
3010  errmsg("source array too small")));
3011 
3012  /*
3013  * Compute space occupied by new entries, space occupied by replaced
3014  * entries, and required space for new array.
3015  */
3016  if (newhasnulls)
3017  overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
3018  else
3019  overheadlen = ARR_OVERHEAD_NONULLS(ndim);
3020  newitemsize = array_nelems_size(ARR_DATA_PTR(srcArray), 0,
3021  ARR_NULLBITMAP(srcArray), nsrcitems,
3022  elmlen, elmbyval, elmalign);
3023  oldoverheadlen = ARR_DATA_OFFSET(array);
3024  olddatasize = ARR_SIZE(array) - oldoverheadlen;
3025  if (ndim > 1)
3026  {
3027  /*
3028  * here we do not need to cope with extension of the array; it would
3029  * be a lot more complicated if we had to do so...
3030  */
3031  olditemsize = array_slice_size(ARR_DATA_PTR(array),
3032  ARR_NULLBITMAP(array),
3033  ndim, dim, lb,
3034  lowerIndx, upperIndx,
3035  elmlen, elmbyval, elmalign);
3036  lenbefore = lenafter = 0; /* keep compiler quiet */
3037  itemsbefore = itemsafter = nolditems = 0;
3038  }
3039  else
3040  {
3041  /*
3042  * here we must allow for possibility of slice larger than orig array
3043  * and/or not adjacent to orig array subscripts
3044  */
3045  int oldlb = ARR_LBOUND(array)[0];
3046  int oldub = oldlb + ARR_DIMS(array)[0] - 1;
3047  int slicelb = Max(oldlb, lowerIndx[0]);
3048  int sliceub = Min(oldub, upperIndx[0]);
3049  char *oldarraydata = ARR_DATA_PTR(array);
3050  bits8 *oldarraybitmap = ARR_NULLBITMAP(array);
3051 
3052  /* count/size of old array entries that will go before the slice */
3053  itemsbefore = Min(slicelb, oldub + 1) - oldlb;
3054  lenbefore = array_nelems_size(oldarraydata, 0, oldarraybitmap,
3055  itemsbefore,
3056  elmlen, elmbyval, elmalign);
3057  /* count/size of old array entries that will be replaced by slice */
3058  if (slicelb > sliceub)
3059  {
3060  nolditems = 0;
3061  olditemsize = 0;
3062  }
3063  else
3064  {
3065  nolditems = sliceub - slicelb + 1;
3066  olditemsize = array_nelems_size(oldarraydata + lenbefore,
3067  itemsbefore, oldarraybitmap,
3068  nolditems,
3069  elmlen, elmbyval, elmalign);
3070  }
3071  /* count/size of old array entries that will go after the slice */
3072  itemsafter = oldub + 1 - Max(sliceub + 1, oldlb);
3073  lenafter = olddatasize - lenbefore - olditemsize;
3074  }
3075 
3076  newsize = overheadlen + olddatasize - olditemsize + newitemsize;
3077 
3078  newarray = (ArrayType *) palloc0(newsize);
3079  SET_VARSIZE(newarray, newsize);
3080  newarray->ndim = ndim;
3081  newarray->dataoffset = newhasnulls ? overheadlen : 0;
3082  newarray->elemtype = ARR_ELEMTYPE(array);
3083  memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
3084  memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
3085 
3086  if (ndim > 1)
3087  {
3088  /*
3089  * here we do not need to cope with extension of the array; it would
3090  * be a lot more complicated if we had to do so...
3091  */
3092  array_insert_slice(newarray, array, srcArray,
3093  ndim, dim, lb,
3094  lowerIndx, upperIndx,
3095  elmlen, elmbyval, elmalign);
3096  }
3097  else
3098  {
3099  /* fill in data */
3100  memcpy((char *) newarray + overheadlen,
3101  (char *) array + oldoverheadlen,
3102  lenbefore);
3103  memcpy((char *) newarray + overheadlen + lenbefore,
3104  ARR_DATA_PTR(srcArray),
3105  newitemsize);
3106  memcpy((char *) newarray + overheadlen + lenbefore + newitemsize,
3107  (char *) array + oldoverheadlen + lenbefore + olditemsize,
3108  lenafter);
3109  /* fill in nulls bitmap if needed */
3110  if (newhasnulls)
3111  {
3112  bits8 *newnullbitmap = ARR_NULLBITMAP(newarray);
3113  bits8 *oldnullbitmap = ARR_NULLBITMAP(array);
3114 
3115  /* palloc0 above already marked any inserted positions as nulls */
3116  array_bitmap_copy(newnullbitmap, addedbefore,
3117  oldnullbitmap, 0,
3118  itemsbefore);
3119  array_bitmap_copy(newnullbitmap, lowerIndx[0] - lb[0],
3120  ARR_NULLBITMAP(srcArray), 0,
3121  nsrcitems);
3122  array_bitmap_copy(newnullbitmap, addedbefore + itemsbefore + nolditems,
3123  oldnullbitmap, itemsbefore + nolditems,
3124  itemsafter);
3125  }
3126  }
3127 
3128  return PointerGetDatum(newarray);
3129 }
3130 
3131 /*
3132  * array_ref : backwards compatibility wrapper for array_get_element
3133  *
3134  * This only works for detoasted/flattened varlena arrays, since the array
3135  * argument is declared as "ArrayType *". However there's enough code like
3136  * that to justify preserving this API.
3137  */
3138 Datum
3139 array_ref(ArrayType *array, int nSubscripts, int *indx,
3140  int arraytyplen, int elmlen, bool elmbyval, char elmalign,
3141  bool *isNull)
3142 {
3143  return array_get_element(PointerGetDatum(array), nSubscripts, indx,
3144  arraytyplen, elmlen, elmbyval, elmalign,
3145  isNull);
3146 }
3147 
3148 /*
3149  * array_set : backwards compatibility wrapper for array_set_element
3150  *
3151  * This only works for detoasted/flattened varlena arrays, since the array
3152  * argument and result are declared as "ArrayType *". However there's enough
3153  * code like that to justify preserving this API.
3154  */
3155 ArrayType *
3156 array_set(ArrayType *array, int nSubscripts, int *indx,
3157  Datum dataValue, bool isNull,
3158  int arraytyplen, int elmlen, bool elmbyval, char elmalign)
3159 {
3161  nSubscripts, indx,
3162  dataValue, isNull,
3163  arraytyplen,
3164  elmlen, elmbyval, elmalign));
3165 }
3166 
3167 /*
3168  * array_map()
3169  *
3170  * Map an array through an arbitrary expression. Return a new array with
3171  * the same dimensions and each source element transformed by the given,
3172  * already-compiled expression. Each source element is placed in the
3173  * innermost_caseval/innermost_casenull fields of the ExprState.
3174  *
3175  * Parameters are:
3176  * * arrayd: Datum representing array argument.
3177  * * exprstate: ExprState representing the per-element transformation.
3178  * * econtext: context for expression evaluation.
3179  * * retType: OID of element type of output array. This must be the same as,
3180  * or binary-compatible with, the result type of the expression. It might
3181  * be different from the input array's element type.
3182  * * amstate: workspace for array_map. Must be zeroed by caller before
3183  * first call, and not touched after that.
3184  *
3185  * It is legitimate to pass a freshly-zeroed ArrayMapState on each call,
3186  * but better performance can be had if the state can be preserved across
3187  * a series of calls.
3188  *
3189  * NB: caller must assure that input array is not NULL. NULL elements in
3190  * the array are OK however.
3191  * NB: caller should be running in econtext's per-tuple memory context.
3192  */
3193 Datum
3195  ExprState *exprstate, ExprContext *econtext,
3196  Oid retType, ArrayMapState *amstate)
3197 {
3198  AnyArrayType *v = DatumGetAnyArrayP(arrayd);
3199  ArrayType *result;
3200  Datum *values;
3201  bool *nulls;
3202  int *dim;
3203  int ndim;
3204  int nitems;
3205  int i;
3206  int32 nbytes = 0;
3207  int32 dataoffset;
3208  bool hasnulls;
3209  Oid inpType;
3210  int inp_typlen;
3211  bool inp_typbyval;
3212  char inp_typalign;
3213  int typlen;
3214  bool typbyval;
3215  char typalign;
3216  array_iter iter;
3217  ArrayMetaState *inp_extra;
3218  ArrayMetaState *ret_extra;
3219  Datum *transform_source = exprstate->innermost_caseval;
3220  bool *transform_source_isnull = exprstate->innermost_casenull;
3221 
3222  inpType = AARR_ELEMTYPE(v);
3223  ndim = AARR_NDIM(v);
3224  dim = AARR_DIMS(v);
3225  nitems = ArrayGetNItems(ndim, dim);
3226 
3227  /* Check for empty array */
3228  if (nitems <= 0)
3229  {
3230  /* Return empty array */
3231  return PointerGetDatum(construct_empty_array(retType));
3232  }
3233 
3234  /*
3235  * We arrange to look up info about input and return element types only
3236  * once per series of calls, assuming the element type doesn't change
3237  * underneath us.
3238  */
3239  inp_extra = &amstate->inp_extra;
3240  ret_extra = &amstate->ret_extra;
3241 
3242  if (inp_extra->element_type != inpType)
3243  {
3244  get_typlenbyvalalign(inpType,
3245  &inp_extra->typlen,
3246  &inp_extra->typbyval,
3247  &inp_extra->typalign);
3248  inp_extra->element_type = inpType;
3249  }
3250  inp_typlen = inp_extra->typlen;
3251  inp_typbyval = inp_extra->typbyval;
3252  inp_typalign = inp_extra->typalign;
3253 
3254  if (ret_extra->element_type != retType)
3255  {
3256  get_typlenbyvalalign(retType,
3257  &ret_extra->typlen,
3258  &ret_extra->typbyval,
3259  &ret_extra->typalign);
3260  ret_extra->element_type = retType;
3261  }
3262  typlen = ret_extra->typlen;
3263  typbyval = ret_extra->typbyval;
3264  typalign = ret_extra->typalign;
3265 
3266  /* Allocate temporary arrays for new values */
3267  values = (Datum *) palloc(nitems * sizeof(Datum));
3268  nulls = (bool *) palloc(nitems * sizeof(bool));
3269 
3270  /* Loop over source data */
3271  array_iter_setup(&iter, v);
3272  hasnulls = false;
3273 
3274  for (i = 0; i < nitems; i++)
3275  {
3276  /* Get source element, checking for NULL */
3277  *transform_source =
3278  array_iter_next(&iter, transform_source_isnull, i,
3279  inp_typlen, inp_typbyval, inp_typalign);
3280 
3281  /* Apply the given expression to source element */
3282  values[i] = ExecEvalExpr(exprstate, econtext, &nulls[i]);
3283 
3284  if (nulls[i])
3285  hasnulls = true;
3286  else
3287  {
3288  /* Ensure data is not toasted */
3289  if (typlen == -1)
3291  /* Update total result size */
3292  nbytes = att_addlength_datum(nbytes, typlen, values[i]);
3293  nbytes = att_align_nominal(nbytes, typalign);
3294  /* check for overflow of total request */
3295  if (!AllocSizeIsValid(nbytes))
3296  ereport(ERROR,
3297  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3298  errmsg("array size exceeds the maximum allowed (%d)",
3299  (int) MaxAllocSize)));
3300  }
3301  }
3302 
3303  /* Allocate and fill the result array */
3304  if (hasnulls)
3305  {
3306  dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
3307  nbytes += dataoffset;
3308  }
3309  else
3310  {
3311  dataoffset = 0; /* marker for no null bitmap */
3312  nbytes += ARR_OVERHEAD_NONULLS(ndim);
3313  }
3314  result = (ArrayType *) palloc0(nbytes);
3315  SET_VARSIZE(result, nbytes);
3316  result->ndim = ndim;
3317  result->dataoffset = dataoffset;
3318  result->elemtype = retType;
3319  memcpy(ARR_DIMS(result), AARR_DIMS(v), ndim * sizeof(int));
3320  memcpy(ARR_LBOUND(result), AARR_LBOUND(v), ndim * sizeof(int));
3321 
3322  CopyArrayEls(result,
3323  values, nulls, nitems,
3324  typlen, typbyval, typalign,
3325  false);
3326 
3327  /*
3328  * Note: do not risk trying to pfree the results of the called expression
3329  */
3330  pfree(values);
3331  pfree(nulls);
3332 
3333  return PointerGetDatum(result);
3334 }
3335 
3336 /*
3337  * construct_array --- simple method for constructing an array object
3338  *
3339  * elems: array of Datum items to become the array contents
3340  * (NULL element values are not supported).
3341  * nelems: number of items
3342  * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
3343  *
3344  * A palloc'd 1-D array object is constructed and returned. Note that
3345  * elem values will be copied into the object even if pass-by-ref type.
3346  * Also note the result will be 0-D not 1-D if nelems = 0.
3347  *
3348  * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
3349  * from the system catalogs, given the elmtype. However, the caller is
3350  * in a better position to cache this info across multiple uses, or even
3351  * to hard-wire values if the element type is hard-wired.
3352  */
3353 ArrayType *
3354 construct_array(Datum *elems, int nelems,
3355  Oid elmtype,
3356  int elmlen, bool elmbyval, char elmalign)
3357 {
3358  int dims[1];
3359  int lbs[1];
3360 
3361  dims[0] = nelems;
3362  lbs[0] = 1;
3363 
3364  return construct_md_array(elems, NULL, 1, dims, lbs,
3365  elmtype, elmlen, elmbyval, elmalign);
3366 }
3367 
3368 /*
3369  * Like construct_array(), where elmtype must be a built-in type, and
3370  * elmlen/elmbyval/elmalign is looked up from hardcoded data. This is often
3371  * useful when manipulating arrays from/for system catalogs.
3372  */
3373 ArrayType *
3374 construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
3375 {
3376  int elmlen;
3377  bool elmbyval;
3378  char elmalign;
3379 
3380  switch (elmtype)
3381  {
3382  case CHAROID:
3383  elmlen = 1;
3384  elmbyval = true;
3385  elmalign = TYPALIGN_CHAR;
3386  break;
3387 
3388  case CSTRINGOID:
3389  elmlen = -2;
3390  elmbyval = false;
3391  elmalign = TYPALIGN_CHAR;
3392  break;
3393 
3394  case FLOAT4OID:
3395  elmlen = sizeof(float4);
3396  elmbyval = true;
3397  elmalign = TYPALIGN_INT;
3398  break;
3399 
3400  case INT2OID:
3401  elmlen = sizeof(int16);
3402  elmbyval = true;
3403  elmalign = TYPALIGN_SHORT;
3404  break;
3405 
3406  case INT4OID:
3407  elmlen = sizeof(int32);
3408  elmbyval = true;
3409  elmalign = TYPALIGN_INT;
3410  break;
3411 
3412  case INT8OID:
3413  elmlen = sizeof(int64);
3414  elmbyval = FLOAT8PASSBYVAL;
3415  elmalign = TYPALIGN_DOUBLE;
3416  break;
3417 
3418  case NAMEOID:
3419  elmlen = NAMEDATALEN;
3420  elmbyval = false;
3421  elmalign = TYPALIGN_CHAR;
3422  break;
3423 
3424  case OIDOID:
3425  case REGTYPEOID:
3426  elmlen = sizeof(Oid);
3427  elmbyval = true;
3428  elmalign = TYPALIGN_INT;
3429  break;
3430 
3431  case TEXTOID:
3432  elmlen = -1;
3433  elmbyval = false;
3434  elmalign = TYPALIGN_INT;
3435  break;
3436 
3437  case TIDOID:
3438  elmlen = sizeof(ItemPointerData);
3439  elmbyval = false;
3440  elmalign = TYPALIGN_SHORT;
3441  break;
3442 
3443  default:
3444  elog(ERROR, "type %u not supported by construct_array_builtin()", elmtype);
3445  /* keep compiler quiet */
3446  elmlen = 0;
3447  elmbyval = false;
3448  elmalign = 0;
3449  }
3450 
3451  return construct_array(elems, nelems, elmtype, elmlen, elmbyval, elmalign);
3452 }
3453 
3454 /*
3455  * construct_md_array --- simple method for constructing an array object
3456  * with arbitrary dimensions and possible NULLs
3457  *
3458  * elems: array of Datum items to become the array contents
3459  * nulls: array of is-null flags (can be NULL if no nulls)
3460  * ndims: number of dimensions
3461  * dims: integer array with size of each dimension
3462  * lbs: integer array with lower bound of each dimension
3463  * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
3464  *
3465  * A palloc'd ndims-D array object is constructed and returned. Note that
3466  * elem values will be copied into the object even if pass-by-ref type.
3467  * Also note the result will be 0-D not ndims-D if any dims[i] = 0.
3468  *
3469  * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
3470  * from the system catalogs, given the elmtype. However, the caller is
3471  * in a better position to cache this info across multiple uses, or even
3472  * to hard-wire values if the element type is hard-wired.
3473  */
3474 ArrayType *
3476  bool *nulls,
3477  int ndims,
3478  int *dims,
3479  int *lbs,
3480  Oid elmtype, int elmlen, bool elmbyval, char elmalign)
3481 {
3482  ArrayType *result;
3483  bool hasnulls;
3484  int32 nbytes;
3485  int32 dataoffset;
3486  int i;
3487  int nelems;
3488 
3489  if (ndims < 0) /* we do allow zero-dimension arrays */
3490  ereport(ERROR,
3491  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3492  errmsg("invalid number of dimensions: %d", ndims)));
3493  if (ndims > MAXDIM)
3494  ereport(ERROR,
3495  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3496  errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
3497  ndims, MAXDIM)));
3498 
3499  /* This checks for overflow of the array dimensions */
3500  nelems = ArrayGetNItems(ndims, dims);
3501  ArrayCheckBounds(ndims, dims, lbs);
3502 
3503  /* if ndims <= 0 or any dims[i] == 0, return empty array */
3504  if (nelems <= 0)
3505  return construct_empty_array(elmtype);
3506 
3507  /* compute required space */
3508  nbytes = 0;
3509  hasnulls = false;
3510  for (i = 0; i < nelems; i++)
3511  {
3512  if (nulls && nulls[i])
3513  {
3514  hasnulls = true;
3515  continue;
3516  }
3517  /* make sure data is not toasted */
3518  if (elmlen == -1)
3519  elems[i] = PointerGetDatum(PG_DETOAST_DATUM(elems[i]));
3520  nbytes = att_addlength_datum(nbytes, elmlen, elems[i]);
3521  nbytes = att_align_nominal(nbytes, elmalign);
3522  /* check for overflow of total request */
3523  if (!AllocSizeIsValid(nbytes))
3524  ereport(ERROR,
3525  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3526  errmsg("array size exceeds the maximum allowed (%d)",
3527  (int) MaxAllocSize)));
3528  }
3529 
3530  /* Allocate and initialize result array */
3531  if (hasnulls)
3532  {
3533  dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nelems);
3534  nbytes += dataoffset;
3535  }
3536  else
3537  {
3538  dataoffset = 0; /* marker for no null bitmap */
3539  nbytes += ARR_OVERHEAD_NONULLS(ndims);
3540  }
3541  result = (ArrayType *) palloc0(nbytes);
3542  SET_VARSIZE(result, nbytes);
3543  result->ndim = ndims;
3544  result->dataoffset = dataoffset;
3545  result->elemtype = elmtype;
3546  memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
3547  memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
3548 
3549  CopyArrayEls(result,
3550  elems, nulls, nelems,
3551  elmlen, elmbyval, elmalign,
3552  false);
3553 
3554  return result;
3555 }
3556 
3557 /*
3558  * construct_empty_array --- make a zero-dimensional array of given type
3559  */
3560 ArrayType *
3562 {
3563  ArrayType *result;
3564 
3565  result = (ArrayType *) palloc0(sizeof(ArrayType));
3566  SET_VARSIZE(result, sizeof(ArrayType));
3567  result->ndim = 0;
3568  result->dataoffset = 0;
3569  result->elemtype = elmtype;
3570  return result;
3571 }
3572 
3573 /*
3574  * construct_empty_expanded_array: make an empty expanded array
3575  * given only type information. (metacache can be NULL if not needed.)
3576  */
3579  MemoryContext parentcontext,
3580  ArrayMetaState *metacache)
3581 {
3582  ArrayType *array = construct_empty_array(element_type);
3583  Datum d;
3584 
3585  d = expand_array(PointerGetDatum(array), parentcontext, metacache);
3586  pfree(array);
3587  return (ExpandedArrayHeader *) DatumGetEOHP(d);
3588 }
3589 
3590 /*
3591  * deconstruct_array --- simple method for extracting data from an array
3592  *
3593  * array: array object to examine (must not be NULL)
3594  * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
3595  * elemsp: return value, set to point to palloc'd array of Datum values
3596  * nullsp: return value, set to point to palloc'd array of isnull markers
3597  * nelemsp: return value, set to number of extracted values
3598  *
3599  * The caller may pass nullsp == NULL if it does not support NULLs in the
3600  * array. Note that this produces a very uninformative error message,
3601  * so do it only in cases where a NULL is really not expected.
3602  *
3603  * If array elements are pass-by-ref data type, the returned Datums will
3604  * be pointers into the array object.
3605  *
3606  * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
3607  * from the system catalogs, given the elmtype. However, the caller is
3608  * in a better position to cache this info across multiple uses, or even
3609  * to hard-wire values if the element type is hard-wired.
3610  */
3611 void
3613  Oid elmtype,
3614  int elmlen, bool elmbyval, char elmalign,
3615  Datum **elemsp, bool **nullsp, int *nelemsp)
3616 {
3617  Datum *elems;
3618  bool *nulls;
3619  int nelems;
3620  char *p;
3621  bits8 *bitmap;
3622  int bitmask;
3623  int i;
3624 
3625  Assert(ARR_ELEMTYPE(array) == elmtype);
3626 
3627  nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
3628  *elemsp = elems = (Datum *) palloc(nelems * sizeof(Datum));
3629  if (nullsp)
3630  *nullsp = nulls = (bool *) palloc0(nelems * sizeof(bool));
3631  else
3632  nulls = NULL;
3633  *nelemsp = nelems;
3634 
3635  p = ARR_DATA_PTR(array);
3636  bitmap = ARR_NULLBITMAP(array);
3637  bitmask = 1;
3638 
3639  for (i = 0; i < nelems; i++)
3640  {
3641  /* Get source element, checking for NULL */
3642  if (bitmap && (*bitmap & bitmask) == 0)
3643  {
3644  elems[i] = (Datum) 0;
3645  if (nulls)
3646  nulls[i] = true;
3647  else
3648  ereport(ERROR,
3649  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
3650  errmsg("null array element not allowed in this context")));
3651  }
3652  else
3653  {
3654  elems[i] = fetch_att(p, elmbyval, elmlen);
3655  p = att_addlength_pointer(p, elmlen, p);
3656  p = (char *) att_align_nominal(p, elmalign);
3657  }
3658 
3659  /* advance bitmap pointer if any */
3660  if (bitmap)
3661  {
3662  bitmask <<= 1;
3663  if (bitmask == 0x100)
3664  {
3665  bitmap++;
3666  bitmask = 1;
3667  }
3668  }
3669  }
3670 }
3671 
3672 /*
3673  * Like deconstruct_array(), where elmtype must be a built-in type, and
3674  * elmlen/elmbyval/elmalign is looked up from hardcoded data. This is often
3675  * useful when manipulating arrays from/for system catalogs.
3676  */
3677 void
3679  Oid elmtype,
3680  Datum **elemsp, bool **nullsp, int *nelemsp)
3681 {
3682  int elmlen;
3683  bool elmbyval;
3684  char elmalign;
3685 
3686  switch (elmtype)
3687  {
3688  case CHAROID:
3689  elmlen = 1;
3690  elmbyval = true;
3691  elmalign = TYPALIGN_CHAR;
3692  break;
3693 
3694  case CSTRINGOID:
3695  elmlen = -2;
3696  elmbyval = false;
3697  elmalign = TYPALIGN_CHAR;
3698  break;
3699 
3700  case FLOAT8OID:
3701  elmlen = sizeof(float8);
3702  elmbyval = FLOAT8PASSBYVAL;
3703  elmalign = TYPALIGN_DOUBLE;
3704  break;
3705 
3706  case INT2OID:
3707  elmlen = sizeof(int16);
3708  elmbyval = true;
3709  elmalign = TYPALIGN_SHORT;
3710  break;
3711 
3712  case OIDOID:
3713  elmlen = sizeof(Oid);
3714  elmbyval = true;
3715  elmalign = TYPALIGN_INT;
3716  break;
3717 
3718  case TEXTOID:
3719  elmlen = -1;
3720  elmbyval = false;
3721  elmalign = TYPALIGN_INT;
3722  break;
3723 
3724  case TIDOID:
3725  elmlen = sizeof(ItemPointerData);
3726  elmbyval = false;
3727  elmalign = TYPALIGN_SHORT;
3728  break;
3729 
3730  default:
3731  elog(ERROR, "type %u not supported by deconstruct_array_builtin()", elmtype);
3732  /* keep compiler quiet */
3733  elmlen = 0;
3734  elmbyval = false;
3735  elmalign = 0;
3736  }
3737 
3738  deconstruct_array(array, elmtype, elmlen, elmbyval, elmalign, elemsp, nullsp, nelemsp);
3739 }
3740 
3741 /*
3742  * array_contains_nulls --- detect whether an array has any null elements
3743  *
3744  * This gives an accurate answer, whereas testing ARR_HASNULL only tells
3745  * if the array *might* contain a null.
3746  */
3747 bool
3749 {
3750  int nelems;
3751  bits8 *bitmap;
3752  int bitmask;
3753 
3754  /* Easy answer if there's no null bitmap */
3755  if (!ARR_HASNULL(array))
3756  return false;
3757 
3758  nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
3759 
3760  bitmap = ARR_NULLBITMAP(array);
3761 
3762  /* check whole bytes of the bitmap byte-at-a-time */
3763  while (nelems >= 8)
3764  {
3765  if (*bitmap != 0xFF)
3766  return true;
3767  bitmap++;
3768  nelems -= 8;
3769  }
3770 
3771  /* check last partial byte */
3772  bitmask = 1;
3773  while (nelems > 0)
3774  {
3775  if ((*bitmap & bitmask) == 0)
3776  return true;
3777  bitmask <<= 1;
3778  nelems--;
3779  }
3780 
3781  return false;
3782 }
3783 
3784 
3785 /*
3786  * array_eq :
3787  * compares two arrays for equality
3788  * result :
3789  * returns true if the arrays are equal, false otherwise.
3790  *
3791  * Note: we do not use array_cmp here, since equality may be meaningful in
3792  * datatypes that don't have a total ordering (and hence no btree support).
3793  */
3794 Datum
3796 {
3797  LOCAL_FCINFO(locfcinfo, 2);
3798  AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
3799  AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
3800  Oid collation = PG_GET_COLLATION();
3801  int ndims1 = AARR_NDIM(array1);
3802  int ndims2 = AARR_NDIM(array2);
3803  int *dims1 = AARR_DIMS(array1);
3804  int *dims2 = AARR_DIMS(array2);
3805  int *lbs1 = AARR_LBOUND(array1);
3806  int *lbs2 = AARR_LBOUND(array2);
3807  Oid element_type = AARR_ELEMTYPE(array1);
3808  bool result = true;
3809  int nitems;
3810  TypeCacheEntry *typentry;
3811  int typlen;
3812  bool typbyval;
3813  char typalign;
3814  array_iter it1;
3815  array_iter it2;
3816  int i;
3817 
3818  if (element_type != AARR_ELEMTYPE(array2))
3819  ereport(ERROR,
3820  (errcode(ERRCODE_DATATYPE_MISMATCH),
3821  errmsg("cannot compare arrays of different element types")));
3822 
3823  /* fast path if the arrays do not have the same dimensionality */
3824  if (ndims1 != ndims2 ||
3825  memcmp(dims1, dims2, ndims1 * sizeof(int)) != 0 ||
3826  memcmp(lbs1, lbs2, ndims1 * sizeof(int)) != 0)
3827  result = false;
3828  else
3829  {
3830  /*
3831  * We arrange to look up the equality function only once per series of
3832  * calls, assuming the element type doesn't change underneath us. The
3833  * typcache is used so that we have no memory leakage when being used
3834  * as an index support function.
3835  */
3836  typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3837  if (typentry == NULL ||
3838  typentry->type_id != element_type)
3839  {
3840  typentry = lookup_type_cache(element_type,
3842  if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
3843  ereport(ERROR,
3844  (errcode(ERRCODE_UNDEFINED_FUNCTION),
3845  errmsg("could not identify an equality operator for type %s",
3846  format_type_be(element_type))));
3847  fcinfo->flinfo->fn_extra = (void *) typentry;
3848  }
3849  typlen = typentry->typlen;
3850  typbyval = typentry->typbyval;
3851  typalign = typentry->typalign;
3852 
3853  /*
3854  * apply the operator to each pair of array elements.
3855  */
3856  InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
3857  collation, NULL, NULL);
3858 
3859  /* Loop over source data */
3860  nitems = ArrayGetNItems(ndims1, dims1);
3861  array_iter_setup(&it1, array1);
3862  array_iter_setup(&it2, array2);
3863 
3864  for (i = 0; i < nitems; i++)
3865  {
3866  Datum elt1;
3867  Datum elt2;
3868  bool isnull1;
3869  bool isnull2;
3870  bool oprresult;
3871 
3872  /* Get elements, checking for NULL */
3873  elt1 = array_iter_next(&it1, &isnull1, i,
3874  typlen, typbyval, typalign);
3875  elt2 = array_iter_next(&it2, &isnull2, i,
3876  typlen, typbyval, typalign);
3877 
3878  /*
3879  * We consider two NULLs equal; NULL and not-NULL are unequal.
3880  */
3881  if (isnull1 && isnull2)
3882  continue;
3883  if (isnull1 || isnull2)
3884  {
3885  result = false;
3886  break;
3887  }
3888 
3889  /*
3890  * Apply the operator to the element pair; treat NULL as false
3891  */
3892  locfcinfo->args[0].value = elt1;
3893  locfcinfo->args[0].isnull = false;
3894  locfcinfo->args[1].value = elt2;
3895  locfcinfo->args[1].isnull = false;
3896  locfcinfo->isnull = false;
3897  oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
3898  if (locfcinfo->isnull || !oprresult)
3899  {
3900  result = false;
3901  break;
3902  }
3903  }
3904  }
3905 
3906  /* Avoid leaking memory when handed toasted input. */
3907  AARR_FREE_IF_COPY(array1, 0);
3908  AARR_FREE_IF_COPY(array2, 1);
3909 
3910  PG_RETURN_BOOL(result);
3911 }
3912 
3913 
3914 /*-----------------------------------------------------------------------------
3915  * array-array bool operators:
3916  * Given two arrays, iterate comparison operators
3917  * over the array. Uses logic similar to text comparison
3918  * functions, except element-by-element instead of
3919  * character-by-character.
3920  *----------------------------------------------------------------------------
3921  */
3922 
3923 Datum
3925 {
3927 }
3928 
3929 Datum
3931 {
3932  PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
3933 }
3934 
3935 Datum
3937 {
3938  PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
3939 }
3940 
3941 Datum
3943 {
3944  PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
3945 }
3946 
3947 Datum
3949 {
3950  PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
3951 }
3952 
3953 Datum
3955 {
3956  PG_RETURN_INT32(array_cmp(fcinfo));
3957 }
3958 
3959 /*
3960  * array_cmp()
3961  * Internal comparison function for arrays.
3962  *
3963  * Returns -1, 0 or 1
3964  */
3965 static int
3967 {
3968  LOCAL_FCINFO(locfcinfo, 2);
3969  AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
3970  AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
3971  Oid collation = PG_GET_COLLATION();
3972  int ndims1 = AARR_NDIM(array1);
3973  int ndims2 = AARR_NDIM(array2);
3974  int *dims1 = AARR_DIMS(array1);
3975  int *dims2 = AARR_DIMS(array2);
3976  int nitems1 = ArrayGetNItems(ndims1, dims1);
3977  int nitems2 = ArrayGetNItems(ndims2, dims2);
3978  Oid element_type = AARR_ELEMTYPE(array1);
3979  int result = 0;
3980  TypeCacheEntry *typentry;
3981  int typlen;
3982  bool typbyval;
3983  char typalign;
3984  int min_nitems;
3985  array_iter it1;
3986  array_iter it2;
3987  int i;
3988 
3989  if (element_type != AARR_ELEMTYPE(array2))
3990  ereport(ERROR,
3991  (errcode(ERRCODE_DATATYPE_MISMATCH),
3992  errmsg("cannot compare arrays of different element types")));
3993 
3994  /*
3995  * We arrange to look up the comparison function only once per series of
3996  * calls, assuming the element type doesn't change underneath us. The
3997  * typcache is used so that we have no memory leakage when being used as
3998  * an index support function.
3999  */
4000  typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
4001  if (typentry == NULL ||
4002  typentry->type_id != element_type)
4003  {
4004  typentry = lookup_type_cache(element_type,
4006  if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
4007  ereport(ERROR,
4008  (errcode(ERRCODE_UNDEFINED_FUNCTION),
4009  errmsg("could not identify a comparison function for type %s",
4010  format_type_be(element_type))));
4011  fcinfo->flinfo->fn_extra = (void *) typentry;
4012  }
4013  typlen = typentry->typlen;
4014  typbyval = typentry->typbyval;
4015  typalign = typentry->typalign;
4016 
4017  /*
4018  * apply the operator to each pair of array elements.
4019  */
4020  InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
4021  collation, NULL, NULL);
4022 
4023  /* Loop over source data */
4024  min_nitems = Min(nitems1, nitems2);
4025  array_iter_setup(&it1, array1);
4026  array_iter_setup(&it2, array2);
4027 
4028  for (i = 0; i < min_nitems; i++)
4029  {
4030  Datum elt1;
4031  Datum elt2;
4032  bool isnull1;
4033  bool isnull2;
4034  int32 cmpresult;
4035 
4036  /* Get elements, checking for NULL */
4037  elt1 = array_iter_next(&it1, &isnull1, i, typlen, typbyval, typalign);
4038  elt2 = array_iter_next(&it2, &isnull2, i, typlen, typbyval, typalign);
4039 
4040  /*
4041  * We consider two NULLs equal; NULL > not-NULL.
4042  */
4043  if (isnull1 && isnull2)
4044  continue;
4045  if (isnull1)
4046  {
4047  /* arg1 is greater than arg2 */
4048  result = 1;
4049  break;
4050  }
4051  if (isnull2)
4052  {
4053  /* arg1 is less than arg2 */
4054  result = -1;
4055  break;
4056  }
4057 
4058  /* Compare the pair of elements */
4059  locfcinfo->args[0].value = elt1;
4060  locfcinfo->args[0].isnull = false;
4061  locfcinfo->args[1].value = elt2;
4062  locfcinfo->args[1].isnull = false;
4063  cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
4064 
4065  /* We don't expect comparison support functions to return null */
4066  Assert(!locfcinfo->isnull);
4067 
4068  if (cmpresult == 0)
4069  continue; /* equal */
4070 
4071  if (cmpresult < 0)
4072  {
4073  /* arg1 is less than arg2 */
4074  result = -1;
4075  break;
4076  }
4077  else
4078  {
4079  /* arg1 is greater than arg2 */
4080  result = 1;
4081  break;
4082  }
4083  }
4084 
4085  /*
4086  * If arrays contain same data (up to end of shorter one), apply
4087  * additional rules to sort by dimensionality. The relative significance
4088  * of the different bits of information is historical; mainly we just care
4089  * that we don't say "equal" for arrays of different dimensionality.
4090  */
4091  if (result == 0)
4092  {
4093  if (nitems1 != nitems2)
4094  result = (nitems1 < nitems2) ? -1 : 1;
4095  else if (ndims1 != ndims2)
4096  result = (ndims1 < ndims2) ? -1 : 1;
4097  else
4098  {
4099  for (i = 0; i < ndims1; i++)
4100  {
4101  if (dims1[i] != dims2[i])
4102  {
4103  result = (dims1[i] < dims2[i]) ? -1 : 1;
4104  break;
4105  }
4106  }
4107  if (result == 0)
4108  {
4109  int *lbound1 = AARR_LBOUND(array1);
4110  int *lbound2 = AARR_LBOUND(array2);
4111 
4112  for (i = 0; i < ndims1; i++)
4113  {
4114  if (lbound1[i] != lbound2[i])
4115  {
4116  result = (lbound1[i] < lbound2[i]) ? -1 : 1;
4117  break;
4118  }
4119  }
4120  }
4121  }
4122  }
4123 
4124  /* Avoid leaking memory when handed toasted input. */
4125  AARR_FREE_IF_COPY(array1, 0);
4126  AARR_FREE_IF_COPY(array2, 1);
4127 
4128  return result;
4129 }
4130 
4131 
4132 /*-----------------------------------------------------------------------------
4133  * array hashing
4134  * Hash the elements and combine the results.
4135  *----------------------------------------------------------------------------
4136  */
4137 
4138 Datum
4140 {
4141  LOCAL_FCINFO(locfcinfo, 1);
4142  AnyArrayType *array = PG_GETARG_ANY_ARRAY_P(0);
4143  int ndims = AARR_NDIM(array);
4144  int *dims = AARR_DIMS(array);
4145  Oid element_type = AARR_ELEMTYPE(array);
4146  uint32 result = 1;
4147  int nitems;
4148  TypeCacheEntry *typentry;
4149  int typlen;
4150  bool typbyval;
4151  char typalign;
4152  int i;
4153  array_iter iter;
4154 
4155  /*
4156  * We arrange to look up the hash function only once per series of calls,
4157  * assuming the element type doesn't change underneath us. The typcache
4158  * is used so that we have no memory leakage when being used as an index
4159  * support function.
4160  */
4161  typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
4162  if (typentry == NULL ||
4163  typentry->type_id != element_type)
4164  {
4165  typentry = lookup_type_cache(element_type,
4167  if (!OidIsValid(typentry->hash_proc_finfo.fn_oid) && element_type != RECORDOID)
4168  ereport(ERROR,
4169  (errcode(ERRCODE_UNDEFINED_FUNCTION),
4170  errmsg("could not identify a hash function for type %s",
4171  format_type_be(element_type))));
4172 
4173  /*
4174  * The type cache doesn't believe that record is hashable (see
4175  * cache_record_field_properties()), but since we're here, we're
4176  * committed to hashing, so we can assume it does. Worst case, if any
4177  * components of the record don't support hashing, we will fail at
4178  * execution.
4179  */
4180  if (element_type == RECORDOID)
4181  {
4182  MemoryContext oldcontext;
4183  TypeCacheEntry *record_typentry;
4184 
4185  oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
4186 
4187  /*
4188  * Make fake type cache entry structure. Note that we can't just
4189  * modify typentry, since that points directly into the type
4190  * cache.
4191  */
4192  record_typentry = palloc0(sizeof(*record_typentry));
4193  record_typentry->type_id = element_type;
4194 
4195  /* fill in what we need below */
4196  record_typentry->typlen = typentry->typlen;
4197  record_typentry->typbyval = typentry->typbyval;
4198  record_typentry->typalign = typentry->typalign;
4199  fmgr_info(F_HASH_RECORD, &record_typentry->hash_proc_finfo);
4200 
4201  MemoryContextSwitchTo(oldcontext);
4202 
4203  typentry = record_typentry;
4204  }
4205 
4206  fcinfo->flinfo->fn_extra = (void *) typentry;
4207  }
4208 
4209  typlen = typentry->typlen;
4210  typbyval = typentry->typbyval;
4211  typalign = typentry->typalign;
4212 
4213  /*
4214  * apply the hash function to each array element.
4215  */
4216  InitFunctionCallInfoData(*locfcinfo, &typentry->hash_proc_finfo, 1,
4217  PG_GET_COLLATION(), NULL, NULL);
4218 
4219  /* Loop over source data */
4220  nitems = ArrayGetNItems(ndims, dims);
4221  array_iter_setup(&iter, array);
4222 
4223  for (i = 0; i < nitems; i++)
4224  {
4225  Datum elt;
4226  bool isnull;
4227  uint32 elthash;
4228 
4229  /* Get element, checking for NULL */
4230  elt = array_iter_next(&iter, &isnull, i, typlen, typbyval, typalign);
4231 
4232  if (isnull)
4233  {
4234  /* Treat nulls as having hashvalue 0 */
4235  elthash = 0;
4236  }
4237  else
4238  {
4239  /* Apply the hash function */
4240  locfcinfo->args[0].value = elt;
4241  locfcinfo->args[0].isnull = false;
4242  elthash = DatumGetUInt32(FunctionCallInvoke(locfcinfo));
4243  /* We don't expect hash functions to return null */
4244  Assert(!locfcinfo->isnull);
4245  }
4246 
4247  /*
4248  * Combine hash values of successive elements by multiplying the
4249  * current value by 31 and adding on the new element's hash value.
4250  *
4251  * The result is a sum in which each element's hash value is
4252  * multiplied by a different power of 31. This is modulo 2^32
4253  * arithmetic, and the powers of 31 modulo 2^32 form a cyclic group of
4254  * order 2^27. So for arrays of up to 2^27 elements, each element's
4255  * hash value is multiplied by a different (odd) number, resulting in
4256  * a good mixing of all the elements' hash values.
4257  */
4258  result = (result << 5) - result + elthash;
4259  }
4260 
4261  /* Avoid leaking memory when handed toasted input. */
4262  AARR_FREE_IF_COPY(array, 0);
4263 
4264  PG_RETURN_UINT32(result);
4265 }
4266 
4267 /*
4268  * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
4269  * Otherwise, similar to hash_array.
4270  */
4271 Datum
4273 {
4274  LOCAL_FCINFO(locfcinfo, 2);
4275  AnyArrayType *array = PG_GETARG_ANY_ARRAY_P(0);
4276  uint64 seed = PG_GETARG_INT64(1);
4277  int ndims = AARR_NDIM(array);
4278  int *dims = AARR_DIMS(array);
4279  Oid element_type = AARR_ELEMTYPE(array);
4280  uint64 result = 1;
4281  int nitems;
4282  TypeCacheEntry *typentry;
4283  int typlen;
4284  bool typbyval;
4285  char typalign;
4286  int i;
4287  array_iter iter;
4288 
4289  typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
4290  if (typentry == NULL ||
4291  typentry->type_id != element_type)
4292  {
4293  typentry = lookup_type_cache(element_type,
4295  if (!OidIsValid(typentry->hash_extended_proc_finfo.fn_oid))
4296  ereport(ERROR,
4297  (errcode(ERRCODE_UNDEFINED_FUNCTION),
4298  errmsg("could not identify an extended hash function for type %s",
4299  format_type_be(element_type))));
4300  fcinfo->flinfo->fn_extra = (void *) typentry;
4301  }
4302  typlen = typentry->typlen;
4303  typbyval = typentry->typbyval;
4304  typalign = typentry->typalign;
4305 
4306  InitFunctionCallInfoData(*locfcinfo, &typentry->hash_extended_proc_finfo, 2,
4307  PG_GET_COLLATION(), NULL, NULL);
4308 
4309  /* Loop over source data */
4310  nitems = ArrayGetNItems(ndims, dims);
4311  array_iter_setup(&iter, array);
4312 
4313  for (i = 0; i < nitems; i++)
4314  {
4315  Datum elt;
4316  bool isnull;
4317  uint64 elthash;
4318 
4319  /* Get element, checking for NULL */
4320  elt = array_iter_next(&iter, &isnull, i, typlen, typbyval, typalign);
4321 
4322  if (isnull)
4323  {
4324  elthash = 0;
4325  }
4326  else
4327  {
4328  /* Apply the hash function */
4329  locfcinfo->args[0].value = elt;
4330  locfcinfo->args[0].isnull = false;
4331  locfcinfo->args[1].value = Int64GetDatum(seed);
4332  locfcinfo->args[1].isnull = false;
4333  elthash = DatumGetUInt64(FunctionCallInvoke(locfcinfo));
4334  /* We don't expect hash functions to return null */
4335  Assert(!locfcinfo->isnull);
4336  }
4337 
4338  result = (result << 5) - result + elthash;
4339  }
4340 
4341  AARR_FREE_IF_COPY(array, 0);
4342 
4343  PG_RETURN_UINT64(result);
4344 }
4345 
4346 
4347 /*-----------------------------------------------------------------------------
4348  * array overlap/containment comparisons
4349  * These use the same methods of comparing array elements as array_eq.
4350  * We consider only the elements of the arrays, ignoring dimensionality.
4351  *----------------------------------------------------------------------------
4352  */
4353 
4354 /*
4355  * array_contain_compare :
4356  * compares two arrays for overlap/containment
4357  *
4358  * When matchall is true, return true if all members of array1 are in array2.
4359  * When matchall is false, return true if any members of array1 are in array2.
4360  */
4361 static bool
4363  bool matchall, void **fn_extra)
4364 {
4365  LOCAL_FCINFO(locfcinfo, 2);
4366  bool result = matchall;
4367  Oid element_type = AARR_ELEMTYPE(array1);
4368  TypeCacheEntry *typentry;
4369  int nelems1;
4370  Datum *values2;
4371  bool *nulls2;
4372  int nelems2;
4373  int typlen;
4374  bool typbyval;
4375  char typalign;
4376  int i;
4377  int j;
4378  array_iter it1;
4379 
4380  if (element_type != AARR_ELEMTYPE(array2))
4381  ereport(ERROR,
4382  (errcode(ERRCODE_DATATYPE_MISMATCH),
4383  errmsg("cannot compare arrays of different element types")));
4384 
4385  /*
4386  * We arrange to look up the equality function only once per series of
4387  * calls, assuming the element type doesn't change underneath us. The
4388  * typcache is used so that we have no memory leakage when being used as
4389  * an index support function.
4390  */
4391  typentry = (TypeCacheEntry *) *fn_extra;
4392  if (typentry == NULL ||
4393  typentry->type_id != element_type)
4394  {
4395  typentry = lookup_type_cache(element_type,
4397  if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
4398  ereport(ERROR,
4399  (errcode(ERRCODE_UNDEFINED_FUNCTION),
4400  errmsg("could not identify an equality operator for type %s",
4401  format_type_be(element_type))));
4402  *fn_extra = (void *) typentry;
4403  }
4404  typlen = typentry->typlen;
4405  typbyval = typentry->typbyval;
4406  typalign = typentry->typalign;
4407 
4408  /*
4409  * Since we probably will need to scan array2 multiple times, it's
4410  * worthwhile to use deconstruct_array on it. We scan array1 the hard way
4411  * however, since we very likely won't need to look at all of it.
4412  */
4413  if (VARATT_IS_EXPANDED_HEADER(array2))
4414  {
4415  /* This should be safe even if input is read-only */
4416  deconstruct_expanded_array(&(array2->xpn));
4417  values2 = array2->xpn.dvalues;
4418  nulls2 = array2->xpn.dnulls;
4419  nelems2 = array2->xpn.nelems;
4420  }
4421  else
4422  deconstruct_array((ArrayType *) array2,
4423  element_type, typlen, typbyval, typalign,
4424  &values2, &nulls2, &nelems2);
4425 
4426  /*
4427  * Apply the comparison operator to each pair of array elements.
4428  */
4429  InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
4430  collation, NULL, NULL);
4431 
4432  /* Loop over source data */
4433  nelems1 = ArrayGetNItems(AARR_NDIM(array1), AARR_DIMS(array1));
4434  array_iter_setup(&it1, array1);
4435 
4436  for (i = 0; i < nelems1; i++)
4437  {
4438  Datum elt1;
4439  bool isnull1;
4440 
4441  /* Get element, checking for NULL */
4442  elt1 = array_iter_next(&it1, &isnull1, i, typlen, typbyval, typalign);
4443 
4444  /*
4445  * We assume that the comparison operator is strict, so a NULL can't
4446  * match anything. XXX this diverges from the "NULL=NULL" behavior of
4447  * array_eq, should we act like that?
4448  */
4449  if (isnull1)
4450  {
4451  if (matchall)
4452  {
4453  result = false;
4454  break;
4455  }
4456  continue;
4457  }
4458 
4459  for (j = 0; j < nelems2; j++)
4460  {
4461  Datum elt2 = values2[j];
4462  bool isnull2 = nulls2 ? nulls2[j] : false;
4463  bool oprresult;
4464 
4465  if (isnull2)
4466  continue; /* can't match */
4467 
4468  /*
4469  * Apply the operator to the element pair; treat NULL as false
4470  */
4471  locfcinfo->args[0].value = elt1;
4472  locfcinfo->args[0].isnull = false;
4473  locfcinfo->args[1].value = elt2;
4474  locfcinfo->args[1].isnull = false;
4475  locfcinfo->isnull = false;
4476  oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
4477  if (!locfcinfo->isnull && oprresult)
4478  break;
4479  }
4480 
4481  if (j < nelems2)
4482  {
4483  /* found a match for elt1 */
4484  if (!matchall)
4485  {
4486  result = true;
4487  break;
4488  }
4489  }
4490  else
4491  {
4492  /* no match for elt1 */
4493  if (matchall)
4494  {
4495  result = false;
4496  break;
4497  }
4498  }
4499  }
4500 
4501  return result;
4502 }
4503 
4504 Datum
4506 {
4507  AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
4508  AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
4509  Oid collation = PG_GET_COLLATION();
4510  bool result;
4511 
4512  result = array_contain_compare(array1, array2, collation, false,
4513  &fcinfo->flinfo->fn_extra);
4514 
4515  /* Avoid leaking memory when handed toasted input. */
4516  AARR_FREE_IF_COPY(array1, 0);
4517  AARR_FREE_IF_COPY(array2, 1);
4518 
4519  PG_RETURN_BOOL(result);
4520 }
4521 
4522 Datum
4524 {
4525  AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
4526  AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
4527  Oid collation = PG_GET_COLLATION();
4528  bool result;
4529 
4530  result = array_contain_compare(array2, array1, collation, true,
4531  &fcinfo->flinfo->fn_extra);
4532 
4533  /* Avoid leaking memory when handed toasted input. */
4534  AARR_FREE_IF_COPY(array1, 0);
4535  AARR_FREE_IF_COPY(array2, 1);
4536 
4537  PG_RETURN_BOOL(result);
4538 }
4539 
4540 Datum
4542 {
4543  AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
4544  AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
4545  Oid collation = PG_GET_COLLATION();
4546  bool result;
4547 
4548  result = array_contain_compare(array1, array2, collation, true,
4549  &fcinfo->flinfo->fn_extra);
4550 
4551  /* Avoid leaking memory when handed toasted input. */
4552  AARR_FREE_IF_COPY(array1, 0);
4553  AARR_FREE_IF_COPY(array2, 1);
4554 
4555  PG_RETURN_BOOL(result);
4556 }
4557 
4558 
4559 /*-----------------------------------------------------------------------------
4560  * Array iteration functions
4561  * These functions are used to iterate efficiently through arrays
4562  *-----------------------------------------------------------------------------
4563  */
4564 
4565 /*
4566  * array_create_iterator --- set up to iterate through an array
4567  *
4568  * If slice_ndim is zero, we will iterate element-by-element; the returned
4569  * datums are of the array's element type.
4570  *
4571  * If slice_ndim is 1..ARR_NDIM(arr), we will iterate by slices: the
4572  * returned datums are of the same array type as 'arr', but of size
4573  * equal to the rightmost N dimensions of 'arr'.
4574  *
4575  * The passed-in array must remain valid for the lifetime of the iterator.
4576  */
4578 array_create_iterator(ArrayType *arr, int slice_ndim, ArrayMetaState *mstate)
4579 {
4580  ArrayIterator iterator = palloc0(sizeof(ArrayIteratorData));
4581 
4582  /*
4583  * Sanity-check inputs --- caller should have got this right already
4584  */
4585  Assert(PointerIsValid(arr));
4586  if (slice_ndim < 0 || slice_ndim > ARR_NDIM(arr))
4587  elog(ERROR, "invalid arguments to array_create_iterator");
4588 
4589  /*
4590  * Remember basic info about the array and its element type
4591  */
4592  iterator->arr = arr;
4593  iterator->nullbitmap = ARR_NULLBITMAP(arr);
4594  iterator->nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
4595 
4596  if (mstate != NULL)
4597  {
4598  Assert(mstate->element_type == ARR_ELEMTYPE(arr));
4599 
4600  iterator->typlen = mstate->typlen;
4601  iterator->typbyval = mstate->typbyval;
4602  iterator->typalign = mstate->typalign;
4603  }
4604  else
4606  &iterator->typlen,
4607  &iterator->typbyval,
4608  &iterator->typalign);
4609 
4610  /*
4611  * Remember the slicing parameters.
4612  */
4613  iterator->slice_ndim = slice_ndim;
4614 
4615  if (slice_ndim > 0)
4616  {
4617  /*
4618  * Get pointers into the array's dims and lbound arrays to represent
4619  * the dims/lbound arrays of a slice. These are the same as the
4620  * rightmost N dimensions of the array.
4621  */
4622  iterator->slice_dims = ARR_DIMS(arr) + ARR_NDIM(arr) - slice_ndim;
4623  iterator->slice_lbound = ARR_LBOUND(arr) + ARR_NDIM(arr) - slice_ndim;
4624 
4625  /*
4626  * Compute number of elements in a slice.
4627  */
4628  iterator->slice_len = ArrayGetNItems(slice_ndim,
4629  iterator->slice_dims);
4630 
4631  /*
4632  * Create workspace for building sub-arrays.
4633  */
4634  iterator->slice_values = (Datum *)
4635  palloc(iterator->slice_len * sizeof(Datum));
4636  iterator->slice_nulls = (bool *)
4637  palloc(iterator->slice_len * sizeof(bool));
4638  }
4639 
4640  /*
4641  * Initialize our data pointer and linear element number. These will
4642  * advance through the array during array_iterate().
4643  */
4644  iterator->data_ptr = ARR_DATA_PTR(arr);
4645  iterator->current_item = 0;
4646 
4647  return iterator;
4648 }
4649 
4650 /*
4651  * Iterate through the array referenced by 'iterator'.
4652  *
4653  * As long as there is another element (or slice), return it into
4654  * *value / *isnull, and return true. Return false when no more data.
4655  */
4656 bool
4657 array_iterate(ArrayIterator iterator, Datum *value, bool *isnull)
4658 {
4659  /* Done if we have reached the end of the array */
4660  if (iterator->current_item >= iterator->nitems)
4661  return false;
4662 
4663  if (iterator->slice_ndim == 0)
4664  {
4665  /*
4666  * Scalar case: return one element.
4667  */
4668  if (array_get_isnull(iterator->nullbitmap, iterator->current_item++))
4669  {
4670  *isnull = true;
4671  *value = (Datum) 0;
4672  }
4673  else
4674  {
4675  /* non-NULL, so fetch the individual Datum to return */
4676  char *p = iterator->data_ptr;
4677 
4678  *isnull = false;
4679  *value = fetch_att(p, iterator->typbyval, iterator->typlen);
4680 
4681  /* Move our data pointer forward to the next element */
4682  p = att_addlength_pointer(p, iterator->typlen, p);
4683  p = (char *) att_align_nominal(p, iterator->typalign);
4684  iterator->data_ptr = p;
4685  }
4686  }
4687  else
4688  {
4689  /*
4690  * Slice case: build and return an array of the requested size.
4691  */
4692  ArrayType *result;
4693  Datum *values = iterator->slice_values;
4694  bool *nulls = iterator->slice_nulls;
4695  char *p = iterator->data_ptr;
4696  int i;
4697 
4698  for (i = 0; i < iterator->slice_len; i++)
4699  {
4700  if (array_get_isnull(iterator->nullbitmap,
4701  iterator->current_item++))
4702  {
4703  nulls[i] = true;
4704  values[i] = (Datum) 0;
4705  }
4706  else
4707  {
4708  nulls[i] = false;
4709  values[i] = fetch_att(p, iterator->typbyval, iterator->typlen);
4710 
4711  /* Move our data pointer forward to the next element */
4712  p = att_addlength_pointer(p, iterator->typlen, p);
4713  p = (char *) att_align_nominal(p, iterator->typalign);
4714  }
4715  }
4716 
4717  iterator->data_ptr = p;
4718 
4719  result = construct_md_array(values,
4720  nulls,
4721  iterator->slice_ndim,
4722  iterator->slice_dims,
4723  iterator->slice_lbound,
4724  ARR_ELEMTYPE(iterator->arr),
4725  iterator->typlen,
4726  iterator->typbyval,
4727  iterator->typalign);
4728 
4729  *isnull = false;
4730  *value = PointerGetDatum(result);
4731  }
4732 
4733  return true;
4734 }
4735 
4736 /*
4737  * Release an ArrayIterator data structure
4738  */
4739 void
4741 {
4742  if (iterator->slice_ndim > 0)
4743  {
4744  pfree(iterator->slice_values);
4745  pfree(iterator->slice_nulls);
4746  }
4747  pfree(iterator);
4748 }
4749 
4750 
4751 /***************************************************************************/
4752 /******************| Support Routines |*****************/
4753 /***************************************************************************/
4754 
4755 /*
4756  * Check whether a specific array element is NULL
4757  *
4758  * nullbitmap: pointer to array's null bitmap (NULL if none)
4759  * offset: 0-based linear element number of array element
4760  */
4761 static bool
4762 array_get_isnull(const bits8 *nullbitmap, int offset)
4763 {
4764  if (nullbitmap == NULL)
4765  return false; /* assume not null */
4766  if (nullbitmap[offset / 8] & (1 << (offset % 8)))
4767  return false; /* not null */
4768  return true;
4769 }
4770 
4771 /*
4772  * Set a specific array element's null-bitmap entry
4773  *
4774  * nullbitmap: pointer to array's null bitmap (mustn't be NULL)
4775  * offset: 0-based linear element number of array element
4776  * isNull: null status to set
4777  */
4778 static void
4779 array_set_isnull(bits8 *nullbitmap, int offset, bool isNull)
4780 {
4781  int bitmask;
4782 
4783  nullbitmap += offset / 8;
4784  bitmask = 1 << (offset % 8);
4785  if (isNull)
4786  *nullbitmap &= ~bitmask;
4787  else
4788  *nullbitmap |= bitmask;
4789 }
4790 
4791 /*
4792  * Fetch array element at pointer, converted correctly to a Datum
4793  *
4794  * Caller must have handled case of NULL element
4795  */
4796 static Datum
4797 ArrayCast(char *value, bool byval, int len)
4798 {
4799  return fetch_att(value, byval, len);
4800 }
4801 
4802 /*
4803  * Copy datum to *dest and return total space used (including align padding)
4804  *
4805  * Caller must have handled case of NULL element
4806  */
4807 static int
4809  int typlen,
4810  bool typbyval,
4811  char typalign,
4812  char *dest)
4813 {
4814  int inc;
4815 
4816  if (typlen > 0)
4817  {
4818  if (typbyval)
4819  store_att_byval(dest, src, typlen);
4820  else
4821  memmove(dest, DatumGetPointer(src), typlen);
4822  inc = att_align_nominal(typlen, typalign);
4823  }
4824  else
4825  {
4826  Assert(!typbyval);
4827  inc = att_addlength_datum(0, typlen, src);
4828  memmove(dest, DatumGetPointer(src), inc);
4829  inc = att_align_nominal(inc, typalign);
4830  }
4831 
4832  return inc;
4833 }
4834 
4835 /*
4836  * Advance ptr over nitems array elements
4837  *
4838  * ptr: starting location in array
4839  * offset: 0-based linear element number of first element (the one at *ptr)
4840  * nullbitmap: start of array's null bitmap, or NULL if none
4841  * nitems: number of array elements to advance over (>= 0)
4842  * typlen, typbyval, typalign: storage parameters of array element datatype
4843  *
4844  * It is caller's responsibility to ensure that nitems is within range
4845  */
4846 static char *
4847 array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
4848  int typlen, bool typbyval, char typalign)
4849 {
4850  int bitmask;
4851  int i;
4852 
4853  /* easy if fixed-size elements and no NULLs */
4854  if (typlen > 0 && !nullbitmap)
4855  return ptr + nitems * ((Size) att_align_nominal(typlen, typalign));
4856 
4857  /* seems worth having separate loops for NULL and no-NULLs cases */
4858  if (nullbitmap)
4859  {
4860  nullbitmap += offset / 8;
4861  bitmask = 1 << (offset % 8);
4862 
4863  for (i = 0; i < nitems; i++)
4864  {
4865  if (*nullbitmap & bitmask)
4866  {
4867  ptr = att_addlength_pointer(ptr, typlen, ptr);
4868  ptr = (char *) att_align_nominal(ptr, typalign);
4869  }
4870  bitmask <<= 1;
4871  if (bitmask == 0x100)
4872  {
4873  nullbitmap++;
4874  bitmask = 1;
4875  }
4876  }
4877  }
4878  else
4879  {
4880  for (i = 0; i < nitems; i++)
4881  {
4882  ptr = att_addlength_pointer(ptr, typlen, ptr);
4883  ptr = (char *) att_align_nominal(ptr, typalign);
4884  }
4885  }
4886  return ptr;
4887 }
4888 
4889 /*
4890  * Compute total size of the nitems array elements starting at *ptr
4891  *
4892  * Parameters same as for array_seek
4893  */
4894 static int
4895 array_nelems_size(char *ptr, int offset, bits8 *nullbitmap, int nitems,
4896  int typlen, bool typbyval, char typalign)
4897 {
4898  return array_seek(ptr, offset, nullbitmap, nitems,
4899  typlen, typbyval, typalign) - ptr;
4900 }
4901 
4902 /*
4903  * Copy nitems array elements from srcptr to destptr
4904  *
4905  * destptr: starting destination location (must be enough room!)
4906  * nitems: number of array elements to copy (>= 0)
4907  * srcptr: starting location in source array
4908  * offset: 0-based linear element number of first element (the one at *srcptr)
4909  * nullbitmap: start of source array's null bitmap, or NULL if none
4910  * typlen, typbyval, typalign: storage parameters of array element datatype
4911  *
4912  * Returns number of bytes copied
4913  *
4914  * NB: this does not take care of setting up the destination's null bitmap!
4915  */
4916 static int
4917 array_copy(char *destptr, int nitems,
4918  char *srcptr, int offset, bits8 *nullbitmap,
4919  int typlen, bool typbyval, char typalign)
4920 {
4921  int numbytes;
4922 
4923  numbytes = array_nelems_size(srcptr, offset, nullbitmap, nitems,
4924  typlen, typbyval, typalign);
4925  memcpy(destptr, srcptr, numbytes);
4926  return numbytes;
4927 }
4928 
4929 /*
4930  * Copy nitems null-bitmap bits from source to destination
4931  *
4932  * destbitmap: start of destination array's null bitmap (mustn't be NULL)
4933  * destoffset: 0-based linear element number of first dest element
4934  * srcbitmap: start of source array's null bitmap, or NULL if none
4935  * srcoffset: 0-based linear element number of first source element
4936  * nitems: number of bits to copy (>= 0)
4937  *
4938  * If srcbitmap is NULL then we assume the source is all-non-NULL and
4939  * fill 1's into the destination bitmap. Note that only the specified
4940  * bits in the destination map are changed, not any before or after.
4941  *
4942  * Note: this could certainly be optimized using standard bitblt methods.
4943  * However, it's not clear that the typical Postgres array has enough elements
4944  * to make it worth worrying too much. For the moment, KISS.
4945  */
4946 void
4947 array_bitmap_copy(bits8 *destbitmap, int destoffset,
4948  const bits8 *srcbitmap, int srcoffset,
4949  int nitems)
4950 {
4951  int destbitmask,
4952  destbitval,
4953  srcbitmask,
4954  srcbitval;
4955 
4956  Assert(destbitmap);
4957  if (nitems <= 0)
4958  return; /* don't risk fetch off end of memory */
4959  destbitmap += destoffset / 8;
4960  destbitmask = 1 << (destoffset % 8);
4961  destbitval = *destbitmap;
4962  if (srcbitmap)
4963  {
4964  srcbitmap += srcoffset / 8;
4965  srcbitmask = 1 << (srcoffset % 8);
4966  srcbitval = *srcbitmap;
4967  while (nitems-- > 0)
4968  {
4969  if (srcbitval & srcbitmask)
4970  destbitval |= destbitmask;
4971  else
4972  destbitval &= ~destbitmask;
4973  destbitmask <<= 1;
4974  if (destbitmask == 0x100)
4975  {
4976  *destbitmap++ = destbitval;
4977  destbitmask = 1;
4978  if (nitems > 0)
4979  destbitval = *destbitmap;
4980  }
4981  srcbitmask <<= 1;
4982  if (srcbitmask == 0x100)
4983  {
4984  srcbitmap++;
4985  srcbitmask = 1;
4986  if (nitems > 0)
4987  srcbitval = *srcbitmap;
4988  }
4989  }
4990  if (destbitmask != 1)
4991  *destbitmap = destbitval;
4992  }
4993  else
4994  {
4995  while (nitems-- > 0)
4996  {
4997  destbitval |= destbitmask;
4998  destbitmask <<= 1;
4999  if (destbitmask == 0x100)
5000  {
5001  *destbitmap++ = destbitval;
5002  destbitmask = 1;
5003  if (nitems > 0)
5004  destbitval = *destbitmap;
5005  }
5006  }
5007  if (destbitmask != 1)
5008  *destbitmap = destbitval;
5009  }
5010 }
5011 
5012 /*
5013  * Compute space needed for a slice of an array
5014  *
5015  * We assume the caller has verified that the slice coordinates are valid.
5016  */
5017 static int
5018 array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
5019  int ndim, int *dim, int *lb,
5020  int *st, int *endp,
5021  int typlen, bool typbyval, char typalign)
5022 {
5023  int src_offset,
5024  span[MAXDIM],
5025  prod[MAXDIM],
5026  dist[MAXDIM],
5027  indx[MAXDIM];
5028  char *ptr;
5029  int i,
5030  j,
5031  inc;
5032  int count = 0;
5033 
5034  mda_get_range(ndim, span, st, endp);
5035 
5036  /* Pretty easy for fixed element length without nulls ... */
5037  if (typlen > 0 && !arraynullsptr)
5038  return ArrayGetNItems(ndim, span) * att_align_nominal(typlen, typalign);
5039 
5040  /* Else gotta do it the hard way */
5041  src_offset = ArrayGetOffset(ndim, dim, lb, st);
5042  ptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
5043  typlen, typbyval, typalign);
5044  mda_get_prod(ndim, dim, prod);
5045  mda_get_offset_values(ndim, dist, prod, span);
5046  for (i = 0; i < ndim; i++)
5047  indx[i] = 0;
5048  j = ndim - 1;
5049  do
5050  {
5051  if (dist[j])
5052  {
5053  ptr = array_seek(ptr, src_offset, arraynullsptr, dist[j],
5054  typlen, typbyval, typalign);
5055  src_offset += dist[j];
5056  }
5057  if (!array_get_isnull(arraynullsptr, src_offset))
5058  {
5059  inc = att_addlength_pointer(0, typlen, ptr);
5060  inc = att_align_nominal(inc, typalign);
5061  ptr += inc;
5062  count += inc;
5063  }
5064  src_offset++;
5065  } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
5066  return count;
5067 }
5068 
5069 /*
5070  * Extract a slice of an array into consecutive elements in the destination
5071  * array.
5072  *
5073  * We assume the caller has verified that the slice coordinates are valid,
5074  * allocated enough storage for the result, and initialized the header
5075  * of the new array.
5076  */
5077 static void
5079  int ndim,
5080  int *dim,
5081  int *lb,
5082  char *arraydataptr,
5083  bits8 *arraynullsptr,
5084  int *st,
5085  int *endp,
5086  int typlen,
5087  bool typbyval,
5088  char typalign)
5089 {
5090  char *destdataptr = ARR_DATA_PTR(newarray);
5091  bits8 *destnullsptr = ARR_NULLBITMAP(newarray);
5092  char *srcdataptr;
5093  int src_offset,
5094  dest_offset,
5095  prod[MAXDIM],
5096  span[MAXDIM],
5097  dist[MAXDIM],
5098  indx[MAXDIM];
5099  int i,
5100  j,
5101  inc;
5102 
5103  src_offset = ArrayGetOffset(ndim, dim, lb, st);
5104  srcdataptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
5105  typlen, typbyval, typalign);
5106  mda_get_prod(ndim, dim, prod);
5107  mda_get_range(ndim, span, st, endp);
5108  mda_get_offset_values(ndim, dist, prod, span);
5109  for (i = 0; i < ndim; i++)
5110  indx[i] = 0;
5111  dest_offset = 0;
5112  j = ndim - 1;
5113  do
5114  {
5115  if (dist[j])
5116  {
5117  /* skip unwanted elements */
5118  srcdataptr = array_seek(srcdataptr, src_offset, arraynullsptr,
5119  dist[j],
5120  typlen, typbyval, typalign);
5121  src_offset += dist[j];
5122  }
5123  inc = array_copy(destdataptr, 1,
5124  srcdataptr, src_offset, arraynullsptr,
5125  typlen, typbyval, typalign);
5126  if (destnullsptr)
5127  array_bitmap_copy(destnullsptr, dest_offset,
5128  arraynullsptr, src_offset,
5129  1);
5130  destdataptr += inc;
5131  srcdataptr += inc;
5132  src_offset++;
5133  dest_offset++;
5134  } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
5135 }
5136 
5137 /*
5138  * Insert a slice into an array.
5139  *
5140  * ndim/dim[]/lb[] are dimensions of the original array. A new array with
5141  * those same dimensions is to be constructed. destArray must already
5142  * have been allocated and its header initialized.
5143  *
5144  * st[]/endp[] identify the slice to be replaced. Elements within the slice
5145  * volume are taken from consecutive elements of the srcArray; elements
5146  * outside it are copied from origArray.
5147  *
5148  * We assume the caller has verified that the slice coordinates are valid.
5149  */
5150 static void
5152  ArrayType *origArray,
5153  ArrayType *srcArray,
5154  int ndim,
5155  int *dim,
5156  int *lb,
5157  int *st,
5158  int *endp,
5159  int typlen,
5160  bool typbyval,
5161  char typalign)
5162 {
5163  char *destPtr = ARR_DATA_PTR(destArray);
5164  char *origPtr = ARR_DATA_PTR(origArray);
5165  char *srcPtr = ARR_DATA_PTR(srcArray);
5166  bits8 *destBitmap = ARR_NULLBITMAP(destArray);
5167  bits8 *origBitmap = ARR_NULLBITMAP(origArray);
5168  bits8 *srcBitmap = ARR_NULLBITMAP(srcArray);
5169  int orignitems = ArrayGetNItems(ARR_NDIM(origArray),
5170  ARR_DIMS(origArray));
5171  int dest_offset,
5172  orig_offset,
5173  src_offset,
5174  prod[MAXDIM],
5175  span[MAXDIM],
5176  dist[MAXDIM],
5177  indx[MAXDIM];
5178  int i,
5179  j,
5180  inc;
5181 
5182  dest_offset = ArrayGetOffset(ndim, dim, lb, st);
5183  /* copy items before the slice start */
5184  inc = array_copy(destPtr, dest_offset,
5185  origPtr, 0, origBitmap,
5186  typlen, typbyval, typalign);
5187  destPtr += inc;
5188  origPtr += inc;
5189  if (destBitmap)
5190  array_bitmap_copy(destBitmap, 0, origBitmap, 0, dest_offset);
5191  orig_offset = dest_offset;
5192  mda_get_prod(ndim, dim, prod);
5193  mda_get_range(ndim, span, st, endp);
5194  mda_get_offset_values(ndim, dist, prod, span);
5195  for (i = 0; i < ndim; i++)
5196  indx[i] = 0;
5197  src_offset = 0;
5198  j = ndim - 1;
5199  do
5200  {
5201  /* Copy/advance over elements between here and next part of slice */
5202  if (dist[j])
5203  {
5204  inc = array_copy(destPtr, dist[j],
5205  origPtr, orig_offset, origBitmap,
5206  typlen, typbyval, typalign);
5207  destPtr += inc;
5208  origPtr += inc;
5209  if (destBitmap)
5210  array_bitmap_copy(destBitmap, dest_offset,
5211  origBitmap, orig_offset,
5212  dist[j]);
5213  dest_offset += dist[j];
5214  orig_offset += dist[j];
5215  }
5216  /* Copy new element at this slice position */
5217  inc = array_copy(destPtr, 1,
5218  srcPtr, src_offset, srcBitmap,
5219  typlen, typbyval, typalign);
5220  if (destBitmap)
5221  array_bitmap_copy(destBitmap, dest_offset,
5222  srcBitmap, src_offset,
5223  1);
5224  destPtr += inc;
5225  srcPtr += inc;
5226  dest_offset++;
5227  src_offset++;
5228  /* Advance over old element at this slice position */
5229  origPtr = array_seek(origPtr, orig_offset, origBitmap, 1,
5230  typlen, typbyval, typalign);
5231  orig_offset++;
5232  } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
5233 
5234  /* don't miss any data at the end */
5235  array_copy(destPtr, orignitems - orig_offset,
5236  origPtr, orig_offset, origBitmap,
5237  typlen, typbyval, typalign);
5238  if (destBitmap)
5239  array_bitmap_copy(destBitmap, dest_offset,
5240  origBitmap, orig_offset,
5241  orignitems - orig_offset);
5242 }
5243 
5244 /*
5245  * initArrayResult - initialize an empty ArrayBuildState
5246  *
5247  * element_type is the array element type (must be a valid array element type)
5248  * rcontext is where to keep working state
5249  * subcontext is a flag determining whether to use a separate memory context
5250  *
5251  * Note: there are two common schemes for using accumArrayResult().
5252  * In the older scheme, you start with a NULL ArrayBuildState pointer, and
5253  * call accumArrayResult once per element. In this scheme you end up with
5254  * a NULL pointer if there were no elements, which you need to special-case.
5255  * In the newer scheme, call initArrayResult and then call accumArrayResult
5256  * once per element. In this scheme you always end with a non-NULL pointer
5257  * that you can pass to makeArrayResult; you get an empty array if there
5258  * were no elements. This is preferred if an empty array is what you want.
5259  *
5260  * It's possible to choose whether to create a separate memory context for the
5261  * array build state, or whether to allocate it directly within rcontext.
5262  *
5263  * When there are many concurrent small states (e.g. array_agg() using hash
5264  * aggregation of many small groups), using a separate memory context for each
5265  * one may result in severe memory bloat. In such cases, use the same memory
5266  * context to initialize all such array build states, and pass
5267  * subcontext=false.
5268  *
5269  * In cases when the array build states have different lifetimes, using a
5270  * single memory context is impractical. Instead, pass subcontext=true so that
5271  * the array build states can be freed individually.
5272  */
5274 initArrayResult(Oid element_type, MemoryContext rcontext, bool subcontext)
5275 {
5276  /*
5277  * When using a subcontext, we can afford to start with a somewhat larger
5278  * initial array size. Without subcontexts, we'd better hope that most of
5279  * the states stay small ...
5280  */
5281  return initArrayResultWithSize(element_type, rcontext, subcontext,
5282  subcontext ? 64 : 8);
5283 }
5284 
5285 /*
5286  * initArrayResultWithSize
5287  * As initArrayResult, but allow the initial size of the allocated arrays
5288  * to be specified.
5289  */
5292  bool subcontext, int initsize)
5293 {
5294  ArrayBuildState *astate;
5295  MemoryContext arr_context = rcontext;
5296 
5297  /* Make a temporary context to hold all the junk */
5298  if (subcontext)
5299  arr_context = AllocSetContextCreate(rcontext,
5300  "accumArrayResult",
5302 
5303  astate = (ArrayBuildState *)
5304  MemoryContextAlloc(arr_context, sizeof(ArrayBuildState));
5305  astate->mcontext = arr_context;
5306  astate->private_cxt = subcontext;
5307  astate->alen = initsize;
5308  astate->dvalues = (Datum *)
5309  MemoryContextAlloc(arr_context, astate->alen * sizeof(Datum));
5310  astate->dnulls = (bool *)
5311  MemoryContextAlloc(arr_context, astate->alen * sizeof(bool));
5312  astate->nelems = 0;
5313  astate->element_type = element_type;
5314  get_typlenbyvalalign(element_type,
5315  &astate->typlen,
5316  &astate->typbyval,
5317  &astate->typalign);
5318 
5319  return astate;
5320 }
5321 
5322 /*
5323  * accumArrayResult - accumulate one (more) Datum for an array result
5324  *
5325  * astate is working state (can be NULL on first call)
5326  * dvalue/disnull represent the new Datum to append to the array
5327  * element_type is the Datum's type (must be a valid array element type)
5328  * rcontext is where to keep working state
5329  */
5332  Datum dvalue, bool disnull,
5333  Oid element_type,
5334  MemoryContext rcontext)
5335 {
5336  MemoryContext oldcontext;
5337 
5338  if (astate == NULL)
5339  {
5340  /* First time through --- initialize */
5341  astate = initArrayResult(element_type, rcontext, true);
5342  }
5343  else
5344  {
5345  Assert(astate->element_type == element_type);
5346  }
5347 
5348  oldcontext = MemoryContextSwitchTo(astate->mcontext);
5349 
5350  /* enlarge dvalues[]/dnulls[] if needed */
5351  if (astate->nelems >= astate->alen)
5352  {
5353  astate->alen *= 2;
5354  /* give an array-related error if we go past MaxAllocSize */
5355  if (!AllocSizeIsValid(astate->alen * sizeof(Datum)))
5356  ereport(ERROR,
5357  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
5358  errmsg("array size exceeds the maximum allowed (%d)",
5359  (int) MaxAllocSize)));
5360  astate->dvalues = (Datum *)
5361  repalloc(astate->dvalues, astate->alen * sizeof(Datum));
5362  astate->dnulls = (bool *)
5363  repalloc(astate->dnulls, astate->alen * sizeof(bool));
5364  }
5365 
5366  /*
5367  * Ensure pass-by-ref stuff is copied into mcontext; and detoast it too if
5368  * it's varlena. (You might think that detoasting is not needed here
5369  * because construct_md_array can detoast the array elements later.
5370  * However, we must not let construct_md_array modify the ArrayBuildState
5371  * because that would mean array_agg_finalfn damages its input, which is
5372  * verboten. Also, this way frequently saves one copying step.)
5373  */
5374  if (!disnull && !astate->typbyval)
5375  {
5376  if (astate->typlen == -1)
5377  dvalue = PointerGetDatum(PG_DETOAST_DATUM_COPY(dvalue));
5378  else
5379  dvalue = datumCopy(dvalue, astate->typbyval, astate->typlen);
5380  }
5381 
5382  astate->dvalues[astate->nelems] = dvalue;
5383  astate->dnulls[astate->nelems] = disnull;
5384  astate->nelems++;
5385 
5386  MemoryContextSwitchTo(oldcontext);
5387 
5388  return astate;
5389 }
5390 
5391 /*
5392  * makeArrayResult - produce 1-D final result of accumArrayResult
5393  *
5394  * Note: only releases astate if it was initialized within a separate memory
5395  * context (i.e. using subcontext=true when calling initArrayResult).
5396  *
5397  * astate is working state (must not be NULL)
5398  * rcontext is where to construct result
5399  */
5400 Datum
5402  MemoryContext rcontext)
5403 {
5404  int ndims;
5405  int dims[1];
5406  int lbs[1];
5407 
5408  /* If no elements were presented, we want to create an empty array */
5409  ndims = (astate->nelems > 0) ? 1 : 0;
5410  dims[0] = astate->nelems;
5411  lbs[0] = 1;
5412 
5413  return makeMdArrayResult(astate, ndims, dims, lbs, rcontext,
5414  astate->private_cxt);
5415 }
5416 
5417 /*
5418  * makeMdArrayResult - produce multi-D final result of accumArrayResult
5419  *
5420  * beware: no check that specified dimensions match the number of values
5421  * accumulated.
5422  *
5423  * Note: if the astate was not initialized within a separate memory context
5424  * (that is, initArrayResult was called with subcontext=false), then using
5425  * release=true is illegal. Instead, release astate along with the rest of its
5426  * context when appropriate.
5427  *
5428  * astate is working state (must not be NULL)
5429  * rcontext is where to construct result
5430  * release is true if okay to release working state
5431  */
5432 Datum
5434  int ndims,
5435  int *dims,
5436  int *lbs,
5437  MemoryContext rcontext,
5438  bool release)
5439 {
5440  ArrayType *result;
5441  MemoryContext oldcontext;
5442 
5443  /* Build the final array result in rcontext */
5444  oldcontext = MemoryContextSwitchTo(rcontext);
5445 
5446  result = construct_md_array(astate->dvalues,
5447  astate->dnulls,
5448  ndims,
5449  dims,
5450  lbs,
5451  astate->element_type,
5452  astate->typlen,
5453  astate->typbyval,
5454  astate->typalign);
5455 
5456  MemoryContextSwitchTo(oldcontext);
5457 
5458  /* Clean up all the junk */
5459  if (release)
5460  {
5461  Assert(astate->private_cxt);
5462  MemoryContextDelete(astate->mcontext);
5463  }
5464 
5465  return PointerGetDatum(result);
5466 }
5467 
5468 /*
5469  * The following three functions provide essentially the same API as
5470  * initArrayResult/accumArrayResult/makeArrayResult, but instead of accepting
5471  * inputs that are array elements, they accept inputs that are arrays and
5472  * produce an output array having N+1 dimensions. The inputs must all have
5473  * identical dimensionality as well as element type.
5474  */
5475 
5476 /*
5477  * initArrayResultArr - initialize an empty ArrayBuildStateArr
5478  *
5479  * array_type is the array type (must be a valid varlena array type)
5480  * element_type is the type of the array's elements (lookup if InvalidOid)
5481  * rcontext is where to keep working state
5482  * subcontext is a flag determining whether to use a separate memory context
5483  */
5485 initArrayResultArr(Oid array_type, Oid element_type, MemoryContext rcontext,
5486  bool subcontext)
5487 {
5488  ArrayBuildStateArr *astate;
5489  MemoryContext arr_context = rcontext; /* by default use the parent ctx */
5490 
5491  /* Lookup element type, unless element_type already provided */
5492  if (!OidIsValid(element_type))
5493  {
5494  element_type = get_element_type(array_type);
5495 
5496  if (!OidIsValid(element_type))
5497  ereport(ERROR,
5498  (errcode(ERRCODE_DATATYPE_MISMATCH),
5499  errmsg("data type %s is not an array type",
5500  format_type_be(array_type))));
5501  }
5502 
5503  /* Make a temporary context to hold all the junk */
5504  if (subcontext)
5505  arr_context = AllocSetContextCreate(rcontext,
5506  "accumArrayResultArr",
5508 
5509  /* Note we initialize all fields to zero */
5510  astate = (ArrayBuildStateArr *)
5511  MemoryContextAllocZero(arr_context, sizeof(ArrayBuildStateArr));
5512  astate->mcontext = arr_context;
5513  astate->private_cxt = subcontext;
5514 
5515  /* Save relevant datatype information */
5516  astate->array_type = array_type;
5517  astate->element_type = element_type;
5518 
5519  return astate;
5520 }
5521 
5522 /*
5523  * accumArrayResultArr - accumulate one (more) sub-array for an array result
5524  *
5525  * astate is working state (can be NULL on first call)
5526  * dvalue/disnull represent the new sub-array to append to the array
5527  * array_type is the array type (must be a valid varlena array type)
5528  * rcontext is where to keep working state
5529  */
5532  Datum dvalue, bool disnull,
5533  Oid array_type,
5534  MemoryContext rcontext)
5535 {
5536  ArrayType *arg;
5537  MemoryContext oldcontext;
5538  int *dims,
5539  *lbs,
5540  ndims,
5541  nitems,
5542  ndatabytes;
5543  char *data;
5544  int i;
5545 
5546  /*
5547  * We disallow accumulating null subarrays. Another plausible definition
5548  * is to ignore them, but callers that want that can just skip calling
5549  * this function.
5550  */
5551  if (disnull)
5552  ereport(ERROR,
5553  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5554  errmsg("cannot accumulate null arrays")));
5555 
5556  /* Detoast input array in caller's context */
5557  arg = DatumGetArrayTypeP(dvalue);
5558 
5559  if (astate == NULL)
5560  astate = initArrayResultArr(array_type, InvalidOid, rcontext, true);
5561  else
5562  Assert(astate->array_type == array_type);
5563 
5564  oldcontext = MemoryContextSwitchTo(astate->mcontext);
5565 
5566  /* Collect this input's dimensions */
5567  ndims = ARR_NDIM(arg);
5568  dims = ARR_DIMS(arg);
5569  lbs = ARR_LBOUND(arg);
5570  data = ARR_DATA_PTR(arg);
5571  nitems = ArrayGetNItems(ndims, dims);
5572  ndatabytes = ARR_SIZE(arg) - ARR_DATA_OFFSET(arg);
5573 
5574  if (astate->ndims == 0)
5575  {
5576  /* First input; check/save the dimensionality info */
5577 
5578  /* Should we allow empty inputs and just produce an empty output? */
5579  if (ndims == 0)
5580  ereport(ERROR,
5581  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5582  errmsg("cannot accumulate empty arrays")));
5583  if (ndims + 1 > MAXDIM)
5584  ereport(ERROR,
5585  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
5586  errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
5587  ndims + 1, MAXDIM)));
5588 
5589  /*
5590  * The output array will have n+1 dimensions, with the ones after the
5591  * first matching the input's dimensions.
5592  */
5593  astate->ndims = ndims + 1;
5594  astate->dims[0] = 0;
5595  memcpy(&astate->dims[1], dims, ndims * sizeof(int));
5596  astate->lbs[0] = 1;
5597  memcpy(&astate->lbs[1], lbs, ndims * sizeof(int));
5598 
5599  /* Allocate at least enough data space for this item */
5600  astate->abytes = pg_nextpower2_32(Max(1024, ndatabytes + 1));
5601  astate->data = (char *) palloc(astate->abytes);
5602  }
5603  else
5604  {
5605  /* Second or later input: must match first input's dimensionality */
5606  if (astate->ndims != ndims + 1)
5607  ereport(ERROR,
5608  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5609  errmsg("cannot accumulate arrays of different dimensionality")));
5610  for (i = 0; i < ndims; i++)
5611  {
5612  if (astate->dims[i + 1] != dims[i] || astate->lbs[i + 1] != lbs[i])
5613  ereport(ERROR,
5614  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5615  errmsg("cannot accumulate arrays of different dimensionality")));
5616  }
5617 
5618  /* Enlarge data space if needed */
5619  if (astate->nbytes + ndatabytes > astate->abytes)
5620  {
5621  astate->abytes = Max(astate->abytes * 2,
5622  astate->nbytes + ndatabytes);
5623  astate->data = (char *) repalloc(astate->data, astate->abytes);
5624  }
5625  }
5626 
5627  /*
5628  * Copy the data portion of the sub-array. Note we assume that the
5629  * advertised data length of the sub-array is properly aligned. We do not
5630  * have to worry about detoasting elements since whatever's in the
5631  * sub-array should be OK already.
5632  */
5633  memcpy(astate->data + astate->nbytes, data, ndatabytes);
5634  astate->nbytes += ndatabytes;
5635 
5636  /* Deal with null bitmap if needed */
5637  if (astate->nullbitmap || ARR_HASNULL(arg))
5638  {
5639  int newnitems = astate->nitems + nitems;
5640 
5641  if (astate->nullbitmap == NULL)
5642  {
5643  /*
5644  * First input with nulls; we must retrospectively handle any
5645  * previous inputs by marking all their items non-null.
5646  */
5647  astate->aitems = pg_nextpower2_32(Max(256, newnitems + 1));
5648  astate->nullbitmap = (bits8 *) palloc((astate->aitems + 7) / 8);
5649  array_bitmap_copy(astate->nullbitmap, 0,
5650  NULL, 0,
5651  astate->nitems);
5652  }
5653  else if (newnitems > astate->aitems)
5654  {
5655  astate->aitems = Max(astate->aitems * 2, newnitems);
5656  astate->nullbitmap = (bits8 *)
5657  repalloc(astate->nullbitmap, (astate->aitems + 7) / 8);
5658  }
5659  array_bitmap_copy(astate->nullbitmap, astate->nitems,
5660  ARR_NULLBITMAP(arg), 0,
5661  nitems);
5662  }
5663 
5664  astate->nitems += nitems;
5665  astate->dims[0] += 1;
5666 
5667  MemoryContextSwitchTo(oldcontext);
5668 
5669  /* Release detoasted copy if any */
5670  if ((Pointer) arg != DatumGetPointer(dvalue))
5671  pfree(arg);
5672 
5673  return astate;
5674 }
5675 
5676 /*
5677  * makeArrayResultArr - produce N+1-D final result of accumArrayResultArr
5678  *
5679  * astate is working state (must not be NULL)
5680  * rcontext is where to construct result
5681  * release is true if okay to release working state
5682  */
5683 Datum
5685  MemoryContext rcontext,
5686  bool release)
5687 {
5688  ArrayType *result;
5689  MemoryContext oldcontext;
5690 
5691  /* Build the final array result in rcontext */
5692  oldcontext = MemoryContextSwitchTo(rcontext);
5693 
5694  if (astate->ndims == 0)
5695  {
5696  /* No inputs, return empty array */
5697  result = construct_empty_array(astate->element_type);
5698  }
5699  else
5700  {
5701  int dataoffset,
5702  nbytes;
5703 
5704  /* Check for overflow of the array dimensions */
5705  (void) ArrayGetNItems(astate->ndims, astate->dims);
5706  ArrayCheckBounds(astate->ndims, astate->dims, astate->lbs);
5707 
5708  /* Compute required space */
5709  nbytes = astate->nbytes;
5710  if (astate->nullbitmap != NULL)
5711  {
5712  dataoffset = ARR_OVERHEAD_WITHNULLS(astate->ndims, astate->nitems);
5713  nbytes += dataoffset;
5714  }
5715  else
5716  {
5717  dataoffset = 0;
5718  nbytes += ARR_OVERHEAD_NONULLS(astate->ndims);
5719  }
5720 
5721  result = (ArrayType *) palloc0(nbytes);
5722  SET_VARSIZE(result, nbytes);
5723  result->ndim = astate->ndims;
5724  result->dataoffset = dataoffset;
5725  result->elemtype = astate->element_type;
5726 
5727  memcpy(ARR_DIMS(result), astate->dims, astate->ndims * sizeof(int));
5728  memcpy(ARR_LBOUND(result), astate->lbs, astate->ndims * sizeof(int));
5729  memcpy(ARR_DATA_PTR(result), astate->data, astate->nbytes);
5730 
5731  if (astate->nullbitmap != NULL)
5732  array_bitmap_copy(ARR_NULLBITMAP(result), 0,
5733  astate->nullbitmap, 0,
5734  astate->nitems);
5735  }
5736 
5737  MemoryContextSwitchTo(oldcontext);
5738 
5739  /* Clean up all the junk */
5740  if (release)
5741  {
5742  Assert(astate->private_cxt);
5743  MemoryContextDelete(astate->mcontext);
5744  }
5745 
5746  return PointerGetDatum(result);
5747 }
5748 
5749 /*
5750  * The following three functions provide essentially the same API as
5751  * initArrayResult/accumArrayResult/makeArrayResult, but can accept either
5752  * scalar or array inputs, invoking the appropriate set of functions above.
5753  */
5754 
5755 /*
5756  * initArrayResultAny - initialize an empty ArrayBuildStateAny
5757  *
5758  * input_type is the input datatype (either element or array type)
5759  * rcontext is where to keep working state
5760  * subcontext is a flag determining whether to use a separate memory context
5761  */
5763 initArrayResultAny(Oid input_type, MemoryContext rcontext, bool subcontext)
5764 {
5765  ArrayBuildStateAny *astate;
5766  Oid element_type = get_element_type(input_type);
5767 
5768  if (OidIsValid(element_type))
5769  {
5770  /* Array case */
5771  ArrayBuildStateArr *arraystate;
5772 
5773  arraystate = initArrayResultArr(input_type, InvalidOid, rcontext, subcontext);
5774  astate = (ArrayBuildStateAny *)
5775  MemoryContextAlloc(arraystate->mcontext,
5776  sizeof(ArrayBuildStateAny));
5777  astate->scalarstate = NULL;
5778  astate->arraystate = arraystate;
5779  }
5780  else
5781  {
5782  /* Scalar case */
5783  ArrayBuildState *scalarstate;
5784 
5785  /* Let's just check that we have a type that can be put into arrays */
5786  Assert(OidIsValid(get_array_type(input_type)));
5787 
5788  scalarstate = initArrayResult(input_type, rcontext, subcontext);
5789  astate = (ArrayBuildStateAny *)
5790  MemoryContextAlloc(scalarstate->mcontext,
5791  sizeof(ArrayBuildStateAny));
5792  astate->scalarstate = scalarstate;
5793  astate->arraystate = NULL;
5794  }
5795 
5796  return astate;
5797 }
5798 
5799 /*
5800  * accumArrayResultAny - accumulate one (more) input for an array result
5801  *
5802  * astate is working state (can be NULL on first call)
5803  * dvalue/disnull represent the new input to append to the array
5804  * input_type is the input datatype (either element or array type)
5805  * rcontext is where to keep working state
5806  */
5809  Datum dvalue, bool disnull,
5810  Oid input_type,
5811  MemoryContext rcontext)
5812 {
5813  if (astate == NULL)
5814  astate = initArrayResultAny(input_type, rcontext, true);
5815 
5816  if (astate->scalarstate)
5817  (void) accumArrayResult(astate->scalarstate,
5818  dvalue, disnull,
5819  input_type, rcontext);
5820  else
5821  (void) accumArrayResultArr(astate->arraystate,
5822  dvalue, disnull,
5823  input_type, rcontext);
5824 
5825  return astate;
5826 }
5827 
5828 /*
5829  * makeArrayResultAny - produce final result of accumArrayResultAny
5830  *
5831  * astate is working state (must not be NULL)
5832  * rcontext is where to construct result
5833  * release is true if okay to release working state
5834  */
5835 Datum
5837  MemoryContext rcontext, bool release)
5838 {
5839  Datum result;
5840 
5841  if (astate->scalarstate)
5842  {
5843  /* Must use makeMdArrayResult to support "release" parameter */
5844  int ndims;
5845  int dims[1];
5846  int lbs[1];
5847 
5848  /* If no elements were presented, we want to create an empty array */
5849  ndims = (astate->scalarstate->nelems > 0) ? 1 : 0;
5850  dims[0] = astate->scalarstate->nelems;
5851  lbs[0] = 1;
5852 
5853  result = makeMdArrayResult(astate->scalarstate, ndims, dims, lbs,
5854  rcontext, release);
5855  }
5856  else
5857  {
5858  result = makeArrayResultArr(astate->arraystate,
5859  rcontext, release);
5860  }
5861  return result;
5862 }
5863 
5864 
5865 Datum
5867 {
5868  if (array_cmp(fcinfo) > 0)
5870  else
5872 }
5873 
5874 Datum
5876 {
5877  if (array_cmp(fcinfo) < 0)
5879  else
5881 }
5882 
5883 
5885 {
5888  bool reverse;
5890 
5891 /*
5892  * generate_subscripts(array anyarray, dim int [, reverse bool])
5893  * Returns all subscripts of the array for any dimension
5894  */
5895 Datum
5897 {
5898  FuncCallContext *funcctx;
5899  MemoryContext oldcontext;
5901 
5902  /* stuff done only on the first call of the function */
5903  if (SRF_IS_FIRSTCALL())
5904  {
5906  int reqdim = PG_GETARG_INT32(1);
5907  int *lb,
5908  *dimv;
5909 
5910  /* create a function context for cross-call persistence */
5911  funcctx = SRF_FIRSTCALL_INIT();
5912 
5913  /* Sanity check: does it look like an array at all? */
5914  if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
5915  SRF_RETURN_DONE(funcctx);
5916 
5917  /* Sanity check: was the requested dim valid */
5918  if (reqdim <= 0 || reqdim > AARR_NDIM(v))
5919  SRF_RETURN_DONE(funcctx);
5920 
5921  /*
5922  * switch to memory context appropriate for multiple function calls
5923  */
5924  oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
5926 
5927  lb = AARR_LBOUND(v);
5928  dimv = AARR_DIMS(v);
5929 
5930  fctx->lower = lb[reqdim - 1];
5931  fctx->upper = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
5932  fctx->reverse = (PG_NARGS() < 3) ? false : PG_GETARG_BOOL(2);
5933 
5934  funcctx->user_fctx = fctx;
5935 
5936  MemoryContextSwitchTo(oldcontext);
5937  }
5938 
5939  funcctx = SRF_PERCALL_SETUP();
5940 
5941  fctx = funcctx->user_fctx;
5942 
5943  if (fctx->lower <= fctx->upper)
5944  {
5945  if (!fctx->reverse)
5946  SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->lower++));
5947  else
5948  SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->upper--));
5949  }
5950  else
5951  /* done when there are no more elements left */
5952  SRF_RETURN_DONE(funcctx);
5953 }
5954 
5955 /*
5956  * generate_subscripts_nodir
5957  * Implements the 2-argument version of generate_subscripts
5958  */
5959 Datum
5961 {
5962  /* just call the other one -- it can handle both cases */
5963  return generate_subscripts(fcinfo);
5964 }
5965 
5966 /*
5967  * array_fill_with_lower_bounds
5968  * Create and fill array with defined lower bounds.