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