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