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