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