1 /*-------------------------------------------------------------------------
4 * Support functions for arrays.
6 * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/backend/utils/adt/arrayfuncs.c
13 *-------------------------------------------------------------------------
20 #include "libpq/pqformat.h"
21 #include "parser/parse_coerce.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"
33 bool Array_nulls = true;
46 ARRAY_QUOTED_ELEM_STARTED,
47 ARRAY_QUOTED_ELEM_COMPLETED,
49 ARRAY_LEVEL_COMPLETED,
53 static bool array_isspace(char ch);
54 static int ArrayCount(const char *str, int *dim, char typdelim);
55 static void ReadArrayStr(char *arrayStr, const char *origStr,
56 int nitems, int ndim, int *dim,
57 FmgrInfo *inputproc, Oid typioparam, int32 typmod,
59 int typlen, bool typbyval, char typalign,
60 Datum *values, bool *nulls,
61 bool *hasnulls, int32 *nbytes);
62 static void ReadArrayBinary(StringInfo buf, int nitems,
63 FmgrInfo *receiveproc, Oid typioparam, int32 typmod,
64 int typlen, bool typbyval, char typalign,
65 Datum *values, bool *nulls,
66 bool *hasnulls, int32 *nbytes);
67 static void CopyArrayEls(ArrayType *array,
68 Datum *values, bool *nulls, int nitems,
69 int typlen, bool typbyval, char typalign,
71 static bool array_get_isnull(const bits8 *nullbitmap, int offset);
72 static void array_set_isnull(bits8 *nullbitmap, int offset, bool isNull);
73 static Datum ArrayCast(char *value, bool byval, int len);
74 static int ArrayCastAndSet(Datum src,
75 int typlen, bool typbyval, char typalign,
77 static char *array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
78 int typlen, bool typbyval, char typalign);
79 static int array_nelems_size(char *ptr, int offset, bits8 *nullbitmap,
80 int nitems, int typlen, bool typbyval, char typalign);
81 static int array_copy(char *destptr, int nitems,
82 char *srcptr, int offset, bits8 *nullbitmap,
83 int typlen, bool typbyval, char typalign);
84 static int array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
85 int ndim, int *dim, int *lb,
87 int typlen, bool typbyval, char typalign);
88 static void array_extract_slice(ArrayType *newarray,
89 int ndim, int *dim, int *lb,
90 char *arraydataptr, bits8 *arraynullsptr,
92 int typlen, bool typbyval, char typalign);
93 static void array_insert_slice(ArrayType *destArray, ArrayType *origArray,
95 int ndim, int *dim, int *lb,
97 int typlen, bool typbyval, char typalign);
98 static int array_cmp(FunctionCallInfo fcinfo);
99 static ArrayType *create_array_envelope(int ndims, int *dimv, int *lbv, int nbytes,
100 Oid elmtype, int dataoffset);
101 static ArrayType *array_fill_internal(ArrayType *dims, ArrayType *lbs,
102 Datum value, bool isnull, Oid elmtype,
103 FunctionCallInfo fcinfo);
108 * converts an array from the external format in "string" to
109 * its internal format.
112 * the internal representation of the input array
115 array_in(PG_FUNCTION_ARGS)
117 char *string = PG_GETARG_CSTRING(0); /* external form */
118 Oid element_type = PG_GETARG_OID(1); /* type of an array
120 int32 typmod = PG_GETARG_INT32(2); /* typmod for array elements */
139 ArrayMetaState *my_extra;
142 * We arrange to look up info about element type, including its input
143 * conversion proc, only once per series of calls, assuming the element
144 * type doesn't change underneath us.
146 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
147 if (my_extra == NULL)
149 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
150 sizeof(ArrayMetaState));
151 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
152 my_extra->element_type = ~element_type;
155 if (my_extra->element_type != element_type)
158 * Get info about element type, including its input conversion proc
160 get_type_io_data(element_type, IOFunc_input,
161 &my_extra->typlen, &my_extra->typbyval,
162 &my_extra->typalign, &my_extra->typdelim,
163 &my_extra->typioparam, &my_extra->typiofunc);
164 fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
165 fcinfo->flinfo->fn_mcxt);
166 my_extra->element_type = element_type;
168 typlen = my_extra->typlen;
169 typbyval = my_extra->typbyval;
170 typalign = my_extra->typalign;
171 typdelim = my_extra->typdelim;
172 typioparam = my_extra->typioparam;
174 /* Make a modifiable copy of the input */
175 string_save = pstrdup(string);
178 * If the input string starts with dimension info, read and use that.
179 * Otherwise, we require the input to be in curly-brace style, and we
180 * prescan the input to determine dimensions.
182 * Dimension info takes the form of one or more [n] or [m:n] items. The
183 * outer loop iterates once per dimension item.
193 * Note: we currently allow whitespace between, but not within,
196 while (array_isspace(*p))
199 break; /* no more dimension items */
203 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
204 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
207 for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++);
208 if (q == p) /* no digits? */
210 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
211 errmsg("missing dimension value")));
217 lBound[ndim] = atoi(p);
219 for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++);
220 if (q == p) /* no digits? */
222 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
223 errmsg("missing dimension value")));
232 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
233 errmsg("missing \"]\" in array dimensions")));
238 if (ub < lBound[ndim])
240 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
241 errmsg("upper bound cannot be less than lower bound")));
243 dim[ndim] = ub - lBound[ndim] + 1;
249 /* No array dimensions, so intuit dimensions from brace structure */
252 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
253 errmsg("array value must start with \"{\" or dimension information")));
254 ndim = ArrayCount(p, dim, typdelim);
255 for (i = 0; i < ndim; i++)
263 /* If array dimensions are given, expect '=' operator */
264 if (strncmp(p, ASSGN, strlen(ASSGN)) != 0)
266 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
267 errmsg("missing assignment operator")));
269 while (array_isspace(*p))
273 * intuit dimensions from brace structure -- it better match what we
278 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
279 errmsg("array value must start with \"{\" or dimension information")));
280 ndim_braces = ArrayCount(p, dim_braces, typdelim);
281 if (ndim_braces != ndim)
283 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
284 errmsg("array dimensions incompatible with array literal")));
285 for (i = 0; i < ndim; ++i)
287 if (dim[i] != dim_braces[i])
289 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
290 errmsg("array dimensions incompatible with array literal")));
295 printf("array_in- ndim %d (", ndim);
296 for (i = 0; i < ndim; i++)
298 printf(" %d", dim[i]);
300 printf(") for %s\n", string);
303 /* This checks for overflow of the array dimensions */
304 nitems = ArrayGetNItems(ndim, dim);
307 PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
309 dataPtr = (Datum *) palloc(nitems * sizeof(Datum));
310 nullsPtr = (bool *) palloc(nitems * sizeof(bool));
311 ReadArrayStr(p, string,
313 &my_extra->proc, typioparam, typmod,
315 typlen, typbyval, typalign,
320 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
321 nbytes += dataoffset;
325 dataoffset = 0; /* marker for no null bitmap */
326 nbytes += ARR_OVERHEAD_NONULLS(ndim);
328 retval = (ArrayType *) palloc0(nbytes);
329 SET_VARSIZE(retval, nbytes);
331 retval->dataoffset = dataoffset;
334 * This comes from the array's pg_type.typelem (which points to the base
335 * data type's pg_type.oid) and stores system oids in user tables. This
336 * oid must be preserved by binary upgrades.
338 retval->elemtype = element_type;
339 memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
340 memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
343 dataPtr, nullsPtr, nitems,
344 typlen, typbyval, typalign,
351 PG_RETURN_ARRAYTYPE_P(retval);
355 * array_isspace() --- a non-locale-dependent isspace()
357 * We used to use isspace() for parsing array values, but that has
358 * undesirable results: an array value might be silently interpreted
359 * differently depending on the locale setting. Now we just hard-wire
360 * the traditional ASCII definition of isspace().
363 array_isspace(char ch)
377 * Determines the dimensions for an array string.
379 * Returns number of dimensions as function result. The axis lengths are
380 * returned in dim[], which must be of size MAXDIM.
383 ArrayCount(const char *str, int *dim, char typdelim)
391 bool in_quotes = false;
392 bool eoArray = false;
393 bool empty_array = true;
395 ArrayParseState parse_state = ARRAY_NO_LEVEL;
397 for (i = 0; i < MAXDIM; ++i)
399 temp[i] = dim[i] = 0;
400 nelems_last[i] = nelems[i] = 1;
406 bool itemdone = false;
410 if (parse_state == ARRAY_ELEM_STARTED ||
411 parse_state == ARRAY_QUOTED_ELEM_STARTED)
417 /* Signal a premature end of the string */
419 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
420 errmsg("malformed array literal: \"%s\"", str)));
425 * An escape must be after a level start, after an element
426 * start, or after an element delimiter. In any case we
427 * now must be past an element start.
429 if (parse_state != ARRAY_LEVEL_STARTED &&
430 parse_state != ARRAY_ELEM_STARTED &&
431 parse_state != ARRAY_QUOTED_ELEM_STARTED &&
432 parse_state != ARRAY_ELEM_DELIMITED)
434 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
435 errmsg("malformed array literal: \"%s\"", str)));
436 if (parse_state != ARRAY_QUOTED_ELEM_STARTED)
437 parse_state = ARRAY_ELEM_STARTED;
438 /* skip the escaped character */
443 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
444 errmsg("malformed array literal: \"%s\"", str)));
449 * A quote must be after a level start, after a quoted
450 * element start, or after an element delimiter. In any
451 * case we now must be past an element start.
453 if (parse_state != ARRAY_LEVEL_STARTED &&
454 parse_state != ARRAY_QUOTED_ELEM_STARTED &&
455 parse_state != ARRAY_ELEM_DELIMITED)
457 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
458 errmsg("malformed array literal: \"%s\"", str)));
459 in_quotes = !in_quotes;
461 parse_state = ARRAY_QUOTED_ELEM_STARTED;
463 parse_state = ARRAY_QUOTED_ELEM_COMPLETED;
469 * A left brace can occur if no nesting has occurred
470 * yet, after a level start, or after a level
473 if (parse_state != ARRAY_NO_LEVEL &&
474 parse_state != ARRAY_LEVEL_STARTED &&
475 parse_state != ARRAY_LEVEL_DELIMITED)
477 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
478 errmsg("malformed array literal: \"%s\"", str)));
479 parse_state = ARRAY_LEVEL_STARTED;
480 if (nest_level >= MAXDIM)
482 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
483 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
484 nest_level, MAXDIM)));
485 temp[nest_level] = 0;
487 if (ndim < nest_level)
495 * A right brace can occur after an element start, an
496 * element completion, a quoted element completion, or
497 * a level completion.
499 if (parse_state != ARRAY_ELEM_STARTED &&
500 parse_state != ARRAY_ELEM_COMPLETED &&
501 parse_state != ARRAY_QUOTED_ELEM_COMPLETED &&
502 parse_state != ARRAY_LEVEL_COMPLETED &&
503 !(nest_level == 1 && parse_state == ARRAY_LEVEL_STARTED))
505 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
506 errmsg("malformed array literal: \"%s\"", str)));
507 parse_state = ARRAY_LEVEL_COMPLETED;
510 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
511 errmsg("malformed array literal: \"%s\"", str)));
514 if ((nelems_last[nest_level] != 1) &&
515 (nelems[nest_level] != nelems_last[nest_level]))
517 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
518 errmsg("multidimensional arrays must have "
519 "array expressions with matching "
521 nelems_last[nest_level] = nelems[nest_level];
522 nelems[nest_level] = 1;
524 eoArray = itemdone = true;
528 * We don't set itemdone here; see comments in
531 temp[nest_level - 1]++;
538 if (*ptr == typdelim)
541 * Delimiters can occur after an element start, an
542 * element completion, a quoted element
543 * completion, or a level completion.
545 if (parse_state != ARRAY_ELEM_STARTED &&
546 parse_state != ARRAY_ELEM_COMPLETED &&
547 parse_state != ARRAY_QUOTED_ELEM_COMPLETED &&
548 parse_state != ARRAY_LEVEL_COMPLETED)
550 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
551 errmsg("malformed array literal: \"%s\"", str)));
552 if (parse_state == ARRAY_LEVEL_COMPLETED)
553 parse_state = ARRAY_LEVEL_DELIMITED;
555 parse_state = ARRAY_ELEM_DELIMITED;
557 nelems[nest_level - 1]++;
559 else if (!array_isspace(*ptr))
562 * Other non-space characters must be after a
563 * level start, after an element start, or after
564 * an element delimiter. In any case we now must
565 * be past an element start.
567 if (parse_state != ARRAY_LEVEL_STARTED &&
568 parse_state != ARRAY_ELEM_STARTED &&
569 parse_state != ARRAY_ELEM_DELIMITED)
571 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
572 errmsg("malformed array literal: \"%s\"", str)));
573 parse_state = ARRAY_ELEM_STARTED;
585 /* only whitespace is allowed after the closing brace */
588 if (!array_isspace(*ptr++))
590 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
591 errmsg("malformed array literal: \"%s\"", str)));
594 /* special case for an empty array */
598 for (i = 0; i < ndim; ++i)
606 * parses the array string pointed to by "arrayStr" and converts the values
607 * to internal format. Unspecified elements are initialized to nulls.
608 * The array dimensions must already have been determined.
611 * arrayStr: the string to parse.
612 * CAUTION: the contents of "arrayStr" will be modified!
613 * origStr: the unmodified input string, used only in error messages.
614 * nitems: total number of array elements, as already determined.
615 * ndim: number of array dimensions
616 * dim[]: array axis lengths
617 * inputproc: type-specific input procedure for element datatype.
618 * typioparam, typmod: auxiliary values to pass to inputproc.
619 * typdelim: the value delimiter (type-specific).
620 * typlen, typbyval, typalign: storage parameters of element datatype.
623 * values[]: filled with converted data values.
624 * nulls[]: filled with is-null markers.
625 * *hasnulls: set TRUE iff there are any null elements.
626 * *nbytes: set to total size of data area needed (including alignment
627 * padding but not including array header overhead).
629 * Note that values[] and nulls[] are allocated by the caller, and must have
633 ReadArrayStr(char *arrayStr,
653 bool in_quotes = false;
654 bool eoArray = false;
660 mda_get_prod(ndim, dim, prod);
661 MemSet(indx, 0, sizeof(indx));
663 /* Initialize is-null markers to true */
664 memset(nulls, true, nitems * sizeof(bool));
667 * We have to remove " and \ characters to create a clean item value to
668 * pass to the datatype input routine. We overwrite each item value
669 * in-place within arrayStr to do this. srcptr is the current scan point,
670 * and dstptr is where we are copying to.
672 * We also want to suppress leading and trailing unquoted whitespace. We
673 * use the leadingspace flag to suppress leading space. Trailing space is
674 * tracked by using dstendptr to point to the last significant output
677 * The error checking in this routine is mostly pro-forma, since we expect
678 * that ArrayCount() already validated the string.
683 bool itemdone = false;
684 bool leadingspace = true;
685 bool hasquoting = false;
691 itemstart = dstptr = dstendptr = srcptr;
698 /* Signal a premature end of the string */
700 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
701 errmsg("malformed array literal: \"%s\"",
705 /* Skip backslash, copy next character as-is. */
709 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
710 errmsg("malformed array literal: \"%s\"",
712 *dstptr++ = *srcptr++;
713 /* Treat the escaped character as non-whitespace */
714 leadingspace = false;
716 hasquoting = true; /* can't be a NULL marker */
719 in_quotes = !in_quotes;
721 leadingspace = false;
725 * Advance dstendptr when we exit in_quotes; this
726 * saves having to do it in all the other in_quotes
731 hasquoting = true; /* can't be a NULL marker */
737 if (nest_level >= ndim)
739 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
740 errmsg("malformed array literal: \"%s\"",
743 indx[nest_level - 1] = 0;
747 *dstptr++ = *srcptr++;
754 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
755 errmsg("malformed array literal: \"%s\"",
758 i = ArrayGetOffset0(ndim, indx, prod);
759 indx[nest_level - 1] = 0;
762 eoArray = itemdone = true;
764 indx[nest_level - 1]++;
768 *dstptr++ = *srcptr++;
772 *dstptr++ = *srcptr++;
773 else if (*srcptr == typdelim)
776 i = ArrayGetOffset0(ndim, indx, prod);
781 else if (array_isspace(*srcptr))
784 * If leading space, drop it immediately. Else, copy
785 * but don't advance dstendptr.
790 *dstptr++ = *srcptr++;
794 *dstptr++ = *srcptr++;
795 leadingspace = false;
802 Assert(dstptr < srcptr);
805 if (i < 0 || i >= nitems)
807 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
808 errmsg("malformed array literal: \"%s\"",
811 if (Array_nulls && !hasquoting &&
812 pg_strcasecmp(itemstart, "NULL") == 0)
814 /* it's a NULL item */
815 values[i] = InputFunctionCall(inputproc, NULL,
821 values[i] = InputFunctionCall(inputproc, itemstart,
828 * Check for nulls, compute total data space needed
832 for (i = 0; i < nitems; i++)
838 /* let's just make sure data is not toasted */
840 values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
841 totbytes = att_addlength_datum(totbytes, typlen, values[i]);
842 totbytes = att_align_nominal(totbytes, typalign);
843 /* check for overflow of total request */
844 if (!AllocSizeIsValid(totbytes))
846 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
847 errmsg("array size exceeds the maximum allowed (%d)",
848 (int) MaxAllocSize)));
857 * Copy data into an array object from a temporary array of Datums.
859 * array: array object (with header fields already filled in)
860 * values: array of Datums to be copied
861 * nulls: array of is-null flags (can be NULL if no nulls)
862 * nitems: number of Datums to be copied
863 * typbyval, typlen, typalign: info about element datatype
864 * freedata: if TRUE and element type is pass-by-ref, pfree data values
865 * referenced by Datums after copying them.
867 * If the input data is of varlena type, the caller must have ensured that
868 * the values are not toasted. (Doing it here doesn't work since the
869 * caller has already allocated space for the array...)
872 CopyArrayEls(ArrayType *array,
881 char *p = ARR_DATA_PTR(array);
882 bits8 *bitmap = ARR_NULLBITMAP(array);
890 for (i = 0; i < nitems; i++)
892 if (nulls && nulls[i])
894 if (!bitmap) /* shouldn't happen */
895 elog(ERROR, "null array element where not supported");
896 /* bitmap bit stays 0 */
901 p += ArrayCastAndSet(values[i], typlen, typbyval, typalign, p);
903 pfree(DatumGetPointer(values[i]));
908 if (bitmask == 0x100)
917 if (bitmap && bitmask != 1)
923 * takes the internal representation of an array and returns a string
924 * containing the array in its external format.
927 array_out(PG_FUNCTION_ARGS)
929 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
930 Oid element_type = ARR_ELEMTYPE(v);
939 dims_str[(MAXDIM * 33) + 2];
942 * 33 per dim since we assume 15 digits per number + ':' +'[]'
944 * +2 allows for assignment operator + trailing null
959 ArrayMetaState *my_extra;
962 * We arrange to look up info about element type, including its output
963 * conversion proc, only once per series of calls, assuming the element
964 * type doesn't change underneath us.
966 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
967 if (my_extra == NULL)
969 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
970 sizeof(ArrayMetaState));
971 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
972 my_extra->element_type = ~element_type;
975 if (my_extra->element_type != element_type)
978 * Get info about element type, including its output conversion proc
980 get_type_io_data(element_type, IOFunc_output,
981 &my_extra->typlen, &my_extra->typbyval,
982 &my_extra->typalign, &my_extra->typdelim,
983 &my_extra->typioparam, &my_extra->typiofunc);
984 fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
985 fcinfo->flinfo->fn_mcxt);
986 my_extra->element_type = element_type;
988 typlen = my_extra->typlen;
989 typbyval = my_extra->typbyval;
990 typalign = my_extra->typalign;
991 typdelim = my_extra->typdelim;
996 nitems = ArrayGetNItems(ndim, dims);
1000 retval = pstrdup("{}");
1001 PG_RETURN_CSTRING(retval);
1005 * we will need to add explicit dimensions if any dimension has a lower
1006 * bound other than one
1008 for (i = 0; i < ndim; i++)
1018 * Convert all values to string form, count total space needed (including
1019 * any overhead such as escaping backslashes), and detect whether each
1020 * item needs double quotes.
1022 values = (char **) palloc(nitems * sizeof(char *));
1023 needquotes = (bool *) palloc(nitems * sizeof(bool));
1024 overall_length = 1; /* don't forget to count \0 at end. */
1026 p = ARR_DATA_PTR(v);
1027 bitmap = ARR_NULLBITMAP(v);
1030 for (i = 0; i < nitems; i++)
1034 /* Get source element, checking for NULL */
1035 if (bitmap && (*bitmap & bitmask) == 0)
1037 values[i] = pstrdup("NULL");
1038 overall_length += 4;
1045 itemvalue = fetch_att(p, typbyval, typlen);
1046 values[i] = OutputFunctionCall(&my_extra->proc, itemvalue);
1047 p = att_addlength_pointer(p, typlen, p);
1048 p = (char *) att_align_nominal(p, typalign);
1050 /* count data plus backslashes; detect chars needing quotes */
1051 if (values[i][0] == '\0')
1052 needquote = true; /* force quotes for empty string */
1053 else if (pg_strcasecmp(values[i], "NULL") == 0)
1054 needquote = true; /* force quotes for literal NULL */
1058 for (tmp = values[i]; *tmp != '\0'; tmp++)
1062 overall_length += 1;
1063 if (ch == '"' || ch == '\\')
1066 overall_length += 1;
1068 else if (ch == '{' || ch == '}' || ch == typdelim ||
1074 needquotes[i] = needquote;
1076 /* Count the pair of double quotes, if needed */
1078 overall_length += 2;
1080 overall_length += 1;
1082 /* advance bitmap pointer if any */
1086 if (bitmask == 0x100)
1095 * count total number of curly braces in output string
1097 for (i = j = 0, k = 1; i < ndim; i++)
1098 k *= dims[i], j += k;
1102 /* add explicit dimensions if required */
1105 char *ptr = dims_str;
1107 for (i = 0; i < ndim; i++)
1109 sprintf(ptr, "[%d:%d]", lb[i], lb[i] + dims[i] - 1);
1116 retval = (char *) palloc(strlen(dims_str) + overall_length + 2 * j);
1119 #define APPENDSTR(str) (strcpy(p, (str)), p += strlen(p))
1120 #define APPENDCHAR(ch) (*p++ = (ch), *p = '\0')
1123 APPENDSTR(dims_str);
1125 for (i = 0; i < ndim; i++)
1131 for (i = j; i < ndim - 1; i++)
1137 for (tmp = values[k]; *tmp; tmp++)
1141 if (ch == '"' || ch == '\\')
1149 APPENDSTR(values[k]);
1152 for (i = ndim - 1; i >= 0; i--)
1154 indx[i] = (indx[i] + 1) % dims[i];
1157 APPENDCHAR(typdelim);
1172 PG_RETURN_CSTRING(retval);
1177 * converts an array from the external binary format to
1178 * its internal format.
1181 * the internal representation of the input array
1184 array_recv(PG_FUNCTION_ARGS)
1186 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
1187 Oid spec_element_type = PG_GETARG_OID(1); /* type of an array
1189 int32 typmod = PG_GETARG_INT32(2); /* typmod for array elements */
1207 ArrayMetaState *my_extra;
1209 /* Get the array header information */
1210 ndim = pq_getmsgint(buf, 4);
1211 if (ndim < 0) /* we do allow zero-dimension arrays */
1213 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1214 errmsg("invalid number of dimensions: %d", ndim)));
1217 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1218 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
1221 flags = pq_getmsgint(buf, 4);
1222 if (flags != 0 && flags != 1)
1224 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1225 errmsg("invalid array flags")));
1227 element_type = pq_getmsgint(buf, sizeof(Oid));
1228 if (element_type != spec_element_type)
1230 /* XXX Can we allow taking the input element type in any cases? */
1232 (errcode(ERRCODE_DATATYPE_MISMATCH),
1233 errmsg("wrong element type")));
1236 for (i = 0; i < ndim; i++)
1238 dim[i] = pq_getmsgint(buf, 4);
1239 lBound[i] = pq_getmsgint(buf, 4);
1242 * Check overflow of upper bound. (ArrayNItems() below checks that
1247 int ub = lBound[i] + dim[i] - 1;
1250 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
1251 errmsg("integer out of range")));
1255 /* This checks for overflow of array dimensions */
1256 nitems = ArrayGetNItems(ndim, dim);
1259 * We arrange to look up info about element type, including its receive
1260 * conversion proc, only once per series of calls, assuming the element
1261 * type doesn't change underneath us.
1263 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1264 if (my_extra == NULL)
1266 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1267 sizeof(ArrayMetaState));
1268 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1269 my_extra->element_type = ~element_type;
1272 if (my_extra->element_type != element_type)
1274 /* Get info about element type, including its receive proc */
1275 get_type_io_data(element_type, IOFunc_receive,
1276 &my_extra->typlen, &my_extra->typbyval,
1277 &my_extra->typalign, &my_extra->typdelim,
1278 &my_extra->typioparam, &my_extra->typiofunc);
1279 if (!OidIsValid(my_extra->typiofunc))
1281 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1282 errmsg("no binary input function available for type %s",
1283 format_type_be(element_type))));
1284 fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1285 fcinfo->flinfo->fn_mcxt);
1286 my_extra->element_type = element_type;
1291 /* Return empty array ... but not till we've validated element_type */
1292 PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
1295 typlen = my_extra->typlen;
1296 typbyval = my_extra->typbyval;
1297 typalign = my_extra->typalign;
1298 typioparam = my_extra->typioparam;
1300 dataPtr = (Datum *) palloc(nitems * sizeof(Datum));
1301 nullsPtr = (bool *) palloc(nitems * sizeof(bool));
1302 ReadArrayBinary(buf, nitems,
1303 &my_extra->proc, typioparam, typmod,
1304 typlen, typbyval, typalign,
1306 &hasnulls, &nbytes);
1309 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
1310 nbytes += dataoffset;
1314 dataoffset = 0; /* marker for no null bitmap */
1315 nbytes += ARR_OVERHEAD_NONULLS(ndim);
1317 retval = (ArrayType *) palloc(nbytes);
1318 SET_VARSIZE(retval, nbytes);
1319 retval->ndim = ndim;
1320 retval->dataoffset = dataoffset;
1321 retval->elemtype = element_type;
1322 memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
1323 memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
1325 CopyArrayEls(retval,
1326 dataPtr, nullsPtr, nitems,
1327 typlen, typbyval, typalign,
1333 PG_RETURN_ARRAYTYPE_P(retval);
1338 * collect the data elements of an array being read in binary style.
1341 * buf: the data buffer to read from.
1342 * nitems: total number of array elements (already read).
1343 * receiveproc: type-specific receive procedure for element datatype.
1344 * typioparam, typmod: auxiliary values to pass to receiveproc.
1345 * typlen, typbyval, typalign: storage parameters of element datatype.
1348 * values[]: filled with converted data values.
1349 * nulls[]: filled with is-null markers.
1350 * *hasnulls: set TRUE iff there are any null elements.
1351 * *nbytes: set to total size of data area needed (including alignment
1352 * padding but not including array header overhead).
1354 * Note that values[] and nulls[] are allocated by the caller, and must have
1358 ReadArrayBinary(StringInfo buf,
1360 FmgrInfo *receiveproc,
1375 for (i = 0; i < nitems; i++)
1378 StringInfoData elem_buf;
1381 /* Get and check the item length */
1382 itemlen = pq_getmsgint(buf, 4);
1383 if (itemlen < -1 || itemlen > (buf->len - buf->cursor))
1385 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1386 errmsg("insufficient data left in message")));
1390 /* -1 length means NULL */
1391 values[i] = ReceiveFunctionCall(receiveproc, NULL,
1392 typioparam, typmod);
1398 * Rather than copying data around, we just set up a phony StringInfo
1399 * pointing to the correct portion of the input buffer. We assume we
1400 * can scribble on the input buffer so as to maintain the convention
1401 * that StringInfos have a trailing null.
1403 elem_buf.data = &buf->data[buf->cursor];
1404 elem_buf.maxlen = itemlen + 1;
1405 elem_buf.len = itemlen;
1406 elem_buf.cursor = 0;
1408 buf->cursor += itemlen;
1410 csave = buf->data[buf->cursor];
1411 buf->data[buf->cursor] = '\0';
1413 /* Now call the element's receiveproc */
1414 values[i] = ReceiveFunctionCall(receiveproc, &elem_buf,
1415 typioparam, typmod);
1418 /* Trouble if it didn't eat the whole buffer */
1419 if (elem_buf.cursor != itemlen)
1421 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1422 errmsg("improper binary format in array element %d",
1425 buf->data[buf->cursor] = csave;
1429 * Check for nulls, compute total data space needed
1433 for (i = 0; i < nitems; i++)
1439 /* let's just make sure data is not toasted */
1441 values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
1442 totbytes = att_addlength_datum(totbytes, typlen, values[i]);
1443 totbytes = att_align_nominal(totbytes, typalign);
1444 /* check for overflow of total request */
1445 if (!AllocSizeIsValid(totbytes))
1447 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1448 errmsg("array size exceeds the maximum allowed (%d)",
1449 (int) MaxAllocSize)));
1452 *hasnulls = hasnull;
1459 * takes the internal representation of an array and returns a bytea
1460 * containing the array in its external binary format.
1463 array_send(PG_FUNCTION_ARGS)
1465 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1466 Oid element_type = ARR_ELEMTYPE(v);
1478 ArrayMetaState *my_extra;
1481 * We arrange to look up info about element type, including its send
1482 * conversion proc, only once per series of calls, assuming the element
1483 * type doesn't change underneath us.
1485 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1486 if (my_extra == NULL)
1488 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1489 sizeof(ArrayMetaState));
1490 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1491 my_extra->element_type = ~element_type;
1494 if (my_extra->element_type != element_type)
1496 /* Get info about element type, including its send proc */
1497 get_type_io_data(element_type, IOFunc_send,
1498 &my_extra->typlen, &my_extra->typbyval,
1499 &my_extra->typalign, &my_extra->typdelim,
1500 &my_extra->typioparam, &my_extra->typiofunc);
1501 if (!OidIsValid(my_extra->typiofunc))
1503 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1504 errmsg("no binary output function available for type %s",
1505 format_type_be(element_type))));
1506 fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1507 fcinfo->flinfo->fn_mcxt);
1508 my_extra->element_type = element_type;
1510 typlen = my_extra->typlen;
1511 typbyval = my_extra->typbyval;
1512 typalign = my_extra->typalign;
1516 nitems = ArrayGetNItems(ndim, dim);
1518 pq_begintypsend(&buf);
1520 /* Send the array header information */
1521 pq_sendint(&buf, ndim, 4);
1522 pq_sendint(&buf, ARR_HASNULL(v) ? 1 : 0, 4);
1523 pq_sendint(&buf, element_type, sizeof(Oid));
1524 for (i = 0; i < ndim; i++)
1526 pq_sendint(&buf, ARR_DIMS(v)[i], 4);
1527 pq_sendint(&buf, ARR_LBOUND(v)[i], 4);
1530 /* Send the array elements using the element's own sendproc */
1531 p = ARR_DATA_PTR(v);
1532 bitmap = ARR_NULLBITMAP(v);
1535 for (i = 0; i < nitems; i++)
1537 /* Get source element, checking for NULL */
1538 if (bitmap && (*bitmap & bitmask) == 0)
1540 /* -1 length means a NULL */
1541 pq_sendint(&buf, -1, 4);
1548 itemvalue = fetch_att(p, typbyval, typlen);
1549 outputbytes = SendFunctionCall(&my_extra->proc, itemvalue);
1550 pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
1551 pq_sendbytes(&buf, VARDATA(outputbytes),
1552 VARSIZE(outputbytes) - VARHDRSZ);
1555 p = att_addlength_pointer(p, typlen, p);
1556 p = (char *) att_align_nominal(p, typalign);
1559 /* advance bitmap pointer if any */
1563 if (bitmask == 0x100)
1571 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
1576 * returns the number of dimensions of the array pointed to by "v"
1579 array_ndims(PG_FUNCTION_ARGS)
1581 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1583 /* Sanity check: does it look like an array at all? */
1584 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1587 PG_RETURN_INT32(ARR_NDIM(v));
1592 * returns the dimensions of the array pointed to by "v", as a "text"
1595 array_dims(PG_FUNCTION_ARGS)
1597 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1604 * 33 since we assume 15 digits per number + ':' +'[]'
1606 * +1 for trailing null
1608 char buf[MAXDIM * 33 + 1];
1610 /* Sanity check: does it look like an array at all? */
1611 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1618 for (i = 0; i < ARR_NDIM(v); i++)
1620 sprintf(p, "[%d:%d]", lb[i], dimv[i] + lb[i] - 1);
1624 PG_RETURN_TEXT_P(cstring_to_text(buf));
1629 * returns the lower dimension, of the DIM requested, for
1630 * the array pointed to by "v", as an int4
1633 array_lower(PG_FUNCTION_ARGS)
1635 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1636 int reqdim = PG_GETARG_INT32(1);
1640 /* Sanity check: does it look like an array at all? */
1641 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1644 /* Sanity check: was the requested dim valid */
1645 if (reqdim <= 0 || reqdim > ARR_NDIM(v))
1649 result = lb[reqdim - 1];
1651 PG_RETURN_INT32(result);
1656 * returns the upper dimension, of the DIM requested, for
1657 * the array pointed to by "v", as an int4
1660 array_upper(PG_FUNCTION_ARGS)
1662 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1663 int reqdim = PG_GETARG_INT32(1);
1668 /* Sanity check: does it look like an array at all? */
1669 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1672 /* Sanity check: was the requested dim valid */
1673 if (reqdim <= 0 || reqdim > ARR_NDIM(v))
1679 result = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
1681 PG_RETURN_INT32(result);
1686 * returns the length, of the dimension requested, for
1687 * the array pointed to by "v", as an int4
1690 array_length(PG_FUNCTION_ARGS)
1692 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1693 int reqdim = PG_GETARG_INT32(1);
1697 /* Sanity check: does it look like an array at all? */
1698 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1701 /* Sanity check: was the requested dim valid */
1702 if (reqdim <= 0 || reqdim > ARR_NDIM(v))
1707 result = dimv[reqdim - 1];
1709 PG_RETURN_INT32(result);
1714 * This routine takes an array pointer and a subscript array and returns
1715 * the referenced item as a Datum. Note that for a pass-by-reference
1716 * datatype, the returned Datum is a pointer into the array object.
1718 * This handles both ordinary varlena arrays and fixed-length arrays.
1721 * array: the array object (mustn't be NULL)
1722 * nSubscripts: number of subscripts supplied
1723 * indx[]: the subscript values
1724 * arraytyplen: pg_type.typlen for the array type
1725 * elmlen: pg_type.typlen for the array's element type
1726 * elmbyval: pg_type.typbyval for the array's element type
1727 * elmalign: pg_type.typalign for the array's element type
1730 * The return value is the element Datum.
1731 * *isNull is set to indicate whether the element is NULL.
1734 array_ref(ArrayType *array,
1752 bits8 *arraynullsptr;
1754 if (arraytyplen > 0)
1757 * fixed-length arrays -- these are assumed to be 1-d, 0-based
1760 fixedDim[0] = arraytyplen / elmlen;
1764 arraydataptr = (char *) array;
1765 arraynullsptr = NULL;
1769 /* detoast input array if necessary */
1770 array = DatumGetArrayTypeP(PointerGetDatum(array));
1772 ndim = ARR_NDIM(array);
1773 dim = ARR_DIMS(array);
1774 lb = ARR_LBOUND(array);
1775 arraydataptr = ARR_DATA_PTR(array);
1776 arraynullsptr = ARR_NULLBITMAP(array);
1780 * Return NULL for invalid subscript
1782 if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
1787 for (i = 0; i < ndim; i++)
1789 if (indx[i] < lb[i] || indx[i] >= (dim[i] + lb[i]))
1797 * Calculate the element number
1799 offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
1802 * Check for NULL array element
1804 if (array_get_isnull(arraynullsptr, offset))
1811 * OK, get the element
1814 retptr = array_seek(arraydataptr, 0, arraynullsptr, offset,
1815 elmlen, elmbyval, elmalign);
1816 return ArrayCast(retptr, elmbyval, elmlen);
1821 * This routine takes an array and a range of indices (upperIndex and
1822 * lowerIndx), creates a new array structure for the referred elements
1823 * and returns a pointer to it.
1825 * This handles both ordinary varlena arrays and fixed-length arrays.
1828 * array: the array object (mustn't be NULL)
1829 * nSubscripts: number of subscripts supplied (must be same for upper/lower)
1830 * upperIndx[]: the upper subscript values
1831 * lowerIndx[]: the lower subscript values
1832 * arraytyplen: pg_type.typlen for the array type
1833 * elmlen: pg_type.typlen for the array's element type
1834 * elmbyval: pg_type.typbyval for the array's element type
1835 * elmalign: pg_type.typalign for the array's element type
1838 * The return value is the new array Datum (it's never NULL)
1840 * NOTE: we assume it is OK to scribble on the provided subscript arrays
1841 * lowerIndx[] and upperIndx[]. These are generally just temporaries.
1844 array_get_slice(ArrayType *array,
1853 ArrayType *newarray;
1863 bits8 *arraynullsptr;
1868 if (arraytyplen > 0)
1871 * fixed-length arrays -- currently, cannot slice these because parser
1872 * labels output as being of the fixed-length array type! Code below
1873 * shows how we could support it if the parser were changed to label
1874 * output as a suitable varlena array type.
1877 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1878 errmsg("slices of fixed-length arrays not implemented")));
1881 * fixed-length arrays -- these are assumed to be 1-d, 0-based
1883 * XXX where would we get the correct ELEMTYPE from?
1886 fixedDim[0] = arraytyplen / elmlen;
1890 elemtype = InvalidOid; /* XXX */
1891 arraydataptr = (char *) array;
1892 arraynullsptr = NULL;
1896 /* detoast input array if necessary */
1897 array = DatumGetArrayTypeP(PointerGetDatum(array));
1899 ndim = ARR_NDIM(array);
1900 dim = ARR_DIMS(array);
1901 lb = ARR_LBOUND(array);
1902 elemtype = ARR_ELEMTYPE(array);
1903 arraydataptr = ARR_DATA_PTR(array);
1904 arraynullsptr = ARR_NULLBITMAP(array);
1908 * Check provided subscripts. A slice exceeding the current array limits
1909 * is silently truncated to the array limits. If we end up with an empty
1910 * slice, return an empty array.
1912 if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
1913 return construct_empty_array(elemtype);
1915 for (i = 0; i < nSubscripts; i++)
1917 if (lowerIndx[i] < lb[i])
1918 lowerIndx[i] = lb[i];
1919 if (upperIndx[i] >= (dim[i] + lb[i]))
1920 upperIndx[i] = dim[i] + lb[i] - 1;
1921 if (lowerIndx[i] > upperIndx[i])
1922 return construct_empty_array(elemtype);
1924 /* fill any missing subscript positions with full array range */
1925 for (; i < ndim; i++)
1927 lowerIndx[i] = lb[i];
1928 upperIndx[i] = dim[i] + lb[i] - 1;
1929 if (lowerIndx[i] > upperIndx[i])
1930 return construct_empty_array(elemtype);
1933 mda_get_range(ndim, span, lowerIndx, upperIndx);
1935 bytes = array_slice_size(arraydataptr, arraynullsptr,
1937 lowerIndx, upperIndx,
1938 elmlen, elmbyval, elmalign);
1941 * Currently, we put a null bitmap in the result if the source has one;
1942 * could be smarter ...
1946 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, ArrayGetNItems(ndim, span));
1947 bytes += dataoffset;
1951 dataoffset = 0; /* marker for no null bitmap */
1952 bytes += ARR_OVERHEAD_NONULLS(ndim);
1955 newarray = (ArrayType *) palloc(bytes);
1956 SET_VARSIZE(newarray, bytes);
1957 newarray->ndim = ndim;
1958 newarray->dataoffset = dataoffset;
1959 newarray->elemtype = elemtype;
1960 memcpy(ARR_DIMS(newarray), span, ndim * sizeof(int));
1963 * Lower bounds of the new array are set to 1. Formerly (before 7.3) we
1964 * copied the given lowerIndx values ... but that seems confusing.
1966 newlb = ARR_LBOUND(newarray);
1967 for (i = 0; i < ndim; i++)
1970 array_extract_slice(newarray,
1972 arraydataptr, arraynullsptr,
1973 lowerIndx, upperIndx,
1974 elmlen, elmbyval, elmalign);
1981 * This routine sets the value of an array element (specified by
1982 * a subscript array) to a new value specified by "dataValue".
1984 * This handles both ordinary varlena arrays and fixed-length arrays.
1987 * array: the initial array object (mustn't be NULL)
1988 * nSubscripts: number of subscripts supplied
1989 * indx[]: the subscript values
1990 * dataValue: the datum to be inserted at the given position
1991 * isNull: whether dataValue is NULL
1992 * arraytyplen: pg_type.typlen for the array type
1993 * elmlen: pg_type.typlen for the array's element type
1994 * elmbyval: pg_type.typbyval for the array's element type
1995 * elmalign: pg_type.typalign for the array's element type
1998 * A new array is returned, just like the old except for the one
1999 * modified entry. The original array object is not changed.
2001 * For one-dimensional arrays only, we allow the array to be extended
2002 * by assigning to a position outside the existing subscript range; any
2003 * positions between the existing elements and the new one are set to NULLs.
2004 * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
2006 * NOTE: For assignments, we throw an error for invalid subscripts etc,
2007 * rather than returning a NULL as the fetch operations do.
2010 array_set(ArrayType *array,
2020 ArrayType *newarray;
2028 bits8 *oldnullbitmap;
2042 if (arraytyplen > 0)
2045 * fixed-length arrays -- these are assumed to be 1-d, 0-based. We
2046 * cannot extend them, either.
2048 if (nSubscripts != 1)
2050 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2051 errmsg("wrong number of array subscripts")));
2053 if (indx[0] < 0 || indx[0] * elmlen >= arraytyplen)
2055 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2056 errmsg("array subscript out of range")));
2060 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
2061 errmsg("cannot assign null value to an element of a fixed-length array")));
2063 newarray = (ArrayType *) palloc(arraytyplen);
2064 memcpy(newarray, array, arraytyplen);
2065 elt_ptr = (char *) newarray + indx[0] * elmlen;
2066 ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign, elt_ptr);
2070 if (nSubscripts <= 0 || nSubscripts > MAXDIM)
2072 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2073 errmsg("wrong number of array subscripts")));
2075 /* make sure item to be inserted is not toasted */
2076 if (elmlen == -1 && !isNull)
2077 dataValue = PointerGetDatum(PG_DETOAST_DATUM(dataValue));
2079 /* detoast input array if necessary */
2080 array = DatumGetArrayTypeP(PointerGetDatum(array));
2082 ndim = ARR_NDIM(array);
2085 * if number of dims is zero, i.e. an empty array, create an array with
2086 * nSubscripts dimensions, and set the lower bounds to the supplied
2091 Oid elmtype = ARR_ELEMTYPE(array);
2093 for (i = 0; i < nSubscripts; i++)
2099 return construct_md_array(&dataValue, &isNull, nSubscripts,
2101 elmlen, elmbyval, elmalign);
2104 if (ndim != nSubscripts)
2106 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2107 errmsg("wrong number of array subscripts")));
2109 /* copy dim/lb since we may modify them */
2110 memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
2111 memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
2113 newhasnulls = (ARR_HASNULL(array) || isNull);
2114 addedbefore = addedafter = 0;
2121 if (indx[0] < lb[0])
2123 addedbefore = lb[0] - indx[0];
2124 dim[0] += addedbefore;
2126 if (addedbefore > 1)
2127 newhasnulls = true; /* will insert nulls */
2129 if (indx[0] >= (dim[0] + lb[0]))
2131 addedafter = indx[0] - (dim[0] + lb[0]) + 1;
2132 dim[0] += addedafter;
2134 newhasnulls = true; /* will insert nulls */
2140 * XXX currently we do not support extending multi-dimensional arrays
2143 for (i = 0; i < ndim; i++)
2145 if (indx[i] < lb[i] ||
2146 indx[i] >= (dim[i] + lb[i]))
2148 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2149 errmsg("array subscript out of range")));
2154 * Compute sizes of items and areas to copy
2156 newnitems = ArrayGetNItems(ndim, dim);
2158 overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, newnitems);
2160 overheadlen = ARR_OVERHEAD_NONULLS(ndim);
2161 oldnitems = ArrayGetNItems(ndim, ARR_DIMS(array));
2162 oldnullbitmap = ARR_NULLBITMAP(array);
2163 oldoverheadlen = ARR_DATA_OFFSET(array);
2164 olddatasize = ARR_SIZE(array) - oldoverheadlen;
2170 lenafter = olddatasize;
2172 else if (addedafter)
2175 lenbefore = olddatasize;
2181 offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
2182 elt_ptr = array_seek(ARR_DATA_PTR(array), 0, oldnullbitmap, offset,
2183 elmlen, elmbyval, elmalign);
2184 lenbefore = (int) (elt_ptr - ARR_DATA_PTR(array));
2185 if (array_get_isnull(oldnullbitmap, offset))
2189 olditemlen = att_addlength_pointer(0, elmlen, elt_ptr);
2190 olditemlen = att_align_nominal(olditemlen, elmalign);
2192 lenafter = (int) (olddatasize - lenbefore - olditemlen);
2199 newitemlen = att_addlength_datum(0, elmlen, dataValue);
2200 newitemlen = att_align_nominal(newitemlen, elmalign);
2203 newsize = overheadlen + lenbefore + newitemlen + lenafter;
2206 * OK, create the new array and fill in header/dimensions
2208 newarray = (ArrayType *) palloc(newsize);
2209 SET_VARSIZE(newarray, newsize);
2210 newarray->ndim = ndim;
2211 newarray->dataoffset = newhasnulls ? overheadlen : 0;
2212 newarray->elemtype = ARR_ELEMTYPE(array);
2213 memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
2214 memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
2219 memcpy((char *) newarray + overheadlen,
2220 (char *) array + oldoverheadlen,
2223 ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign,
2224 (char *) newarray + overheadlen + lenbefore);
2225 memcpy((char *) newarray + overheadlen + lenbefore + newitemlen,
2226 (char *) array + oldoverheadlen + lenbefore + olditemlen,
2230 * Fill in nulls bitmap if needed
2232 * Note: it's possible we just replaced the last NULL with a non-NULL, and
2233 * could get rid of the bitmap. Seems not worth testing for though.
2237 bits8 *newnullbitmap = ARR_NULLBITMAP(newarray);
2239 /* Zero the bitmap to take care of marking inserted positions null */
2240 MemSet(newnullbitmap, 0, (newnitems + 7) / 8);
2241 /* Fix the inserted value */
2243 array_set_isnull(newnullbitmap, newnitems - 1, isNull);
2245 array_set_isnull(newnullbitmap, offset, isNull);
2246 /* Fix the copied range(s) */
2248 array_bitmap_copy(newnullbitmap, addedbefore,
2253 array_bitmap_copy(newnullbitmap, 0,
2256 if (addedafter == 0)
2257 array_bitmap_copy(newnullbitmap, offset + 1,
2258 oldnullbitmap, offset + 1,
2259 oldnitems - offset - 1);
2268 * This routine sets the value of a range of array locations (specified
2269 * by upper and lower subscript values) to new values passed as
2272 * This handles both ordinary varlena arrays and fixed-length arrays.
2275 * array: the initial array object (mustn't be NULL)
2276 * nSubscripts: number of subscripts supplied (must be same for upper/lower)
2277 * upperIndx[]: the upper subscript values
2278 * lowerIndx[]: the lower subscript values
2279 * srcArray: the source for the inserted values
2280 * isNull: indicates whether srcArray is NULL
2281 * arraytyplen: pg_type.typlen for the array type
2282 * elmlen: pg_type.typlen for the array's element type
2283 * elmbyval: pg_type.typbyval for the array's element type
2284 * elmalign: pg_type.typalign for the array's element type
2287 * A new array is returned, just like the old except for the
2288 * modified range. The original array object is not changed.
2290 * For one-dimensional arrays only, we allow the array to be extended
2291 * by assigning to positions outside the existing subscript range; any
2292 * positions between the existing elements and the new ones are set to NULLs.
2293 * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
2295 * NOTE: we assume it is OK to scribble on the provided index arrays
2296 * lowerIndx[] and upperIndx[]. These are generally just temporaries.
2298 * NOTE: For assignments, we throw an error for silly subscripts etc,
2299 * rather than returning a NULL or empty array as the fetch operations do.
2302 array_set_slice(ArrayType *array,
2306 ArrayType *srcArray,
2313 ArrayType *newarray;
2336 /* Currently, assignment from a NULL source array is a no-op */
2340 if (arraytyplen > 0)
2343 * fixed-length arrays -- not got round to doing this...
2346 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2347 errmsg("updates on slices of fixed-length arrays not implemented")));
2350 /* detoast arrays if necessary */
2351 array = DatumGetArrayTypeP(PointerGetDatum(array));
2352 srcArray = DatumGetArrayTypeP(PointerGetDatum(srcArray));
2354 /* note: we assume srcArray contains no toasted elements */
2356 ndim = ARR_NDIM(array);
2359 * if number of dims is zero, i.e. an empty array, create an array with
2360 * nSubscripts dimensions, and set the upper and lower bounds to the
2361 * supplied subscripts
2368 Oid elmtype = ARR_ELEMTYPE(array);
2370 deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
2371 &dvalues, &dnulls, &nelems);
2373 for (i = 0; i < nSubscripts; i++)
2375 dim[i] = 1 + upperIndx[i] - lowerIndx[i];
2376 lb[i] = lowerIndx[i];
2379 /* complain if too few source items; we ignore extras, however */
2380 if (nelems < ArrayGetNItems(nSubscripts, dim))
2382 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2383 errmsg("source array too small")));
2385 return construct_md_array(dvalues, dnulls, nSubscripts,
2387 elmlen, elmbyval, elmalign);
2390 if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
2392 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2393 errmsg("wrong number of array subscripts")));
2395 /* copy dim/lb since we may modify them */
2396 memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
2397 memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
2399 newhasnulls = (ARR_HASNULL(array) || ARR_HASNULL(srcArray));
2400 addedbefore = addedafter = 0;
2407 Assert(nSubscripts == 1);
2408 if (lowerIndx[0] > upperIndx[0])
2410 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2411 errmsg("upper bound cannot be less than lower bound")));
2412 if (lowerIndx[0] < lb[0])
2414 if (upperIndx[0] < lb[0] - 1)
2415 newhasnulls = true; /* will insert nulls */
2416 addedbefore = lb[0] - lowerIndx[0];
2417 dim[0] += addedbefore;
2418 lb[0] = lowerIndx[0];
2420 if (upperIndx[0] >= (dim[0] + lb[0]))
2422 if (lowerIndx[0] > (dim[0] + lb[0]))
2423 newhasnulls = true; /* will insert nulls */
2424 addedafter = upperIndx[0] - (dim[0] + lb[0]) + 1;
2425 dim[0] += addedafter;
2431 * XXX currently we do not support extending multi-dimensional arrays
2434 for (i = 0; i < nSubscripts; i++)
2436 if (lowerIndx[i] > upperIndx[i])
2438 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2439 errmsg("upper bound cannot be less than lower bound")));
2440 if (lowerIndx[i] < lb[i] ||
2441 upperIndx[i] >= (dim[i] + lb[i]))
2443 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2444 errmsg("array subscript out of range")));
2446 /* fill any missing subscript positions with full array range */
2447 for (; i < ndim; i++)
2449 lowerIndx[i] = lb[i];
2450 upperIndx[i] = dim[i] + lb[i] - 1;
2451 if (lowerIndx[i] > upperIndx[i])
2453 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2454 errmsg("upper bound cannot be less than lower bound")));
2458 /* Do this mainly to check for overflow */
2459 nitems = ArrayGetNItems(ndim, dim);
2462 * Make sure source array has enough entries. Note we ignore the shape of
2463 * the source array and just read entries serially.
2465 mda_get_range(ndim, span, lowerIndx, upperIndx);
2466 nsrcitems = ArrayGetNItems(ndim, span);
2467 if (nsrcitems > ArrayGetNItems(ARR_NDIM(srcArray), ARR_DIMS(srcArray)))
2469 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2470 errmsg("source array too small")));
2473 * Compute space occupied by new entries, space occupied by replaced
2474 * entries, and required space for new array.
2477 overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
2479 overheadlen = ARR_OVERHEAD_NONULLS(ndim);
2480 newitemsize = array_nelems_size(ARR_DATA_PTR(srcArray), 0,
2481 ARR_NULLBITMAP(srcArray), nsrcitems,
2482 elmlen, elmbyval, elmalign);
2483 oldoverheadlen = ARR_DATA_OFFSET(array);
2484 olddatasize = ARR_SIZE(array) - oldoverheadlen;
2488 * here we do not need to cope with extension of the array; it would
2489 * be a lot more complicated if we had to do so...
2491 olditemsize = array_slice_size(ARR_DATA_PTR(array),
2492 ARR_NULLBITMAP(array),
2494 lowerIndx, upperIndx,
2495 elmlen, elmbyval, elmalign);
2496 lenbefore = lenafter = 0; /* keep compiler quiet */
2497 itemsbefore = itemsafter = nolditems = 0;
2502 * here we must allow for possibility of slice larger than orig array
2504 int oldlb = ARR_LBOUND(array)[0];
2505 int oldub = oldlb + ARR_DIMS(array)[0] - 1;
2506 int slicelb = Max(oldlb, lowerIndx[0]);
2507 int sliceub = Min(oldub, upperIndx[0]);
2508 char *oldarraydata = ARR_DATA_PTR(array);
2509 bits8 *oldarraybitmap = ARR_NULLBITMAP(array);
2511 itemsbefore = Min(slicelb, oldub + 1) - oldlb;
2512 lenbefore = array_nelems_size(oldarraydata, 0, oldarraybitmap,
2514 elmlen, elmbyval, elmalign);
2515 if (slicelb > sliceub)
2522 nolditems = sliceub - slicelb + 1;
2523 olditemsize = array_nelems_size(oldarraydata + lenbefore,
2524 itemsbefore, oldarraybitmap,
2526 elmlen, elmbyval, elmalign);
2528 itemsafter = oldub - sliceub;
2529 lenafter = olddatasize - lenbefore - olditemsize;
2532 newsize = overheadlen + olddatasize - olditemsize + newitemsize;
2534 newarray = (ArrayType *) palloc(newsize);
2535 SET_VARSIZE(newarray, newsize);
2536 newarray->ndim = ndim;
2537 newarray->dataoffset = newhasnulls ? overheadlen : 0;
2538 newarray->elemtype = ARR_ELEMTYPE(array);
2539 memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
2540 memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
2545 * here we do not need to cope with extension of the array; it would
2546 * be a lot more complicated if we had to do so...
2548 array_insert_slice(newarray, array, srcArray,
2550 lowerIndx, upperIndx,
2551 elmlen, elmbyval, elmalign);
2556 memcpy((char *) newarray + overheadlen,
2557 (char *) array + oldoverheadlen,
2559 memcpy((char *) newarray + overheadlen + lenbefore,
2560 ARR_DATA_PTR(srcArray),
2562 memcpy((char *) newarray + overheadlen + lenbefore + newitemsize,
2563 (char *) array + oldoverheadlen + lenbefore + olditemsize,
2565 /* fill in nulls bitmap if needed */
2568 bits8 *newnullbitmap = ARR_NULLBITMAP(newarray);
2569 bits8 *oldnullbitmap = ARR_NULLBITMAP(array);
2571 /* Zero the bitmap to handle marking inserted positions null */
2572 MemSet(newnullbitmap, 0, (nitems + 7) / 8);
2573 array_bitmap_copy(newnullbitmap, addedbefore,
2576 array_bitmap_copy(newnullbitmap, lowerIndx[0] - lb[0],
2577 ARR_NULLBITMAP(srcArray), 0,
2579 array_bitmap_copy(newnullbitmap, addedbefore + itemsbefore + nolditems,
2580 oldnullbitmap, itemsbefore + nolditems,
2591 * Map an array through an arbitrary function. Return a new array with
2592 * same dimensions and each source element transformed by fn(). Each
2593 * source element is passed as the first argument to fn(); additional
2594 * arguments to be passed to fn() can be specified by the caller.
2595 * The output array can have a different element type than the input.
2598 * * fcinfo: a function-call data structure pre-constructed by the caller
2599 * to be ready to call the desired function, with everything except the
2600 * first argument position filled in. In particular, flinfo identifies
2601 * the function fn(), and if nargs > 1 then argument positions after the
2602 * first must be preset to the additional values to be passed. The
2603 * first argument position initially holds the input array value.
2604 * * inpType: OID of element type of input array. This must be the same as,
2605 * or binary-compatible with, the first argument type of fn().
2606 * * retType: OID of element type of output array. This must be the same as,
2607 * or binary-compatible with, the result type of fn().
2608 * * amstate: workspace for array_map. Must be zeroed by caller before
2609 * first call, and not touched after that.
2611 * It is legitimate to pass a freshly-zeroed ArrayMapState on each call,
2612 * but better performance can be had if the state can be preserved across
2613 * a series of calls.
2615 * NB: caller must assure that input array is not NULL. NULL elements in
2616 * the array are OK however.
2619 array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType,
2620 ArrayMapState *amstate)
2643 ArrayMetaState *inp_extra;
2644 ArrayMetaState *ret_extra;
2646 /* Get input array */
2647 if (fcinfo->nargs < 1)
2648 elog(ERROR, "invalid nargs: %d", fcinfo->nargs);
2649 if (PG_ARGISNULL(0))
2650 elog(ERROR, "null input array");
2651 v = PG_GETARG_ARRAYTYPE_P(0);
2653 Assert(ARR_ELEMTYPE(v) == inpType);
2657 nitems = ArrayGetNItems(ndim, dim);
2659 /* Check for empty array */
2662 /* Return empty array */
2663 PG_RETURN_ARRAYTYPE_P(construct_empty_array(retType));
2667 * We arrange to look up info about input and return element types only
2668 * once per series of calls, assuming the element type doesn't change
2671 inp_extra = &amstate->inp_extra;
2672 ret_extra = &amstate->ret_extra;
2674 if (inp_extra->element_type != inpType)
2676 get_typlenbyvalalign(inpType,
2678 &inp_extra->typbyval,
2679 &inp_extra->typalign);
2680 inp_extra->element_type = inpType;
2682 inp_typlen = inp_extra->typlen;
2683 inp_typbyval = inp_extra->typbyval;
2684 inp_typalign = inp_extra->typalign;
2686 if (ret_extra->element_type != retType)
2688 get_typlenbyvalalign(retType,
2690 &ret_extra->typbyval,
2691 &ret_extra->typalign);
2692 ret_extra->element_type = retType;
2694 typlen = ret_extra->typlen;
2695 typbyval = ret_extra->typbyval;
2696 typalign = ret_extra->typalign;
2698 /* Allocate temporary arrays for new values */
2699 values = (Datum *) palloc(nitems * sizeof(Datum));
2700 nulls = (bool *) palloc(nitems * sizeof(bool));
2702 /* Loop over source data */
2703 s = ARR_DATA_PTR(v);
2704 bitmap = ARR_NULLBITMAP(v);
2708 for (i = 0; i < nitems; i++)
2712 /* Get source element, checking for NULL */
2713 if (bitmap && (*bitmap & bitmask) == 0)
2715 fcinfo->argnull[0] = true;
2719 elt = fetch_att(s, inp_typbyval, inp_typlen);
2720 s = att_addlength_datum(s, inp_typlen, elt);
2721 s = (char *) att_align_nominal(s, inp_typalign);
2722 fcinfo->arg[0] = elt;
2723 fcinfo->argnull[0] = false;
2727 * Apply the given function to source elt and extra args.
2729 if (fcinfo->flinfo->fn_strict)
2733 for (j = 0; j < fcinfo->nargs; j++)
2735 if (fcinfo->argnull[j])
2745 fcinfo->isnull = false;
2746 values[i] = FunctionCallInvoke(fcinfo);
2749 fcinfo->isnull = true;
2751 nulls[i] = fcinfo->isnull;
2756 /* Ensure data is not toasted */
2758 values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
2759 /* Update total result size */
2760 nbytes = att_addlength_datum(nbytes, typlen, values[i]);
2761 nbytes = att_align_nominal(nbytes, typalign);
2762 /* check for overflow of total request */
2763 if (!AllocSizeIsValid(nbytes))
2765 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2766 errmsg("array size exceeds the maximum allowed (%d)",
2767 (int) MaxAllocSize)));
2770 /* advance bitmap pointer if any */
2774 if (bitmask == 0x100)
2782 /* Allocate and initialize the result array */
2785 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
2786 nbytes += dataoffset;
2790 dataoffset = 0; /* marker for no null bitmap */
2791 nbytes += ARR_OVERHEAD_NONULLS(ndim);
2793 result = (ArrayType *) palloc(nbytes);
2794 SET_VARSIZE(result, nbytes);
2795 result->ndim = ndim;
2796 result->dataoffset = dataoffset;
2797 result->elemtype = retType;
2798 memcpy(ARR_DIMS(result), ARR_DIMS(v), 2 * ndim * sizeof(int));
2801 * Note: do not risk trying to pfree the results of the called function
2803 CopyArrayEls(result,
2804 values, nulls, nitems,
2805 typlen, typbyval, typalign,
2811 PG_RETURN_ARRAYTYPE_P(result);
2815 * construct_array --- simple method for constructing an array object
2817 * elems: array of Datum items to become the array contents
2818 * (NULL element values are not supported).
2819 * nelems: number of items
2820 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
2822 * A palloc'd 1-D array object is constructed and returned. Note that
2823 * elem values will be copied into the object even if pass-by-ref type.
2825 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
2826 * from the system catalogs, given the elmtype. However, the caller is
2827 * in a better position to cache this info across multiple uses, or even
2828 * to hard-wire values if the element type is hard-wired.
2831 construct_array(Datum *elems, int nelems,
2833 int elmlen, bool elmbyval, char elmalign)
2841 return construct_md_array(elems, NULL, 1, dims, lbs,
2842 elmtype, elmlen, elmbyval, elmalign);
2846 * construct_md_array --- simple method for constructing an array object
2847 * with arbitrary dimensions and possible NULLs
2849 * elems: array of Datum items to become the array contents
2850 * nulls: array of is-null flags (can be NULL if no nulls)
2851 * ndims: number of dimensions
2852 * dims: integer array with size of each dimension
2853 * lbs: integer array with lower bound of each dimension
2854 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
2856 * A palloc'd ndims-D array object is constructed and returned. Note that
2857 * elem values will be copied into the object even if pass-by-ref type.
2859 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
2860 * from the system catalogs, given the elmtype. However, the caller is
2861 * in a better position to cache this info across multiple uses, or even
2862 * to hard-wire values if the element type is hard-wired.
2865 construct_md_array(Datum *elems,
2870 Oid elmtype, int elmlen, bool elmbyval, char elmalign)
2879 if (ndims < 0) /* we do allow zero-dimension arrays */
2881 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2882 errmsg("invalid number of dimensions: %d", ndims)));
2885 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2886 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
2889 /* fast track for empty array */
2891 return construct_empty_array(elmtype);
2893 nelems = ArrayGetNItems(ndims, dims);
2895 /* compute required space */
2898 for (i = 0; i < nelems; i++)
2900 if (nulls && nulls[i])
2905 /* make sure data is not toasted */
2907 elems[i] = PointerGetDatum(PG_DETOAST_DATUM(elems[i]));
2908 nbytes = att_addlength_datum(nbytes, elmlen, elems[i]);
2909 nbytes = att_align_nominal(nbytes, elmalign);
2910 /* check for overflow of total request */
2911 if (!AllocSizeIsValid(nbytes))
2913 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2914 errmsg("array size exceeds the maximum allowed (%d)",
2915 (int) MaxAllocSize)));
2918 /* Allocate and initialize result array */
2921 dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nelems);
2922 nbytes += dataoffset;
2926 dataoffset = 0; /* marker for no null bitmap */
2927 nbytes += ARR_OVERHEAD_NONULLS(ndims);
2929 result = (ArrayType *) palloc(nbytes);
2930 SET_VARSIZE(result, nbytes);
2931 result->ndim = ndims;
2932 result->dataoffset = dataoffset;
2933 result->elemtype = elmtype;
2934 memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
2935 memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
2937 CopyArrayEls(result,
2938 elems, nulls, nelems,
2939 elmlen, elmbyval, elmalign,
2946 * construct_empty_array --- make a zero-dimensional array of given type
2949 construct_empty_array(Oid elmtype)
2953 result = (ArrayType *) palloc(sizeof(ArrayType));
2954 SET_VARSIZE(result, sizeof(ArrayType));
2956 result->dataoffset = 0;
2957 result->elemtype = elmtype;
2962 * deconstruct_array --- simple method for extracting data from an array
2964 * array: array object to examine (must not be NULL)
2965 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
2966 * elemsp: return value, set to point to palloc'd array of Datum values
2967 * nullsp: return value, set to point to palloc'd array of isnull markers
2968 * nelemsp: return value, set to number of extracted values
2970 * The caller may pass nullsp == NULL if it does not support NULLs in the
2971 * array. Note that this produces a very uninformative error message,
2972 * so do it only in cases where a NULL is really not expected.
2974 * If array elements are pass-by-ref data type, the returned Datums will
2975 * be pointers into the array object.
2977 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
2978 * from the system catalogs, given the elmtype. However, in most current
2979 * uses the type is hard-wired into the caller and so we can save a lookup
2980 * cycle by hard-wiring the type info as well.
2983 deconstruct_array(ArrayType *array,
2985 int elmlen, bool elmbyval, char elmalign,
2986 Datum **elemsp, bool **nullsp, int *nelemsp)
2996 Assert(ARR_ELEMTYPE(array) == elmtype);
2998 nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
2999 *elemsp = elems = (Datum *) palloc(nelems * sizeof(Datum));
3001 *nullsp = nulls = (bool *) palloc0(nelems * sizeof(bool));
3006 p = ARR_DATA_PTR(array);
3007 bitmap = ARR_NULLBITMAP(array);
3010 for (i = 0; i < nelems; i++)
3012 /* Get source element, checking for NULL */
3013 if (bitmap && (*bitmap & bitmask) == 0)
3015 elems[i] = (Datum) 0;
3020 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
3021 errmsg("null array element not allowed in this context")));
3025 elems[i] = fetch_att(p, elmbyval, elmlen);
3026 p = att_addlength_pointer(p, elmlen, p);
3027 p = (char *) att_align_nominal(p, elmalign);
3030 /* advance bitmap pointer if any */
3034 if (bitmask == 0x100)
3044 * array_contains_nulls --- detect whether an array has any null elements
3046 * This gives an accurate answer, whereas testing ARR_HASNULL only tells
3047 * if the array *might* contain a null.
3050 array_contains_nulls(ArrayType *array)
3056 /* Easy answer if there's no null bitmap */
3057 if (!ARR_HASNULL(array))
3060 nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
3062 bitmap = ARR_NULLBITMAP(array);
3064 /* check whole bytes of the bitmap byte-at-a-time */
3067 if (*bitmap != 0xFF)
3073 /* check last partial byte */
3077 if ((*bitmap & bitmask) == 0)
3089 * compares two arrays for equality
3091 * returns true if the arrays are equal, false otherwise.
3093 * Note: we do not use array_cmp here, since equality may be meaningful in
3094 * datatypes that don't have a total ordering (and hence no btree support).
3097 array_eq(PG_FUNCTION_ARGS)
3099 ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
3100 ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
3101 int ndims1 = ARR_NDIM(array1);
3102 int ndims2 = ARR_NDIM(array2);
3103 int *dims1 = ARR_DIMS(array1);
3104 int *dims2 = ARR_DIMS(array2);
3105 Oid element_type = ARR_ELEMTYPE(array1);
3108 TypeCacheEntry *typentry;
3118 FunctionCallInfoData locfcinfo;
3120 if (element_type != ARR_ELEMTYPE(array2))
3122 (errcode(ERRCODE_DATATYPE_MISMATCH),
3123 errmsg("cannot compare arrays of different element types")));
3125 /* fast path if the arrays do not have the same dimensionality */
3126 if (ndims1 != ndims2 ||
3127 memcmp(dims1, dims2, 2 * ndims1 * sizeof(int)) != 0)
3132 * We arrange to look up the equality function only once per series of
3133 * calls, assuming the element type doesn't change underneath us. The
3134 * typcache is used so that we have no memory leakage when being used
3135 * as an index support function.
3137 typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3138 if (typentry == NULL ||
3139 typentry->type_id != element_type)
3141 typentry = lookup_type_cache(element_type,
3142 TYPECACHE_EQ_OPR_FINFO);
3143 if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
3145 (errcode(ERRCODE_UNDEFINED_FUNCTION),
3146 errmsg("could not identify an equality operator for type %s",
3147 format_type_be(element_type))));
3148 fcinfo->flinfo->fn_extra = (void *) typentry;
3150 typlen = typentry->typlen;
3151 typbyval = typentry->typbyval;
3152 typalign = typentry->typalign;
3155 * apply the operator to each pair of array elements.
3157 InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
3160 /* Loop over source data */
3161 nitems = ArrayGetNItems(ndims1, dims1);
3162 ptr1 = ARR_DATA_PTR(array1);
3163 ptr2 = ARR_DATA_PTR(array2);
3164 bitmap1 = ARR_NULLBITMAP(array1);
3165 bitmap2 = ARR_NULLBITMAP(array2);
3166 bitmask = 1; /* use same bitmask for both arrays */
3168 for (i = 0; i < nitems; i++)
3176 /* Get elements, checking for NULL */
3177 if (bitmap1 && (*bitmap1 & bitmask) == 0)
3185 elt1 = fetch_att(ptr1, typbyval, typlen);
3186 ptr1 = att_addlength_pointer(ptr1, typlen, ptr1);
3187 ptr1 = (char *) att_align_nominal(ptr1, typalign);
3190 if (bitmap2 && (*bitmap2 & bitmask) == 0)
3198 elt2 = fetch_att(ptr2, typbyval, typlen);
3199 ptr2 = att_addlength_pointer(ptr2, typlen, ptr2);
3200 ptr2 = (char *) att_align_nominal(ptr2, typalign);
3203 /* advance bitmap pointers if any */
3205 if (bitmask == 0x100)
3215 * We consider two NULLs equal; NULL and not-NULL are unequal.
3217 if (isnull1 && isnull2)
3219 if (isnull1 || isnull2)
3226 * Apply the operator to the element pair
3228 locfcinfo.arg[0] = elt1;
3229 locfcinfo.arg[1] = elt2;
3230 locfcinfo.argnull[0] = false;
3231 locfcinfo.argnull[1] = false;
3232 locfcinfo.isnull = false;
3233 oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
3242 /* Avoid leaking memory when handed toasted input. */
3243 PG_FREE_IF_COPY(array1, 0);
3244 PG_FREE_IF_COPY(array2, 1);
3246 PG_RETURN_BOOL(result);
3250 /*-----------------------------------------------------------------------------
3251 * array-array bool operators:
3252 * Given two arrays, iterate comparison operators
3253 * over the array. Uses logic similar to text comparison
3254 * functions, except element-by-element instead of
3255 * character-by-character.
3256 *----------------------------------------------------------------------------
3260 array_ne(PG_FUNCTION_ARGS)
3262 PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo)));
3266 array_lt(PG_FUNCTION_ARGS)
3268 PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
3272 array_gt(PG_FUNCTION_ARGS)
3274 PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
3278 array_le(PG_FUNCTION_ARGS)
3280 PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
3284 array_ge(PG_FUNCTION_ARGS)
3286 PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
3290 btarraycmp(PG_FUNCTION_ARGS)
3292 PG_RETURN_INT32(array_cmp(fcinfo));
3297 * Internal comparison function for arrays.
3299 * Returns -1, 0 or 1
3302 array_cmp(FunctionCallInfo fcinfo)
3304 ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
3305 ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
3306 int ndims1 = ARR_NDIM(array1);
3307 int ndims2 = ARR_NDIM(array2);
3308 int *dims1 = ARR_DIMS(array1);
3309 int *dims2 = ARR_DIMS(array2);
3310 int nitems1 = ArrayGetNItems(ndims1, dims1);
3311 int nitems2 = ArrayGetNItems(ndims2, dims2);
3312 Oid element_type = ARR_ELEMTYPE(array1);
3314 TypeCacheEntry *typentry;
3325 FunctionCallInfoData locfcinfo;
3327 if (element_type != ARR_ELEMTYPE(array2))
3329 (errcode(ERRCODE_DATATYPE_MISMATCH),
3330 errmsg("cannot compare arrays of different element types")));
3333 * We arrange to look up the comparison function only once per series of
3334 * calls, assuming the element type doesn't change underneath us. The
3335 * typcache is used so that we have no memory leakage when being used as
3336 * an index support function.
3338 typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3339 if (typentry == NULL ||
3340 typentry->type_id != element_type)
3342 typentry = lookup_type_cache(element_type,
3343 TYPECACHE_CMP_PROC_FINFO);
3344 if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
3346 (errcode(ERRCODE_UNDEFINED_FUNCTION),
3347 errmsg("could not identify a comparison function for type %s",
3348 format_type_be(element_type))));
3349 fcinfo->flinfo->fn_extra = (void *) typentry;
3351 typlen = typentry->typlen;
3352 typbyval = typentry->typbyval;
3353 typalign = typentry->typalign;
3356 * apply the operator to each pair of array elements.
3358 InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2,
3361 /* Loop over source data */
3362 min_nitems = Min(nitems1, nitems2);
3363 ptr1 = ARR_DATA_PTR(array1);
3364 ptr2 = ARR_DATA_PTR(array2);
3365 bitmap1 = ARR_NULLBITMAP(array1);
3366 bitmap2 = ARR_NULLBITMAP(array2);
3367 bitmask = 1; /* use same bitmask for both arrays */
3369 for (i = 0; i < min_nitems; i++)
3377 /* Get elements, checking for NULL */
3378 if (bitmap1 && (*bitmap1 & bitmask) == 0)
3386 elt1 = fetch_att(ptr1, typbyval, typlen);
3387 ptr1 = att_addlength_pointer(ptr1, typlen, ptr1);
3388 ptr1 = (char *) att_align_nominal(ptr1, typalign);
3391 if (bitmap2 && (*bitmap2 & bitmask) == 0)
3399 elt2 = fetch_att(ptr2, typbyval, typlen);
3400 ptr2 = att_addlength_pointer(ptr2, typlen, ptr2);
3401 ptr2 = (char *) att_align_nominal(ptr2, typalign);
3404 /* advance bitmap pointers if any */
3406 if (bitmask == 0x100)
3416 * We consider two NULLs equal; NULL > not-NULL.
3418 if (isnull1 && isnull2)
3422 /* arg1 is greater than arg2 */
3428 /* arg1 is less than arg2 */
3433 /* Compare the pair of elements */
3434 locfcinfo.arg[0] = elt1;
3435 locfcinfo.arg[1] = elt2;
3436 locfcinfo.argnull[0] = false;
3437 locfcinfo.argnull[1] = false;
3438 locfcinfo.isnull = false;
3439 cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
3442 continue; /* equal */
3446 /* arg1 is less than arg2 */
3452 /* arg1 is greater than arg2 */
3459 * If arrays contain same data (up to end of shorter one), apply
3460 * additional rules to sort by dimensionality. The relative significance
3461 * of the different bits of information is historical; mainly we just care
3462 * that we don't say "equal" for arrays of different dimensionality.
3466 if (nitems1 != nitems2)
3467 result = (nitems1 < nitems2) ? -1 : 1;
3468 else if (ndims1 != ndims2)
3469 result = (ndims1 < ndims2) ? -1 : 1;
3472 /* this relies on LB array immediately following DIMS array */
3473 for (i = 0; i < ndims1 * 2; i++)
3475 if (dims1[i] != dims2[i])
3477 result = (dims1[i] < dims2[i]) ? -1 : 1;
3484 /* Avoid leaking memory when handed toasted input. */
3485 PG_FREE_IF_COPY(array1, 0);
3486 PG_FREE_IF_COPY(array2, 1);
3492 /*-----------------------------------------------------------------------------
3494 * Hash the elements and combine the results.
3495 *----------------------------------------------------------------------------
3499 hash_array(PG_FUNCTION_ARGS)
3501 ArrayType *array = PG_GETARG_ARRAYTYPE_P(0);
3502 int ndims = ARR_NDIM(array);
3503 int *dims = ARR_DIMS(array);
3504 Oid element_type = ARR_ELEMTYPE(array);
3507 TypeCacheEntry *typentry;
3515 FunctionCallInfoData locfcinfo;
3518 * We arrange to look up the hash function only once per series of calls,
3519 * assuming the element type doesn't change underneath us. The typcache
3520 * is used so that we have no memory leakage when being used as an index
3523 typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3524 if (typentry == NULL ||
3525 typentry->type_id != element_type)
3527 typentry = lookup_type_cache(element_type,
3528 TYPECACHE_HASH_PROC_FINFO);
3529 if (!OidIsValid(typentry->hash_proc_finfo.fn_oid))
3531 (errcode(ERRCODE_UNDEFINED_FUNCTION),
3532 errmsg("could not identify a hash function for type %s",
3533 format_type_be(element_type))));
3534 fcinfo->flinfo->fn_extra = (void *) typentry;
3536 typlen = typentry->typlen;
3537 typbyval = typentry->typbyval;
3538 typalign = typentry->typalign;
3541 * apply the hash function to each array element.
3543 InitFunctionCallInfoData(locfcinfo, &typentry->hash_proc_finfo, 1,
3546 /* Loop over source data */
3547 nitems = ArrayGetNItems(ndims, dims);
3548 ptr = ARR_DATA_PTR(array);
3549 bitmap = ARR_NULLBITMAP(array);
3552 for (i = 0; i < nitems; i++)
3556 /* Get element, checking for NULL */
3557 if (bitmap && (*bitmap & bitmask) == 0)
3559 /* Treat nulls as having hashvalue 0 */
3566 elt = fetch_att(ptr, typbyval, typlen);
3567 ptr = att_addlength_pointer(ptr, typlen, ptr);
3568 ptr = (char *) att_align_nominal(ptr, typalign);
3570 /* Apply the hash function */
3571 locfcinfo.arg[0] = elt;
3572 locfcinfo.argnull[0] = false;
3573 locfcinfo.isnull = false;
3574 elthash = DatumGetUInt32(FunctionCallInvoke(&locfcinfo));
3577 /* advance bitmap pointer if any */
3581 if (bitmask == 0x100)
3589 * Combine hash values of successive elements by rotating the previous
3590 * value left 1 bit, then XOR'ing in the new element's hash value.
3592 result = (result << 1) | (result >> 31);
3596 /* Avoid leaking memory when handed toasted input. */
3597 PG_FREE_IF_COPY(array, 0);
3599 PG_RETURN_UINT32(result);
3603 /*-----------------------------------------------------------------------------
3604 * array overlap/containment comparisons
3605 * These use the same methods of comparing array elements as array_eq.
3606 * We consider only the elements of the arrays, ignoring dimensionality.
3607 *----------------------------------------------------------------------------
3611 * array_contain_compare :
3612 * compares two arrays for overlap/containment
3614 * When matchall is true, return true if all members of array1 are in array2.
3615 * When matchall is false, return true if any members of array1 are in array2.
3618 array_contain_compare(ArrayType *array1, ArrayType *array2, bool matchall,
3621 bool result = matchall;
3622 Oid element_type = ARR_ELEMTYPE(array1);
3623 TypeCacheEntry *typentry;
3636 FunctionCallInfoData locfcinfo;
3638 if (element_type != ARR_ELEMTYPE(array2))
3640 (errcode(ERRCODE_DATATYPE_MISMATCH),
3641 errmsg("cannot compare arrays of different element types")));
3644 * We arrange to look up the equality function only once per series of
3645 * calls, assuming the element type doesn't change underneath us. The
3646 * typcache is used so that we have no memory leakage when being used as
3647 * an index support function.
3649 typentry = (TypeCacheEntry *) *fn_extra;
3650 if (typentry == NULL ||
3651 typentry->type_id != element_type)
3653 typentry = lookup_type_cache(element_type,
3654 TYPECACHE_EQ_OPR_FINFO);
3655 if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
3657 (errcode(ERRCODE_UNDEFINED_FUNCTION),
3658 errmsg("could not identify an equality operator for type %s",
3659 format_type_be(element_type))));
3660 *fn_extra = (void *) typentry;
3662 typlen = typentry->typlen;
3663 typbyval = typentry->typbyval;
3664 typalign = typentry->typalign;
3667 * Since we probably will need to scan array2 multiple times, it's
3668 * worthwhile to use deconstruct_array on it. We scan array1 the hard way
3669 * however, since we very likely won't need to look at all of it.
3671 deconstruct_array(array2, element_type, typlen, typbyval, typalign,
3672 &values2, &nulls2, &nelems2);
3675 * Apply the comparison operator to each pair of array elements.
3677 InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
3680 /* Loop over source data */
3681 nelems1 = ArrayGetNItems(ARR_NDIM(array1), ARR_DIMS(array1));
3682 ptr1 = ARR_DATA_PTR(array1);
3683 bitmap1 = ARR_NULLBITMAP(array1);
3686 for (i = 0; i < nelems1; i++)
3691 /* Get element, checking for NULL */
3692 if (bitmap1 && (*bitmap1 & bitmask) == 0)
3700 elt1 = fetch_att(ptr1, typbyval, typlen);
3701 ptr1 = att_addlength_pointer(ptr1, typlen, ptr1);
3702 ptr1 = (char *) att_align_nominal(ptr1, typalign);
3705 /* advance bitmap pointer if any */
3707 if (bitmask == 0x100)
3715 * We assume that the comparison operator is strict, so a NULL can't
3716 * match anything. XXX this diverges from the "NULL=NULL" behavior of
3717 * array_eq, should we act like that?
3729 for (j = 0; j < nelems2; j++)
3731 Datum elt2 = values2[j];
3732 bool isnull2 = nulls2[j];
3736 continue; /* can't match */
3739 * Apply the operator to the element pair
3741 locfcinfo.arg[0] = elt1;
3742 locfcinfo.arg[1] = elt2;
3743 locfcinfo.argnull[0] = false;
3744 locfcinfo.argnull[1] = false;
3745 locfcinfo.isnull = false;
3746 oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
3753 /* found a match for elt1 */
3762 /* no match for elt1 */
3778 arrayoverlap(PG_FUNCTION_ARGS)
3780 ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
3781 ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
3784 result = array_contain_compare(array1, array2, false,
3785 &fcinfo->flinfo->fn_extra);
3787 /* Avoid leaking memory when handed toasted input. */
3788 PG_FREE_IF_COPY(array1, 0);
3789 PG_FREE_IF_COPY(array2, 1);
3791 PG_RETURN_BOOL(result);
3795 arraycontains(PG_FUNCTION_ARGS)
3797 ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
3798 ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
3801 result = array_contain_compare(array2, array1, true,
3802 &fcinfo->flinfo->fn_extra);
3804 /* Avoid leaking memory when handed toasted input. */
3805 PG_FREE_IF_COPY(array1, 0);
3806 PG_FREE_IF_COPY(array2, 1);
3808 PG_RETURN_BOOL(result);
3812 arraycontained(PG_FUNCTION_ARGS)
3814 ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
3815 ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
3818 result = array_contain_compare(array1, array2, true,
3819 &fcinfo->flinfo->fn_extra);
3821 /* Avoid leaking memory when handed toasted input. */
3822 PG_FREE_IF_COPY(array1, 0);
3823 PG_FREE_IF_COPY(array2, 1);
3825 PG_RETURN_BOOL(result);
3829 /***************************************************************************/
3830 /******************| Support Routines |*****************/
3831 /***************************************************************************/
3834 * Check whether a specific array element is NULL
3836 * nullbitmap: pointer to array's null bitmap (NULL if none)
3837 * offset: 0-based linear element number of array element
3840 array_get_isnull(const bits8 *nullbitmap, int offset)
3842 if (nullbitmap == NULL)
3843 return false; /* assume not null */
3844 if (nullbitmap[offset / 8] & (1 << (offset % 8)))
3845 return false; /* not null */
3850 * Set a specific array element's null-bitmap entry
3852 * nullbitmap: pointer to array's null bitmap (mustn't be NULL)
3853 * offset: 0-based linear element number of array element
3854 * isNull: null status to set
3857 array_set_isnull(bits8 *nullbitmap, int offset, bool isNull)
3861 nullbitmap += offset / 8;
3862 bitmask = 1 << (offset % 8);
3864 *nullbitmap &= ~bitmask;
3866 *nullbitmap |= bitmask;
3870 * Fetch array element at pointer, converted correctly to a Datum
3872 * Caller must have handled case of NULL element
3875 ArrayCast(char *value, bool byval, int len)
3877 return fetch_att(value, byval, len);
3881 * Copy datum to *dest and return total space used (including align padding)
3883 * Caller must have handled case of NULL element
3886 ArrayCastAndSet(Datum src,
3897 store_att_byval(dest, src, typlen);
3899 memmove(dest, DatumGetPointer(src), typlen);
3900 inc = att_align_nominal(typlen, typalign);
3905 inc = att_addlength_datum(0, typlen, src);
3906 memmove(dest, DatumGetPointer(src), inc);
3907 inc = att_align_nominal(inc, typalign);
3914 * Advance ptr over nitems array elements
3916 * ptr: starting location in array
3917 * offset: 0-based linear element number of first element (the one at *ptr)
3918 * nullbitmap: start of array's null bitmap, or NULL if none
3919 * nitems: number of array elements to advance over (>= 0)
3920 * typlen, typbyval, typalign: storage parameters of array element datatype
3922 * It is caller's responsibility to ensure that nitems is within range
3925 array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
3926 int typlen, bool typbyval, char typalign)
3931 /* easy if fixed-size elements and no NULLs */
3932 if (typlen > 0 && !nullbitmap)
3933 return ptr + nitems * ((Size) att_align_nominal(typlen, typalign));
3935 /* seems worth having separate loops for NULL and no-NULLs cases */
3938 nullbitmap += offset / 8;
3939 bitmask = 1 << (offset % 8);
3941 for (i = 0; i < nitems; i++)
3943 if (*nullbitmap & bitmask)
3945 ptr = att_addlength_pointer(ptr, typlen, ptr);
3946 ptr = (char *) att_align_nominal(ptr, typalign);
3949 if (bitmask == 0x100)
3958 for (i = 0; i < nitems; i++)
3960 ptr = att_addlength_pointer(ptr, typlen, ptr);
3961 ptr = (char *) att_align_nominal(ptr, typalign);
3968 * Compute total size of the nitems array elements starting at *ptr
3970 * Parameters same as for array_seek
3973 array_nelems_size(char *ptr, int offset, bits8 *nullbitmap, int nitems,
3974 int typlen, bool typbyval, char typalign)
3976 return array_seek(ptr, offset, nullbitmap, nitems,
3977 typlen, typbyval, typalign) - ptr;
3981 * Copy nitems array elements from srcptr to destptr
3983 * destptr: starting destination location (must be enough room!)
3984 * nitems: number of array elements to copy (>= 0)
3985 * srcptr: starting location in source array
3986 * offset: 0-based linear element number of first element (the one at *srcptr)
3987 * nullbitmap: start of source array's null bitmap, or NULL if none
3988 * typlen, typbyval, typalign: storage parameters of array element datatype
3990 * Returns number of bytes copied
3992 * NB: this does not take care of setting up the destination's null bitmap!
3995 array_copy(char *destptr, int nitems,
3996 char *srcptr, int offset, bits8 *nullbitmap,
3997 int typlen, bool typbyval, char typalign)
4001 numbytes = array_nelems_size(srcptr, offset, nullbitmap, nitems,
4002 typlen, typbyval, typalign);
4003 memcpy(destptr, srcptr, numbytes);
4008 * Copy nitems null-bitmap bits from source to destination
4010 * destbitmap: start of destination array's null bitmap (mustn't be NULL)
4011 * destoffset: 0-based linear element number of first dest element
4012 * srcbitmap: start of source array's null bitmap, or NULL if none
4013 * srcoffset: 0-based linear element number of first source element
4014 * nitems: number of bits to copy (>= 0)
4016 * If srcbitmap is NULL then we assume the source is all-non-NULL and
4017 * fill 1's into the destination bitmap. Note that only the specified
4018 * bits in the destination map are changed, not any before or after.
4020 * Note: this could certainly be optimized using standard bitblt methods.
4021 * However, it's not clear that the typical Postgres array has enough elements
4022 * to make it worth worrying too much. For the moment, KISS.
4025 array_bitmap_copy(bits8 *destbitmap, int destoffset,
4026 const bits8 *srcbitmap, int srcoffset,
4036 return; /* don't risk fetch off end of memory */
4037 destbitmap += destoffset / 8;
4038 destbitmask = 1 << (destoffset % 8);
4039 destbitval = *destbitmap;
4042 srcbitmap += srcoffset / 8;
4043 srcbitmask = 1 << (srcoffset % 8);
4044 srcbitval = *srcbitmap;
4045 while (nitems-- > 0)
4047 if (srcbitval & srcbitmask)
4048 destbitval |= destbitmask;
4050 destbitval &= ~destbitmask;
4052 if (destbitmask == 0x100)
4054 *destbitmap++ = destbitval;
4057 destbitval = *destbitmap;
4060 if (srcbitmask == 0x100)
4065 srcbitval = *srcbitmap;
4068 if (destbitmask != 1)
4069 *destbitmap = destbitval;
4073 while (nitems-- > 0)
4075 destbitval |= destbitmask;
4077 if (destbitmask == 0x100)
4079 *destbitmap++ = destbitval;
4082 destbitval = *destbitmap;
4085 if (destbitmask != 1)
4086 *destbitmap = destbitval;
4091 * Compute space needed for a slice of an array
4093 * We assume the caller has verified that the slice coordinates are valid.
4096 array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
4097 int ndim, int *dim, int *lb,
4099 int typlen, bool typbyval, char typalign)
4112 mda_get_range(ndim, span, st, endp);
4114 /* Pretty easy for fixed element length without nulls ... */
4115 if (typlen > 0 && !arraynullsptr)
4116 return ArrayGetNItems(ndim, span) * att_align_nominal(typlen, typalign);
4118 /* Else gotta do it the hard way */
4119 src_offset = ArrayGetOffset(ndim, dim, lb, st);
4120 ptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
4121 typlen, typbyval, typalign);
4122 mda_get_prod(ndim, dim, prod);
4123 mda_get_offset_values(ndim, dist, prod, span);
4124 for (i = 0; i < ndim; i++)
4131 ptr = array_seek(ptr, src_offset, arraynullsptr, dist[j],
4132 typlen, typbyval, typalign);
4133 src_offset += dist[j];
4135 if (!array_get_isnull(arraynullsptr, src_offset))
4137 inc = att_addlength_pointer(0, typlen, ptr);
4138 inc = att_align_nominal(inc, typalign);
4143 } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
4148 * Extract a slice of an array into consecutive elements in the destination
4151 * We assume the caller has verified that the slice coordinates are valid,
4152 * allocated enough storage for the result, and initialized the header
4156 array_extract_slice(ArrayType *newarray,
4161 bits8 *arraynullsptr,
4168 char *destdataptr = ARR_DATA_PTR(newarray);
4169 bits8 *destnullsptr = ARR_NULLBITMAP(newarray);
4181 src_offset = ArrayGetOffset(ndim, dim, lb, st);
4182 srcdataptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
4183 typlen, typbyval, typalign);
4184 mda_get_prod(ndim, dim, prod);
4185 mda_get_range(ndim, span, st, endp);
4186 mda_get_offset_values(ndim, dist, prod, span);
4187 for (i = 0; i < ndim; i++)
4195 /* skip unwanted elements */
4196 srcdataptr = array_seek(srcdataptr, src_offset, arraynullsptr,
4198 typlen, typbyval, typalign);
4199 src_offset += dist[j];
4201 inc = array_copy(destdataptr, 1,
4202 srcdataptr, src_offset, arraynullsptr,
4203 typlen, typbyval, typalign);
4205 array_bitmap_copy(destnullsptr, dest_offset,
4206 arraynullsptr, src_offset,
4212 } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
4216 * Insert a slice into an array.
4218 * ndim/dim[]/lb[] are dimensions of the original array. A new array with
4219 * those same dimensions is to be constructed. destArray must already
4220 * have been allocated and its header initialized.
4222 * st[]/endp[] identify the slice to be replaced. Elements within the slice
4223 * volume are taken from consecutive elements of the srcArray; elements
4224 * outside it are copied from origArray.
4226 * We assume the caller has verified that the slice coordinates are valid.
4229 array_insert_slice(ArrayType *destArray,
4230 ArrayType *origArray,
4231 ArrayType *srcArray,
4241 char *destPtr = ARR_DATA_PTR(destArray);
4242 char *origPtr = ARR_DATA_PTR(origArray);
4243 char *srcPtr = ARR_DATA_PTR(srcArray);
4244 bits8 *destBitmap = ARR_NULLBITMAP(destArray);
4245 bits8 *origBitmap = ARR_NULLBITMAP(origArray);
4246 bits8 *srcBitmap = ARR_NULLBITMAP(srcArray);
4247 int orignitems = ArrayGetNItems(ARR_NDIM(origArray),
4248 ARR_DIMS(origArray));
4260 dest_offset = ArrayGetOffset(ndim, dim, lb, st);
4261 /* copy items before the slice start */
4262 inc = array_copy(destPtr, dest_offset,
4263 origPtr, 0, origBitmap,
4264 typlen, typbyval, typalign);
4268 array_bitmap_copy(destBitmap, 0, origBitmap, 0, dest_offset);
4269 orig_offset = dest_offset;
4270 mda_get_prod(ndim, dim, prod);
4271 mda_get_range(ndim, span, st, endp);
4272 mda_get_offset_values(ndim, dist, prod, span);
4273 for (i = 0; i < ndim; i++)
4279 /* Copy/advance over elements between here and next part of slice */
4282 inc = array_copy(destPtr, dist[j],
4283 origPtr, orig_offset, origBitmap,
4284 typlen, typbyval, typalign);
4288 array_bitmap_copy(destBitmap, dest_offset,
4289 origBitmap, orig_offset,
4291 dest_offset += dist[j];
4292 orig_offset += dist[j];
4294 /* Copy new element at this slice position */
4295 inc = array_copy(destPtr, 1,
4296 srcPtr, src_offset, srcBitmap,
4297 typlen, typbyval, typalign);
4299 array_bitmap_copy(destBitmap, dest_offset,
4300 srcBitmap, src_offset,
4306 /* Advance over old element at this slice position */
4307 origPtr = array_seek(origPtr, orig_offset, origBitmap, 1,
4308 typlen, typbyval, typalign);
4310 } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
4312 /* don't miss any data at the end */
4313 array_copy(destPtr, orignitems - orig_offset,
4314 origPtr, orig_offset, origBitmap,
4315 typlen, typbyval, typalign);
4317 array_bitmap_copy(destBitmap, dest_offset,
4318 origBitmap, orig_offset,
4319 orignitems - orig_offset);
4323 * accumArrayResult - accumulate one (more) Datum for an array result
4325 * astate is working state (NULL on first call)
4326 * rcontext is where to keep working state
4329 accumArrayResult(ArrayBuildState *astate,
4330 Datum dvalue, bool disnull,
4332 MemoryContext rcontext)
4334 MemoryContext arr_context,
4339 /* First time through --- initialize */
4341 /* Make a temporary context to hold all the junk */
4342 arr_context = AllocSetContextCreate(rcontext,
4344 ALLOCSET_DEFAULT_MINSIZE,
4345 ALLOCSET_DEFAULT_INITSIZE,
4346 ALLOCSET_DEFAULT_MAXSIZE);
4347 oldcontext = MemoryContextSwitchTo(arr_context);
4348 astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
4349 astate->mcontext = arr_context;
4350 astate->alen = 64; /* arbitrary starting array size */
4351 astate->dvalues = (Datum *) palloc(astate->alen * sizeof(Datum));
4352 astate->dnulls = (bool *) palloc(astate->alen * sizeof(bool));
4354 astate->element_type = element_type;
4355 get_typlenbyvalalign(element_type,
4362 oldcontext = MemoryContextSwitchTo(astate->mcontext);
4363 Assert(astate->element_type == element_type);
4364 /* enlarge dvalues[]/dnulls[] if needed */
4365 if (astate->nelems >= astate->alen)
4368 astate->dvalues = (Datum *)
4369 repalloc(astate->dvalues, astate->alen * sizeof(Datum));
4370 astate->dnulls = (bool *)
4371 repalloc(astate->dnulls, astate->alen * sizeof(bool));
4376 * Ensure pass-by-ref stuff is copied into mcontext; and detoast it too if
4377 * it's varlena. (You might think that detoasting is not needed here
4378 * because construct_md_array can detoast the array elements later.
4379 * However, we must not let construct_md_array modify the ArrayBuildState
4380 * because that would mean array_agg_finalfn damages its input, which is
4381 * verboten. Also, this way frequently saves one copying step.)
4383 if (!disnull && !astate->typbyval)
4385 if (astate->typlen == -1)
4386 dvalue = PointerGetDatum(PG_DETOAST_DATUM_COPY(dvalue));
4388 dvalue = datumCopy(dvalue, astate->typbyval, astate->typlen);
4391 astate->dvalues[astate->nelems] = dvalue;
4392 astate->dnulls[astate->nelems] = disnull;
4395 MemoryContextSwitchTo(oldcontext);
4401 * makeArrayResult - produce 1-D final result of accumArrayResult
4403 * astate is working state (not NULL)
4404 * rcontext is where to construct result
4407 makeArrayResult(ArrayBuildState *astate,
4408 MemoryContext rcontext)
4413 dims[0] = astate->nelems;
4416 return makeMdArrayResult(astate, 1, dims, lbs, rcontext, true);
4420 * makeMdArrayResult - produce multi-D final result of accumArrayResult
4422 * beware: no check that specified dimensions match the number of values
4425 * astate is working state (not NULL)
4426 * rcontext is where to construct result
4427 * release is true if okay to release working state
4430 makeMdArrayResult(ArrayBuildState *astate,
4434 MemoryContext rcontext,
4438 MemoryContext oldcontext;
4440 /* Build the final array result in rcontext */
4441 oldcontext = MemoryContextSwitchTo(rcontext);
4443 result = construct_md_array(astate->dvalues,
4448 astate->element_type,
4453 MemoryContextSwitchTo(oldcontext);
4455 /* Clean up all the junk */
4457 MemoryContextDelete(astate->mcontext);
4459 return PointerGetDatum(result);
4463 array_larger(PG_FUNCTION_ARGS)
4469 v1 = PG_GETARG_ARRAYTYPE_P(0);
4470 v2 = PG_GETARG_ARRAYTYPE_P(1);
4472 result = ((array_cmp(fcinfo) > 0) ? v1 : v2);
4474 PG_RETURN_ARRAYTYPE_P(result);
4478 array_smaller(PG_FUNCTION_ARGS)
4484 v1 = PG_GETARG_ARRAYTYPE_P(0);
4485 v2 = PG_GETARG_ARRAYTYPE_P(1);
4487 result = ((array_cmp(fcinfo) < 0) ? v1 : v2);
4489 PG_RETURN_ARRAYTYPE_P(result);
4493 typedef struct generate_subscripts_fctx
4498 } generate_subscripts_fctx;
4501 * generate_subscripts(array anyarray, dim int [, reverse bool])
4502 * Returns all subscripts of the array for any dimension
4505 generate_subscripts(PG_FUNCTION_ARGS)
4507 FuncCallContext *funcctx;
4508 MemoryContext oldcontext;
4509 generate_subscripts_fctx *fctx;
4511 /* stuff done only on the first call of the function */
4512 if (SRF_IS_FIRSTCALL())
4514 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
4515 int reqdim = PG_GETARG_INT32(1);
4519 /* create a function context for cross-call persistence */
4520 funcctx = SRF_FIRSTCALL_INIT();
4522 /* Sanity check: does it look like an array at all? */
4523 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
4524 SRF_RETURN_DONE(funcctx);
4526 /* Sanity check: was the requested dim valid */
4527 if (reqdim <= 0 || reqdim > ARR_NDIM(v))
4528 SRF_RETURN_DONE(funcctx);
4531 * switch to memory context appropriate for multiple function calls
4533 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
4534 fctx = (generate_subscripts_fctx *) palloc(sizeof(generate_subscripts_fctx));
4539 fctx->lower = lb[reqdim - 1];
4540 fctx->upper = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
4541 fctx->reverse = (PG_NARGS() < 3) ? false : PG_GETARG_BOOL(2);
4543 funcctx->user_fctx = fctx;
4545 MemoryContextSwitchTo(oldcontext);
4548 funcctx = SRF_PERCALL_SETUP();
4550 fctx = funcctx->user_fctx;
4552 if (fctx->lower <= fctx->upper)
4555 SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->lower++));
4557 SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->upper--));
4560 /* done when there are no more elements left */
4561 SRF_RETURN_DONE(funcctx);
4565 * generate_subscripts_nodir
4566 * Implements the 2-argument version of generate_subscripts
4569 generate_subscripts_nodir(PG_FUNCTION_ARGS)
4571 /* just call the other one -- it can handle both cases */
4572 return generate_subscripts(fcinfo);
4576 * array_fill_with_lower_bounds
4577 * Create and fill array with defined lower bounds.
4580 array_fill_with_lower_bounds(PG_FUNCTION_ARGS)
4589 if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
4591 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
4592 errmsg("dimension array or low bound array cannot be NULL")));
4594 dims = PG_GETARG_ARRAYTYPE_P(1);
4595 lbs = PG_GETARG_ARRAYTYPE_P(2);
4597 if (!PG_ARGISNULL(0))
4599 value = PG_GETARG_DATUM(0);
4608 elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
4609 if (!OidIsValid(elmtype))
4610 elog(ERROR, "could not determine data type of input");
4612 result = array_fill_internal(dims, lbs, value, isnull, elmtype, fcinfo);
4613 PG_RETURN_ARRAYTYPE_P(result);
4618 * Create and fill array with default lower bounds.
4621 array_fill(PG_FUNCTION_ARGS)
4629 if (PG_ARGISNULL(1))
4631 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
4632 errmsg("dimension array or low bound array cannot be NULL")));
4634 dims = PG_GETARG_ARRAYTYPE_P(1);
4636 if (!PG_ARGISNULL(0))
4638 value = PG_GETARG_DATUM(0);
4647 elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
4648 if (!OidIsValid(elmtype))
4649 elog(ERROR, "could not determine data type of input");
4651 result = array_fill_internal(dims, NULL, value, isnull, elmtype, fcinfo);
4652 PG_RETURN_ARRAYTYPE_P(result);
4656 create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
4657 Oid elmtype, int dataoffset)
4661 result = (ArrayType *) palloc0(nbytes);
4662 SET_VARSIZE(result, nbytes);
4663 result->ndim = ndims;
4664 result->dataoffset = dataoffset;
4665 result->elemtype = elmtype;
4666 memcpy(ARR_DIMS(result), dimv, ndims * sizeof(int));
4667 memcpy(ARR_LBOUND(result), lbsv, ndims * sizeof(int));
4673 array_fill_internal(ArrayType *dims, ArrayType *lbs,
4674 Datum value, bool isnull, Oid elmtype,
4675 FunctionCallInfo fcinfo)
4686 ArrayMetaState *my_extra;
4691 if (ARR_NDIM(dims) != 1)
4693 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4694 errmsg("wrong number of array subscripts"),
4695 errdetail("Dimension array must be one dimensional.")));
4697 if (ARR_LBOUND(dims)[0] != 1)
4699 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4700 errmsg("wrong range of array subscripts"),
4701 errdetail("Lower bound of dimension array must be one.")));
4703 if (ARR_HASNULL(dims))
4705 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
4706 errmsg("dimension values cannot be null")));
4708 dimv = (int *) ARR_DATA_PTR(dims);
4709 ndims = ARR_DIMS(dims)[0];
4711 if (ndims < 0) /* we do allow zero-dimension arrays */
4713 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4714 errmsg("invalid number of dimensions: %d", ndims)));
4717 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
4718 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
4723 if (ARR_NDIM(lbs) != 1)
4725 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4726 errmsg("wrong number of array subscripts"),
4727 errdetail("Dimension array must be one dimensional.")));
4729 if (ARR_LBOUND(lbs)[0] != 1)
4731 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4732 errmsg("wrong range of array subscripts"),
4733 errdetail("Lower bound of dimension array must be one.")));
4735 if (ARR_HASNULL(lbs))
4737 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
4738 errmsg("dimension values cannot be null")));
4740 if (ARR_DIMS(lbs)[0] != ndims)
4742 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4743 errmsg("wrong number of array subscripts"),
4744 errdetail("Low bound array has different size than dimensions array.")));
4746 lbsv = (int *) ARR_DATA_PTR(lbs);
4752 for (i = 0; i < MAXDIM; i++)
4758 /* fast track for empty array */
4760 return construct_empty_array(elmtype);
4762 nitems = ArrayGetNItems(ndims, dimv);
4765 * We arrange to look up info about element type only once per series of
4766 * calls, assuming the element type doesn't change underneath us.
4768 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
4769 if (my_extra == NULL)
4771 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
4772 sizeof(ArrayMetaState));
4773 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
4774 my_extra->element_type = InvalidOid;
4777 if (my_extra->element_type != elmtype)
4779 /* Get info about element type */
4780 get_typlenbyvalalign(elmtype,
4782 &my_extra->typbyval,
4783 &my_extra->typalign);
4784 my_extra->element_type = elmtype;
4787 elmlen = my_extra->typlen;
4788 elmbyval = my_extra->typbyval;
4789 elmalign = my_extra->typalign;
4791 /* compute required space */
4799 /* make sure data is not toasted */
4801 value = PointerGetDatum(PG_DETOAST_DATUM(value));
4803 nbytes = att_addlength_datum(0, elmlen, value);
4804 nbytes = att_align_nominal(nbytes, elmalign);
4807 totbytes = nbytes * nitems;
4809 /* check for overflow of multiplication or total request */
4810 if (totbytes / nbytes != nitems ||
4811 !AllocSizeIsValid(totbytes))
4813 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
4814 errmsg("array size exceeds the maximum allowed (%d)",
4815 (int) MaxAllocSize)));
4818 * This addition can't overflow, but it might cause us to go past
4819 * MaxAllocSize. We leave it to palloc to complain in that case.
4821 totbytes += ARR_OVERHEAD_NONULLS(ndims);
4823 result = create_array_envelope(ndims, dimv, lbsv, totbytes,
4826 p = ARR_DATA_PTR(result);
4827 for (i = 0; i < nitems; i++)
4828 p += ArrayCastAndSet(value, elmlen, elmbyval, elmalign, p);
4835 dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
4836 nbytes = dataoffset;
4838 result = create_array_envelope(ndims, dimv, lbsv, nbytes,
4839 elmtype, dataoffset);
4841 /* create_array_envelope already zeroed the bitmap, so we're done */
4852 array_unnest(PG_FUNCTION_ARGS)
4859 char *elemdataptr; /* this moves with nextelem */
4860 bits8 *arraynullsptr; /* this does not */
4864 } array_unnest_fctx;
4866 FuncCallContext *funcctx;
4867 array_unnest_fctx *fctx;
4868 MemoryContext oldcontext;
4870 /* stuff done only on the first call of the function */
4871 if (SRF_IS_FIRSTCALL())
4875 /* create a function context for cross-call persistence */
4876 funcctx = SRF_FIRSTCALL_INIT();
4879 * switch to memory context appropriate for multiple function calls
4881 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
4884 * Get the array value and detoast if needed. We can't do this
4885 * earlier because if we have to detoast, we want the detoasted copy
4886 * to be in multi_call_memory_ctx, so it will go away when we're done
4887 * and not before. (If no detoast happens, we assume the originally
4888 * passed array will stick around till then.)
4890 arr = PG_GETARG_ARRAYTYPE_P(0);
4892 /* allocate memory for user context */
4893 fctx = (array_unnest_fctx *) palloc(sizeof(array_unnest_fctx));
4895 /* initialize state */
4898 fctx->numelems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
4900 fctx->elemdataptr = ARR_DATA_PTR(arr);
4901 fctx->arraynullsptr = ARR_NULLBITMAP(arr);
4903 get_typlenbyvalalign(ARR_ELEMTYPE(arr),
4908 funcctx->user_fctx = fctx;
4909 MemoryContextSwitchTo(oldcontext);
4912 /* stuff done on every call of the function */
4913 funcctx = SRF_PERCALL_SETUP();
4914 fctx = funcctx->user_fctx;
4916 if (fctx->nextelem < fctx->numelems)
4918 int offset = fctx->nextelem++;
4922 * Check for NULL array element
4924 if (array_get_isnull(fctx->arraynullsptr, offset))
4926 fcinfo->isnull = true;
4928 /* elemdataptr does not move */
4933 * OK, get the element
4935 char *ptr = fctx->elemdataptr;
4937 fcinfo->isnull = false;
4938 elem = ArrayCast(ptr, fctx->elmbyval, fctx->elmlen);
4941 * Advance elemdataptr over it
4943 ptr = att_addlength_pointer(ptr, fctx->elmlen, ptr);
4944 ptr = (char *) att_align_nominal(ptr, fctx->elmalign);
4945 fctx->elemdataptr = ptr;
4948 SRF_RETURN_NEXT(funcctx, elem);
4952 /* do when there is no more left */
4953 SRF_RETURN_DONE(funcctx);