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 /* Working state for array_iterate() */
54 typedef struct ArrayIteratorData
56 /* basic info about the array, set up during array_create_iterator() */
57 ArrayType *arr; /* array we're iterating through */
58 bits8 *nullbitmap; /* its null bitmap, if any */
59 int nitems; /* total number of elements in array */
60 int16 typlen; /* element type's length */
61 bool typbyval; /* element type's byval property */
62 char typalign; /* element type's align property */
64 /* information about the requested slice size */
65 int slice_ndim; /* slice dimension, or 0 if not slicing */
66 int slice_len; /* number of elements per slice */
67 int *slice_dims; /* slice dims array */
68 int *slice_lbound; /* slice lbound array */
69 Datum *slice_values; /* workspace of length slice_len */
70 bool *slice_nulls; /* workspace of length slice_len */
72 /* current position information, updated on each iteration */
73 char *data_ptr; /* our current position in the array */
74 int current_item; /* the item # we're at in the array */
77 static bool array_isspace(char ch);
78 static int ArrayCount(const char *str, int *dim, char typdelim);
79 static void ReadArrayStr(char *arrayStr, const char *origStr,
80 int nitems, int ndim, int *dim,
81 FmgrInfo *inputproc, Oid typioparam, int32 typmod,
83 int typlen, bool typbyval, char typalign,
84 Datum *values, bool *nulls,
85 bool *hasnulls, int32 *nbytes);
86 static void ReadArrayBinary(StringInfo buf, int nitems,
87 FmgrInfo *receiveproc, Oid typioparam, int32 typmod,
88 int typlen, bool typbyval, char typalign,
89 Datum *values, bool *nulls,
90 bool *hasnulls, int32 *nbytes);
91 static void CopyArrayEls(ArrayType *array,
92 Datum *values, bool *nulls, int nitems,
93 int typlen, bool typbyval, char typalign,
95 static bool array_get_isnull(const bits8 *nullbitmap, int offset);
96 static void array_set_isnull(bits8 *nullbitmap, int offset, bool isNull);
97 static Datum ArrayCast(char *value, bool byval, int len);
98 static int ArrayCastAndSet(Datum src,
99 int typlen, bool typbyval, char typalign,
101 static char *array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
102 int typlen, bool typbyval, char typalign);
103 static int array_nelems_size(char *ptr, int offset, bits8 *nullbitmap,
104 int nitems, int typlen, bool typbyval, char typalign);
105 static int array_copy(char *destptr, int nitems,
106 char *srcptr, int offset, bits8 *nullbitmap,
107 int typlen, bool typbyval, char typalign);
108 static int array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
109 int ndim, int *dim, int *lb,
111 int typlen, bool typbyval, char typalign);
112 static void array_extract_slice(ArrayType *newarray,
113 int ndim, int *dim, int *lb,
114 char *arraydataptr, bits8 *arraynullsptr,
116 int typlen, bool typbyval, char typalign);
117 static void array_insert_slice(ArrayType *destArray, ArrayType *origArray,
119 int ndim, int *dim, int *lb,
121 int typlen, bool typbyval, char typalign);
122 static int array_cmp(FunctionCallInfo fcinfo);
123 static ArrayType *create_array_envelope(int ndims, int *dimv, int *lbv, int nbytes,
124 Oid elmtype, int dataoffset);
125 static ArrayType *array_fill_internal(ArrayType *dims, ArrayType *lbs,
126 Datum value, bool isnull, Oid elmtype,
127 FunctionCallInfo fcinfo);
132 * converts an array from the external format in "string" to
133 * its internal format.
136 * the internal representation of the input array
139 array_in(PG_FUNCTION_ARGS)
141 char *string = PG_GETARG_CSTRING(0); /* external form */
142 Oid element_type = PG_GETARG_OID(1); /* type of an array
144 int32 typmod = PG_GETARG_INT32(2); /* typmod for array elements */
163 ArrayMetaState *my_extra;
166 * We arrange to look up info about element type, including its input
167 * conversion proc, only once per series of calls, assuming the element
168 * type doesn't change underneath us.
170 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
171 if (my_extra == NULL)
173 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
174 sizeof(ArrayMetaState));
175 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
176 my_extra->element_type = ~element_type;
179 if (my_extra->element_type != element_type)
182 * Get info about element type, including its input conversion proc
184 get_type_io_data(element_type, IOFunc_input,
185 &my_extra->typlen, &my_extra->typbyval,
186 &my_extra->typalign, &my_extra->typdelim,
187 &my_extra->typioparam, &my_extra->typiofunc);
188 fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
189 fcinfo->flinfo->fn_mcxt);
190 my_extra->element_type = element_type;
192 typlen = my_extra->typlen;
193 typbyval = my_extra->typbyval;
194 typalign = my_extra->typalign;
195 typdelim = my_extra->typdelim;
196 typioparam = my_extra->typioparam;
198 /* Make a modifiable copy of the input */
199 string_save = pstrdup(string);
202 * If the input string starts with dimension info, read and use that.
203 * Otherwise, we require the input to be in curly-brace style, and we
204 * prescan the input to determine dimensions.
206 * Dimension info takes the form of one or more [n] or [m:n] items. The
207 * outer loop iterates once per dimension item.
217 * Note: we currently allow whitespace between, but not within,
220 while (array_isspace(*p))
223 break; /* no more dimension items */
227 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
228 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
231 for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++);
232 if (q == p) /* no digits? */
234 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
235 errmsg("missing dimension value")));
241 lBound[ndim] = atoi(p);
243 for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++);
244 if (q == p) /* no digits? */
246 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
247 errmsg("missing dimension value")));
256 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
257 errmsg("missing \"]\" in array dimensions")));
262 if (ub < lBound[ndim])
264 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
265 errmsg("upper bound cannot be less than lower bound")));
267 dim[ndim] = ub - lBound[ndim] + 1;
273 /* No array dimensions, so intuit dimensions from brace structure */
276 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
277 errmsg("array value must start with \"{\" or dimension information")));
278 ndim = ArrayCount(p, dim, typdelim);
279 for (i = 0; i < ndim; i++)
287 /* If array dimensions are given, expect '=' operator */
288 if (strncmp(p, ASSGN, strlen(ASSGN)) != 0)
290 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
291 errmsg("missing assignment operator")));
293 while (array_isspace(*p))
297 * intuit dimensions from brace structure -- it better match what we
302 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
303 errmsg("array value must start with \"{\" or dimension information")));
304 ndim_braces = ArrayCount(p, dim_braces, typdelim);
305 if (ndim_braces != ndim)
307 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
308 errmsg("array dimensions incompatible with array literal")));
309 for (i = 0; i < ndim; ++i)
311 if (dim[i] != dim_braces[i])
313 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
314 errmsg("array dimensions incompatible with array literal")));
319 printf("array_in- ndim %d (", ndim);
320 for (i = 0; i < ndim; i++)
322 printf(" %d", dim[i]);
324 printf(") for %s\n", string);
327 /* This checks for overflow of the array dimensions */
328 nitems = ArrayGetNItems(ndim, dim);
331 PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
333 dataPtr = (Datum *) palloc(nitems * sizeof(Datum));
334 nullsPtr = (bool *) palloc(nitems * sizeof(bool));
335 ReadArrayStr(p, string,
337 &my_extra->proc, typioparam, typmod,
339 typlen, typbyval, typalign,
344 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
345 nbytes += dataoffset;
349 dataoffset = 0; /* marker for no null bitmap */
350 nbytes += ARR_OVERHEAD_NONULLS(ndim);
352 retval = (ArrayType *) palloc0(nbytes);
353 SET_VARSIZE(retval, nbytes);
355 retval->dataoffset = dataoffset;
358 * This comes from the array's pg_type.typelem (which points to the base
359 * data type's pg_type.oid) and stores system oids in user tables. This
360 * oid must be preserved by binary upgrades.
362 retval->elemtype = element_type;
363 memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
364 memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
367 dataPtr, nullsPtr, nitems,
368 typlen, typbyval, typalign,
375 PG_RETURN_ARRAYTYPE_P(retval);
379 * array_isspace() --- a non-locale-dependent isspace()
381 * We used to use isspace() for parsing array values, but that has
382 * undesirable results: an array value might be silently interpreted
383 * differently depending on the locale setting. Now we just hard-wire
384 * the traditional ASCII definition of isspace().
387 array_isspace(char ch)
401 * Determines the dimensions for an array string.
403 * Returns number of dimensions as function result. The axis lengths are
404 * returned in dim[], which must be of size MAXDIM.
407 ArrayCount(const char *str, int *dim, char typdelim)
415 bool in_quotes = false;
416 bool eoArray = false;
417 bool empty_array = true;
419 ArrayParseState parse_state = ARRAY_NO_LEVEL;
421 for (i = 0; i < MAXDIM; ++i)
423 temp[i] = dim[i] = 0;
424 nelems_last[i] = nelems[i] = 1;
430 bool itemdone = false;
434 if (parse_state == ARRAY_ELEM_STARTED ||
435 parse_state == ARRAY_QUOTED_ELEM_STARTED)
441 /* Signal a premature end of the string */
443 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
444 errmsg("malformed array literal: \"%s\"", str)));
449 * An escape must be after a level start, after an element
450 * start, or after an element delimiter. In any case we
451 * now must be past an element start.
453 if (parse_state != ARRAY_LEVEL_STARTED &&
454 parse_state != ARRAY_ELEM_STARTED &&
455 parse_state != ARRAY_QUOTED_ELEM_STARTED &&
456 parse_state != ARRAY_ELEM_DELIMITED)
458 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
459 errmsg("malformed array literal: \"%s\"", str)));
460 if (parse_state != ARRAY_QUOTED_ELEM_STARTED)
461 parse_state = ARRAY_ELEM_STARTED;
462 /* skip the escaped character */
467 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
468 errmsg("malformed array literal: \"%s\"", str)));
473 * A quote must be after a level start, after a quoted
474 * element start, or after an element delimiter. In any
475 * case we now must be past an element start.
477 if (parse_state != ARRAY_LEVEL_STARTED &&
478 parse_state != ARRAY_QUOTED_ELEM_STARTED &&
479 parse_state != ARRAY_ELEM_DELIMITED)
481 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
482 errmsg("malformed array literal: \"%s\"", str)));
483 in_quotes = !in_quotes;
485 parse_state = ARRAY_QUOTED_ELEM_STARTED;
487 parse_state = ARRAY_QUOTED_ELEM_COMPLETED;
493 * A left brace can occur if no nesting has occurred
494 * yet, after a level start, or after a level
497 if (parse_state != ARRAY_NO_LEVEL &&
498 parse_state != ARRAY_LEVEL_STARTED &&
499 parse_state != ARRAY_LEVEL_DELIMITED)
501 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
502 errmsg("malformed array literal: \"%s\"", str)));
503 parse_state = ARRAY_LEVEL_STARTED;
504 if (nest_level >= MAXDIM)
506 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
507 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
508 nest_level + 1, MAXDIM)));
509 temp[nest_level] = 0;
511 if (ndim < nest_level)
519 * A right brace can occur after an element start, an
520 * element completion, a quoted element completion, or
521 * a level completion.
523 if (parse_state != ARRAY_ELEM_STARTED &&
524 parse_state != ARRAY_ELEM_COMPLETED &&
525 parse_state != ARRAY_QUOTED_ELEM_COMPLETED &&
526 parse_state != ARRAY_LEVEL_COMPLETED &&
527 !(nest_level == 1 && parse_state == ARRAY_LEVEL_STARTED))
529 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
530 errmsg("malformed array literal: \"%s\"", str)));
531 parse_state = ARRAY_LEVEL_COMPLETED;
534 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
535 errmsg("malformed array literal: \"%s\"", str)));
538 if ((nelems_last[nest_level] != 1) &&
539 (nelems[nest_level] != nelems_last[nest_level]))
541 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
542 errmsg("multidimensional arrays must have "
543 "array expressions with matching "
545 nelems_last[nest_level] = nelems[nest_level];
546 nelems[nest_level] = 1;
548 eoArray = itemdone = true;
552 * We don't set itemdone here; see comments in
555 temp[nest_level - 1]++;
562 if (*ptr == typdelim)
565 * Delimiters can occur after an element start, an
566 * element completion, a quoted element
567 * completion, or a level completion.
569 if (parse_state != ARRAY_ELEM_STARTED &&
570 parse_state != ARRAY_ELEM_COMPLETED &&
571 parse_state != ARRAY_QUOTED_ELEM_COMPLETED &&
572 parse_state != ARRAY_LEVEL_COMPLETED)
574 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
575 errmsg("malformed array literal: \"%s\"", str)));
576 if (parse_state == ARRAY_LEVEL_COMPLETED)
577 parse_state = ARRAY_LEVEL_DELIMITED;
579 parse_state = ARRAY_ELEM_DELIMITED;
581 nelems[nest_level - 1]++;
583 else if (!array_isspace(*ptr))
586 * Other non-space characters must be after a
587 * level start, after an element start, or after
588 * an element delimiter. In any case we now must
589 * be past an element start.
591 if (parse_state != ARRAY_LEVEL_STARTED &&
592 parse_state != ARRAY_ELEM_STARTED &&
593 parse_state != ARRAY_ELEM_DELIMITED)
595 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
596 errmsg("malformed array literal: \"%s\"", str)));
597 parse_state = ARRAY_ELEM_STARTED;
609 /* only whitespace is allowed after the closing brace */
612 if (!array_isspace(*ptr++))
614 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
615 errmsg("malformed array literal: \"%s\"", str)));
618 /* special case for an empty array */
622 for (i = 0; i < ndim; ++i)
630 * parses the array string pointed to by "arrayStr" and converts the values
631 * to internal format. Unspecified elements are initialized to nulls.
632 * The array dimensions must already have been determined.
635 * arrayStr: the string to parse.
636 * CAUTION: the contents of "arrayStr" will be modified!
637 * origStr: the unmodified input string, used only in error messages.
638 * nitems: total number of array elements, as already determined.
639 * ndim: number of array dimensions
640 * dim[]: array axis lengths
641 * inputproc: type-specific input procedure for element datatype.
642 * typioparam, typmod: auxiliary values to pass to inputproc.
643 * typdelim: the value delimiter (type-specific).
644 * typlen, typbyval, typalign: storage parameters of element datatype.
647 * values[]: filled with converted data values.
648 * nulls[]: filled with is-null markers.
649 * *hasnulls: set TRUE iff there are any null elements.
650 * *nbytes: set to total size of data area needed (including alignment
651 * padding but not including array header overhead).
653 * Note that values[] and nulls[] are allocated by the caller, and must have
657 ReadArrayStr(char *arrayStr,
677 bool in_quotes = false;
678 bool eoArray = false;
684 mda_get_prod(ndim, dim, prod);
685 MemSet(indx, 0, sizeof(indx));
687 /* Initialize is-null markers to true */
688 memset(nulls, true, nitems * sizeof(bool));
691 * We have to remove " and \ characters to create a clean item value to
692 * pass to the datatype input routine. We overwrite each item value
693 * in-place within arrayStr to do this. srcptr is the current scan point,
694 * and dstptr is where we are copying to.
696 * We also want to suppress leading and trailing unquoted whitespace. We
697 * use the leadingspace flag to suppress leading space. Trailing space is
698 * tracked by using dstendptr to point to the last significant output
701 * The error checking in this routine is mostly pro-forma, since we expect
702 * that ArrayCount() already validated the string.
707 bool itemdone = false;
708 bool leadingspace = true;
709 bool hasquoting = false;
715 itemstart = dstptr = dstendptr = srcptr;
722 /* Signal a premature end of the string */
724 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
725 errmsg("malformed array literal: \"%s\"",
729 /* Skip backslash, copy next character as-is. */
733 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
734 errmsg("malformed array literal: \"%s\"",
736 *dstptr++ = *srcptr++;
737 /* Treat the escaped character as non-whitespace */
738 leadingspace = false;
740 hasquoting = true; /* can't be a NULL marker */
743 in_quotes = !in_quotes;
745 leadingspace = false;
749 * Advance dstendptr when we exit in_quotes; this
750 * saves having to do it in all the other in_quotes
755 hasquoting = true; /* can't be a NULL marker */
761 if (nest_level >= ndim)
763 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
764 errmsg("malformed array literal: \"%s\"",
767 indx[nest_level - 1] = 0;
771 *dstptr++ = *srcptr++;
778 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
779 errmsg("malformed array literal: \"%s\"",
782 i = ArrayGetOffset0(ndim, indx, prod);
783 indx[nest_level - 1] = 0;
786 eoArray = itemdone = true;
788 indx[nest_level - 1]++;
792 *dstptr++ = *srcptr++;
796 *dstptr++ = *srcptr++;
797 else if (*srcptr == typdelim)
800 i = ArrayGetOffset0(ndim, indx, prod);
805 else if (array_isspace(*srcptr))
808 * If leading space, drop it immediately. Else, copy
809 * but don't advance dstendptr.
814 *dstptr++ = *srcptr++;
818 *dstptr++ = *srcptr++;
819 leadingspace = false;
826 Assert(dstptr < srcptr);
829 if (i < 0 || i >= nitems)
831 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
832 errmsg("malformed array literal: \"%s\"",
835 if (Array_nulls && !hasquoting &&
836 pg_strcasecmp(itemstart, "NULL") == 0)
838 /* it's a NULL item */
839 values[i] = InputFunctionCall(inputproc, NULL,
845 values[i] = InputFunctionCall(inputproc, itemstart,
852 * Check for nulls, compute total data space needed
856 for (i = 0; i < nitems; i++)
862 /* let's just make sure data is not toasted */
864 values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
865 totbytes = att_addlength_datum(totbytes, typlen, values[i]);
866 totbytes = att_align_nominal(totbytes, typalign);
867 /* check for overflow of total request */
868 if (!AllocSizeIsValid(totbytes))
870 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
871 errmsg("array size exceeds the maximum allowed (%d)",
872 (int) MaxAllocSize)));
881 * Copy data into an array object from a temporary array of Datums.
883 * array: array object (with header fields already filled in)
884 * values: array of Datums to be copied
885 * nulls: array of is-null flags (can be NULL if no nulls)
886 * nitems: number of Datums to be copied
887 * typbyval, typlen, typalign: info about element datatype
888 * freedata: if TRUE and element type is pass-by-ref, pfree data values
889 * referenced by Datums after copying them.
891 * If the input data is of varlena type, the caller must have ensured that
892 * the values are not toasted. (Doing it here doesn't work since the
893 * caller has already allocated space for the array...)
896 CopyArrayEls(ArrayType *array,
905 char *p = ARR_DATA_PTR(array);
906 bits8 *bitmap = ARR_NULLBITMAP(array);
914 for (i = 0; i < nitems; i++)
916 if (nulls && nulls[i])
918 if (!bitmap) /* shouldn't happen */
919 elog(ERROR, "null array element where not supported");
920 /* bitmap bit stays 0 */
925 p += ArrayCastAndSet(values[i], typlen, typbyval, typalign, p);
927 pfree(DatumGetPointer(values[i]));
932 if (bitmask == 0x100)
941 if (bitmap && bitmask != 1)
947 * takes the internal representation of an array and returns a string
948 * containing the array in its external format.
951 array_out(PG_FUNCTION_ARGS)
953 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
954 Oid element_type = ARR_ELEMTYPE(v);
963 dims_str[(MAXDIM * 33) + 2];
966 * 33 per dim since we assume 15 digits per number + ':' +'[]'
968 * +2 allows for assignment operator + trailing null
983 ArrayMetaState *my_extra;
986 * We arrange to look up info about element type, including its output
987 * conversion proc, only once per series of calls, assuming the element
988 * type doesn't change underneath us.
990 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
991 if (my_extra == NULL)
993 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
994 sizeof(ArrayMetaState));
995 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
996 my_extra->element_type = ~element_type;
999 if (my_extra->element_type != element_type)
1002 * Get info about element type, including its output conversion proc
1004 get_type_io_data(element_type, IOFunc_output,
1005 &my_extra->typlen, &my_extra->typbyval,
1006 &my_extra->typalign, &my_extra->typdelim,
1007 &my_extra->typioparam, &my_extra->typiofunc);
1008 fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1009 fcinfo->flinfo->fn_mcxt);
1010 my_extra->element_type = element_type;
1012 typlen = my_extra->typlen;
1013 typbyval = my_extra->typbyval;
1014 typalign = my_extra->typalign;
1015 typdelim = my_extra->typdelim;
1020 nitems = ArrayGetNItems(ndim, dims);
1024 retval = pstrdup("{}");
1025 PG_RETURN_CSTRING(retval);
1029 * we will need to add explicit dimensions if any dimension has a lower
1030 * bound other than one
1032 for (i = 0; i < ndim; i++)
1042 * Convert all values to string form, count total space needed (including
1043 * any overhead such as escaping backslashes), and detect whether each
1044 * item needs double quotes.
1046 values = (char **) palloc(nitems * sizeof(char *));
1047 needquotes = (bool *) palloc(nitems * sizeof(bool));
1048 overall_length = 1; /* don't forget to count \0 at end. */
1050 p = ARR_DATA_PTR(v);
1051 bitmap = ARR_NULLBITMAP(v);
1054 for (i = 0; i < nitems; i++)
1058 /* Get source element, checking for NULL */
1059 if (bitmap && (*bitmap & bitmask) == 0)
1061 values[i] = pstrdup("NULL");
1062 overall_length += 4;
1069 itemvalue = fetch_att(p, typbyval, typlen);
1070 values[i] = OutputFunctionCall(&my_extra->proc, itemvalue);
1071 p = att_addlength_pointer(p, typlen, p);
1072 p = (char *) att_align_nominal(p, typalign);
1074 /* count data plus backslashes; detect chars needing quotes */
1075 if (values[i][0] == '\0')
1076 needquote = true; /* force quotes for empty string */
1077 else if (pg_strcasecmp(values[i], "NULL") == 0)
1078 needquote = true; /* force quotes for literal NULL */
1082 for (tmp = values[i]; *tmp != '\0'; tmp++)
1086 overall_length += 1;
1087 if (ch == '"' || ch == '\\')
1090 overall_length += 1;
1092 else if (ch == '{' || ch == '}' || ch == typdelim ||
1098 needquotes[i] = needquote;
1100 /* Count the pair of double quotes, if needed */
1102 overall_length += 2;
1104 overall_length += 1;
1106 /* advance bitmap pointer if any */
1110 if (bitmask == 0x100)
1119 * count total number of curly braces in output string
1121 for (i = j = 0, k = 1; i < ndim; i++)
1122 k *= dims[i], j += k;
1126 /* add explicit dimensions if required */
1129 char *ptr = dims_str;
1131 for (i = 0; i < ndim; i++)
1133 sprintf(ptr, "[%d:%d]", lb[i], lb[i] + dims[i] - 1);
1140 retval = (char *) palloc(strlen(dims_str) + overall_length + 2 * j);
1143 #define APPENDSTR(str) (strcpy(p, (str)), p += strlen(p))
1144 #define APPENDCHAR(ch) (*p++ = (ch), *p = '\0')
1147 APPENDSTR(dims_str);
1149 for (i = 0; i < ndim; i++)
1155 for (i = j; i < ndim - 1; i++)
1161 for (tmp = values[k]; *tmp; tmp++)
1165 if (ch == '"' || ch == '\\')
1173 APPENDSTR(values[k]);
1176 for (i = ndim - 1; i >= 0; i--)
1178 indx[i] = (indx[i] + 1) % dims[i];
1181 APPENDCHAR(typdelim);
1196 PG_RETURN_CSTRING(retval);
1201 * converts an array from the external binary format to
1202 * its internal format.
1205 * the internal representation of the input array
1208 array_recv(PG_FUNCTION_ARGS)
1210 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
1211 Oid spec_element_type = PG_GETARG_OID(1); /* type of an array
1213 int32 typmod = PG_GETARG_INT32(2); /* typmod for array elements */
1231 ArrayMetaState *my_extra;
1233 /* Get the array header information */
1234 ndim = pq_getmsgint(buf, 4);
1235 if (ndim < 0) /* we do allow zero-dimension arrays */
1237 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1238 errmsg("invalid number of dimensions: %d", ndim)));
1241 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1242 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
1245 flags = pq_getmsgint(buf, 4);
1246 if (flags != 0 && flags != 1)
1248 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1249 errmsg("invalid array flags")));
1251 element_type = pq_getmsgint(buf, sizeof(Oid));
1252 if (element_type != spec_element_type)
1254 /* XXX Can we allow taking the input element type in any cases? */
1256 (errcode(ERRCODE_DATATYPE_MISMATCH),
1257 errmsg("wrong element type")));
1260 for (i = 0; i < ndim; i++)
1262 dim[i] = pq_getmsgint(buf, 4);
1263 lBound[i] = pq_getmsgint(buf, 4);
1266 * Check overflow of upper bound. (ArrayNItems() below checks that
1271 int ub = lBound[i] + dim[i] - 1;
1275 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
1276 errmsg("integer out of range")));
1280 /* This checks for overflow of array dimensions */
1281 nitems = ArrayGetNItems(ndim, dim);
1284 * We arrange to look up info about element type, including its receive
1285 * conversion proc, only once per series of calls, assuming the element
1286 * type doesn't change underneath us.
1288 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1289 if (my_extra == NULL)
1291 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1292 sizeof(ArrayMetaState));
1293 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1294 my_extra->element_type = ~element_type;
1297 if (my_extra->element_type != element_type)
1299 /* Get info about element type, including its receive proc */
1300 get_type_io_data(element_type, IOFunc_receive,
1301 &my_extra->typlen, &my_extra->typbyval,
1302 &my_extra->typalign, &my_extra->typdelim,
1303 &my_extra->typioparam, &my_extra->typiofunc);
1304 if (!OidIsValid(my_extra->typiofunc))
1306 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1307 errmsg("no binary input function available for type %s",
1308 format_type_be(element_type))));
1309 fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1310 fcinfo->flinfo->fn_mcxt);
1311 my_extra->element_type = element_type;
1316 /* Return empty array ... but not till we've validated element_type */
1317 PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
1320 typlen = my_extra->typlen;
1321 typbyval = my_extra->typbyval;
1322 typalign = my_extra->typalign;
1323 typioparam = my_extra->typioparam;
1325 dataPtr = (Datum *) palloc(nitems * sizeof(Datum));
1326 nullsPtr = (bool *) palloc(nitems * sizeof(bool));
1327 ReadArrayBinary(buf, nitems,
1328 &my_extra->proc, typioparam, typmod,
1329 typlen, typbyval, typalign,
1331 &hasnulls, &nbytes);
1334 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
1335 nbytes += dataoffset;
1339 dataoffset = 0; /* marker for no null bitmap */
1340 nbytes += ARR_OVERHEAD_NONULLS(ndim);
1342 retval = (ArrayType *) palloc0(nbytes);
1343 SET_VARSIZE(retval, nbytes);
1344 retval->ndim = ndim;
1345 retval->dataoffset = dataoffset;
1346 retval->elemtype = element_type;
1347 memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
1348 memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
1350 CopyArrayEls(retval,
1351 dataPtr, nullsPtr, nitems,
1352 typlen, typbyval, typalign,
1358 PG_RETURN_ARRAYTYPE_P(retval);
1363 * collect the data elements of an array being read in binary style.
1366 * buf: the data buffer to read from.
1367 * nitems: total number of array elements (already read).
1368 * receiveproc: type-specific receive procedure for element datatype.
1369 * typioparam, typmod: auxiliary values to pass to receiveproc.
1370 * typlen, typbyval, typalign: storage parameters of element datatype.
1373 * values[]: filled with converted data values.
1374 * nulls[]: filled with is-null markers.
1375 * *hasnulls: set TRUE iff there are any null elements.
1376 * *nbytes: set to total size of data area needed (including alignment
1377 * padding but not including array header overhead).
1379 * Note that values[] and nulls[] are allocated by the caller, and must have
1383 ReadArrayBinary(StringInfo buf,
1385 FmgrInfo *receiveproc,
1400 for (i = 0; i < nitems; i++)
1403 StringInfoData elem_buf;
1406 /* Get and check the item length */
1407 itemlen = pq_getmsgint(buf, 4);
1408 if (itemlen < -1 || itemlen > (buf->len - buf->cursor))
1410 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1411 errmsg("insufficient data left in message")));
1415 /* -1 length means NULL */
1416 values[i] = ReceiveFunctionCall(receiveproc, NULL,
1417 typioparam, typmod);
1423 * Rather than copying data around, we just set up a phony StringInfo
1424 * pointing to the correct portion of the input buffer. We assume we
1425 * can scribble on the input buffer so as to maintain the convention
1426 * that StringInfos have a trailing null.
1428 elem_buf.data = &buf->data[buf->cursor];
1429 elem_buf.maxlen = itemlen + 1;
1430 elem_buf.len = itemlen;
1431 elem_buf.cursor = 0;
1433 buf->cursor += itemlen;
1435 csave = buf->data[buf->cursor];
1436 buf->data[buf->cursor] = '\0';
1438 /* Now call the element's receiveproc */
1439 values[i] = ReceiveFunctionCall(receiveproc, &elem_buf,
1440 typioparam, typmod);
1443 /* Trouble if it didn't eat the whole buffer */
1444 if (elem_buf.cursor != itemlen)
1446 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1447 errmsg("improper binary format in array element %d",
1450 buf->data[buf->cursor] = csave;
1454 * Check for nulls, compute total data space needed
1458 for (i = 0; i < nitems; i++)
1464 /* let's just make sure data is not toasted */
1466 values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
1467 totbytes = att_addlength_datum(totbytes, typlen, values[i]);
1468 totbytes = att_align_nominal(totbytes, typalign);
1469 /* check for overflow of total request */
1470 if (!AllocSizeIsValid(totbytes))
1472 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1473 errmsg("array size exceeds the maximum allowed (%d)",
1474 (int) MaxAllocSize)));
1477 *hasnulls = hasnull;
1484 * takes the internal representation of an array and returns a bytea
1485 * containing the array in its external binary format.
1488 array_send(PG_FUNCTION_ARGS)
1490 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1491 Oid element_type = ARR_ELEMTYPE(v);
1503 ArrayMetaState *my_extra;
1506 * We arrange to look up info about element type, including its send
1507 * conversion proc, only once per series of calls, assuming the element
1508 * type doesn't change underneath us.
1510 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1511 if (my_extra == NULL)
1513 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1514 sizeof(ArrayMetaState));
1515 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1516 my_extra->element_type = ~element_type;
1519 if (my_extra->element_type != element_type)
1521 /* Get info about element type, including its send proc */
1522 get_type_io_data(element_type, IOFunc_send,
1523 &my_extra->typlen, &my_extra->typbyval,
1524 &my_extra->typalign, &my_extra->typdelim,
1525 &my_extra->typioparam, &my_extra->typiofunc);
1526 if (!OidIsValid(my_extra->typiofunc))
1528 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1529 errmsg("no binary output function available for type %s",
1530 format_type_be(element_type))));
1531 fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1532 fcinfo->flinfo->fn_mcxt);
1533 my_extra->element_type = element_type;
1535 typlen = my_extra->typlen;
1536 typbyval = my_extra->typbyval;
1537 typalign = my_extra->typalign;
1541 nitems = ArrayGetNItems(ndim, dim);
1543 pq_begintypsend(&buf);
1545 /* Send the array header information */
1546 pq_sendint(&buf, ndim, 4);
1547 pq_sendint(&buf, ARR_HASNULL(v) ? 1 : 0, 4);
1548 pq_sendint(&buf, element_type, sizeof(Oid));
1549 for (i = 0; i < ndim; i++)
1551 pq_sendint(&buf, ARR_DIMS(v)[i], 4);
1552 pq_sendint(&buf, ARR_LBOUND(v)[i], 4);
1555 /* Send the array elements using the element's own sendproc */
1556 p = ARR_DATA_PTR(v);
1557 bitmap = ARR_NULLBITMAP(v);
1560 for (i = 0; i < nitems; i++)
1562 /* Get source element, checking for NULL */
1563 if (bitmap && (*bitmap & bitmask) == 0)
1565 /* -1 length means a NULL */
1566 pq_sendint(&buf, -1, 4);
1573 itemvalue = fetch_att(p, typbyval, typlen);
1574 outputbytes = SendFunctionCall(&my_extra->proc, itemvalue);
1575 pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
1576 pq_sendbytes(&buf, VARDATA(outputbytes),
1577 VARSIZE(outputbytes) - VARHDRSZ);
1580 p = att_addlength_pointer(p, typlen, p);
1581 p = (char *) att_align_nominal(p, typalign);
1584 /* advance bitmap pointer if any */
1588 if (bitmask == 0x100)
1596 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
1601 * returns the number of dimensions of the array pointed to by "v"
1604 array_ndims(PG_FUNCTION_ARGS)
1606 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1608 /* Sanity check: does it look like an array at all? */
1609 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1612 PG_RETURN_INT32(ARR_NDIM(v));
1617 * returns the dimensions of the array pointed to by "v", as a "text"
1620 array_dims(PG_FUNCTION_ARGS)
1622 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1629 * 33 since we assume 15 digits per number + ':' +'[]'
1631 * +1 for trailing null
1633 char buf[MAXDIM * 33 + 1];
1635 /* Sanity check: does it look like an array at all? */
1636 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1643 for (i = 0; i < ARR_NDIM(v); i++)
1645 sprintf(p, "[%d:%d]", lb[i], dimv[i] + lb[i] - 1);
1649 PG_RETURN_TEXT_P(cstring_to_text(buf));
1654 * returns the lower dimension, of the DIM requested, for
1655 * the array pointed to by "v", as an int4
1658 array_lower(PG_FUNCTION_ARGS)
1660 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1661 int reqdim = PG_GETARG_INT32(1);
1665 /* Sanity check: does it look like an array at all? */
1666 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1669 /* Sanity check: was the requested dim valid */
1670 if (reqdim <= 0 || reqdim > ARR_NDIM(v))
1674 result = lb[reqdim - 1];
1676 PG_RETURN_INT32(result);
1681 * returns the upper dimension, of the DIM requested, for
1682 * the array pointed to by "v", as an int4
1685 array_upper(PG_FUNCTION_ARGS)
1687 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1688 int reqdim = PG_GETARG_INT32(1);
1693 /* Sanity check: does it look like an array at all? */
1694 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1697 /* Sanity check: was the requested dim valid */
1698 if (reqdim <= 0 || reqdim > ARR_NDIM(v))
1704 result = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
1706 PG_RETURN_INT32(result);
1711 * returns the length, of the dimension requested, for
1712 * the array pointed to by "v", as an int4
1715 array_length(PG_FUNCTION_ARGS)
1717 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1718 int reqdim = PG_GETARG_INT32(1);
1722 /* Sanity check: does it look like an array at all? */
1723 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1726 /* Sanity check: was the requested dim valid */
1727 if (reqdim <= 0 || reqdim > ARR_NDIM(v))
1732 result = dimv[reqdim - 1];
1734 PG_RETURN_INT32(result);
1739 * This routine takes an array pointer and a subscript array and returns
1740 * the referenced item as a Datum. Note that for a pass-by-reference
1741 * datatype, the returned Datum is a pointer into the array object.
1743 * This handles both ordinary varlena arrays and fixed-length arrays.
1746 * array: the array object (mustn't be NULL)
1747 * nSubscripts: number of subscripts supplied
1748 * indx[]: the subscript values
1749 * arraytyplen: pg_type.typlen for the array type
1750 * elmlen: pg_type.typlen for the array's element type
1751 * elmbyval: pg_type.typbyval for the array's element type
1752 * elmalign: pg_type.typalign for the array's element type
1755 * The return value is the element Datum.
1756 * *isNull is set to indicate whether the element is NULL.
1759 array_ref(ArrayType *array,
1777 bits8 *arraynullsptr;
1779 if (arraytyplen > 0)
1782 * fixed-length arrays -- these are assumed to be 1-d, 0-based
1785 fixedDim[0] = arraytyplen / elmlen;
1789 arraydataptr = (char *) array;
1790 arraynullsptr = NULL;
1794 /* detoast input array if necessary */
1795 array = DatumGetArrayTypeP(PointerGetDatum(array));
1797 ndim = ARR_NDIM(array);
1798 dim = ARR_DIMS(array);
1799 lb = ARR_LBOUND(array);
1800 arraydataptr = ARR_DATA_PTR(array);
1801 arraynullsptr = ARR_NULLBITMAP(array);
1805 * Return NULL for invalid subscript
1807 if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
1812 for (i = 0; i < ndim; i++)
1814 if (indx[i] < lb[i] || indx[i] >= (dim[i] + lb[i]))
1822 * Calculate the element number
1824 offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
1827 * Check for NULL array element
1829 if (array_get_isnull(arraynullsptr, offset))
1836 * OK, get the element
1839 retptr = array_seek(arraydataptr, 0, arraynullsptr, offset,
1840 elmlen, elmbyval, elmalign);
1841 return ArrayCast(retptr, elmbyval, elmlen);
1846 * This routine takes an array and a range of indices (upperIndex and
1847 * lowerIndx), creates a new array structure for the referred elements
1848 * and returns a pointer to it.
1850 * This handles both ordinary varlena arrays and fixed-length arrays.
1853 * array: the array object (mustn't be NULL)
1854 * nSubscripts: number of subscripts supplied (must be same for upper/lower)
1855 * upperIndx[]: the upper subscript values
1856 * lowerIndx[]: the lower subscript values
1857 * arraytyplen: pg_type.typlen for the array type
1858 * elmlen: pg_type.typlen for the array's element type
1859 * elmbyval: pg_type.typbyval for the array's element type
1860 * elmalign: pg_type.typalign for the array's element type
1863 * The return value is the new array Datum (it's never NULL)
1865 * NOTE: we assume it is OK to scribble on the provided subscript arrays
1866 * lowerIndx[] and upperIndx[]. These are generally just temporaries.
1869 array_get_slice(ArrayType *array,
1878 ArrayType *newarray;
1888 bits8 *arraynullsptr;
1893 if (arraytyplen > 0)
1896 * fixed-length arrays -- currently, cannot slice these because parser
1897 * labels output as being of the fixed-length array type! Code below
1898 * shows how we could support it if the parser were changed to label
1899 * output as a suitable varlena array type.
1902 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1903 errmsg("slices of fixed-length arrays not implemented")));
1906 * fixed-length arrays -- these are assumed to be 1-d, 0-based
1908 * XXX where would we get the correct ELEMTYPE from?
1911 fixedDim[0] = arraytyplen / elmlen;
1915 elemtype = InvalidOid; /* XXX */
1916 arraydataptr = (char *) array;
1917 arraynullsptr = NULL;
1921 /* detoast input array if necessary */
1922 array = DatumGetArrayTypeP(PointerGetDatum(array));
1924 ndim = ARR_NDIM(array);
1925 dim = ARR_DIMS(array);
1926 lb = ARR_LBOUND(array);
1927 elemtype = ARR_ELEMTYPE(array);
1928 arraydataptr = ARR_DATA_PTR(array);
1929 arraynullsptr = ARR_NULLBITMAP(array);
1933 * Check provided subscripts. A slice exceeding the current array limits
1934 * is silently truncated to the array limits. If we end up with an empty
1935 * slice, return an empty array.
1937 if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
1938 return construct_empty_array(elemtype);
1940 for (i = 0; i < nSubscripts; i++)
1942 if (lowerIndx[i] < lb[i])
1943 lowerIndx[i] = lb[i];
1944 if (upperIndx[i] >= (dim[i] + lb[i]))
1945 upperIndx[i] = dim[i] + lb[i] - 1;
1946 if (lowerIndx[i] > upperIndx[i])
1947 return construct_empty_array(elemtype);
1949 /* fill any missing subscript positions with full array range */
1950 for (; i < ndim; i++)
1952 lowerIndx[i] = lb[i];
1953 upperIndx[i] = dim[i] + lb[i] - 1;
1954 if (lowerIndx[i] > upperIndx[i])
1955 return construct_empty_array(elemtype);
1958 mda_get_range(ndim, span, lowerIndx, upperIndx);
1960 bytes = array_slice_size(arraydataptr, arraynullsptr,
1962 lowerIndx, upperIndx,
1963 elmlen, elmbyval, elmalign);
1966 * Currently, we put a null bitmap in the result if the source has one;
1967 * could be smarter ...
1971 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, ArrayGetNItems(ndim, span));
1972 bytes += dataoffset;
1976 dataoffset = 0; /* marker for no null bitmap */
1977 bytes += ARR_OVERHEAD_NONULLS(ndim);
1980 newarray = (ArrayType *) palloc0(bytes);
1981 SET_VARSIZE(newarray, bytes);
1982 newarray->ndim = ndim;
1983 newarray->dataoffset = dataoffset;
1984 newarray->elemtype = elemtype;
1985 memcpy(ARR_DIMS(newarray), span, ndim * sizeof(int));
1988 * Lower bounds of the new array are set to 1. Formerly (before 7.3) we
1989 * copied the given lowerIndx values ... but that seems confusing.
1991 newlb = ARR_LBOUND(newarray);
1992 for (i = 0; i < ndim; i++)
1995 array_extract_slice(newarray,
1997 arraydataptr, arraynullsptr,
1998 lowerIndx, upperIndx,
1999 elmlen, elmbyval, elmalign);
2006 * This routine sets the value of an array element (specified by
2007 * a subscript array) to a new value specified by "dataValue".
2009 * This handles both ordinary varlena arrays and fixed-length arrays.
2012 * array: the initial array object (mustn't be NULL)
2013 * nSubscripts: number of subscripts supplied
2014 * indx[]: the subscript values
2015 * dataValue: the datum to be inserted at the given position
2016 * isNull: whether dataValue is NULL
2017 * arraytyplen: pg_type.typlen for the array type
2018 * elmlen: pg_type.typlen for the array's element type
2019 * elmbyval: pg_type.typbyval for the array's element type
2020 * elmalign: pg_type.typalign for the array's element type
2023 * A new array is returned, just like the old except for the one
2024 * modified entry. The original array object is not changed.
2026 * For one-dimensional arrays only, we allow the array to be extended
2027 * by assigning to a position outside the existing subscript range; any
2028 * positions between the existing elements and the new one are set to NULLs.
2029 * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
2031 * NOTE: For assignments, we throw an error for invalid subscripts etc,
2032 * rather than returning a NULL as the fetch operations do.
2035 array_set(ArrayType *array,
2045 ArrayType *newarray;
2053 bits8 *oldnullbitmap;
2067 if (arraytyplen > 0)
2070 * fixed-length arrays -- these are assumed to be 1-d, 0-based. We
2071 * cannot extend them, either.
2073 if (nSubscripts != 1)
2075 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2076 errmsg("wrong number of array subscripts")));
2078 if (indx[0] < 0 || indx[0] * elmlen >= arraytyplen)
2080 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2081 errmsg("array subscript out of range")));
2085 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
2086 errmsg("cannot assign null value to an element of a fixed-length array")));
2088 newarray = (ArrayType *) palloc(arraytyplen);
2089 memcpy(newarray, array, arraytyplen);
2090 elt_ptr = (char *) newarray + indx[0] * elmlen;
2091 ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign, elt_ptr);
2095 if (nSubscripts <= 0 || nSubscripts > MAXDIM)
2097 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2098 errmsg("wrong number of array subscripts")));
2100 /* make sure item to be inserted is not toasted */
2101 if (elmlen == -1 && !isNull)
2102 dataValue = PointerGetDatum(PG_DETOAST_DATUM(dataValue));
2104 /* detoast input array if necessary */
2105 array = DatumGetArrayTypeP(PointerGetDatum(array));
2107 ndim = ARR_NDIM(array);
2110 * if number of dims is zero, i.e. an empty array, create an array with
2111 * nSubscripts dimensions, and set the lower bounds to the supplied
2116 Oid elmtype = ARR_ELEMTYPE(array);
2118 for (i = 0; i < nSubscripts; i++)
2124 return construct_md_array(&dataValue, &isNull, nSubscripts,
2126 elmlen, elmbyval, elmalign);
2129 if (ndim != nSubscripts)
2131 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2132 errmsg("wrong number of array subscripts")));
2134 /* copy dim/lb since we may modify them */
2135 memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
2136 memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
2138 newhasnulls = (ARR_HASNULL(array) || isNull);
2139 addedbefore = addedafter = 0;
2146 if (indx[0] < lb[0])
2148 addedbefore = lb[0] - indx[0];
2149 dim[0] += addedbefore;
2151 if (addedbefore > 1)
2152 newhasnulls = true; /* will insert nulls */
2154 if (indx[0] >= (dim[0] + lb[0]))
2156 addedafter = indx[0] - (dim[0] + lb[0]) + 1;
2157 dim[0] += addedafter;
2159 newhasnulls = true; /* will insert nulls */
2165 * XXX currently we do not support extending multi-dimensional arrays
2168 for (i = 0; i < ndim; i++)
2170 if (indx[i] < lb[i] ||
2171 indx[i] >= (dim[i] + lb[i]))
2173 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2174 errmsg("array subscript out of range")));
2179 * Compute sizes of items and areas to copy
2181 newnitems = ArrayGetNItems(ndim, dim);
2183 overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, newnitems);
2185 overheadlen = ARR_OVERHEAD_NONULLS(ndim);
2186 oldnitems = ArrayGetNItems(ndim, ARR_DIMS(array));
2187 oldnullbitmap = ARR_NULLBITMAP(array);
2188 oldoverheadlen = ARR_DATA_OFFSET(array);
2189 olddatasize = ARR_SIZE(array) - oldoverheadlen;
2195 lenafter = olddatasize;
2197 else if (addedafter)
2200 lenbefore = olddatasize;
2206 offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
2207 elt_ptr = array_seek(ARR_DATA_PTR(array), 0, oldnullbitmap, offset,
2208 elmlen, elmbyval, elmalign);
2209 lenbefore = (int) (elt_ptr - ARR_DATA_PTR(array));
2210 if (array_get_isnull(oldnullbitmap, offset))
2214 olditemlen = att_addlength_pointer(0, elmlen, elt_ptr);
2215 olditemlen = att_align_nominal(olditemlen, elmalign);
2217 lenafter = (int) (olddatasize - lenbefore - olditemlen);
2224 newitemlen = att_addlength_datum(0, elmlen, dataValue);
2225 newitemlen = att_align_nominal(newitemlen, elmalign);
2228 newsize = overheadlen + lenbefore + newitemlen + lenafter;
2231 * OK, create the new array and fill in header/dimensions
2233 newarray = (ArrayType *) palloc0(newsize);
2234 SET_VARSIZE(newarray, newsize);
2235 newarray->ndim = ndim;
2236 newarray->dataoffset = newhasnulls ? overheadlen : 0;
2237 newarray->elemtype = ARR_ELEMTYPE(array);
2238 memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
2239 memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
2244 memcpy((char *) newarray + overheadlen,
2245 (char *) array + oldoverheadlen,
2248 ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign,
2249 (char *) newarray + overheadlen + lenbefore);
2250 memcpy((char *) newarray + overheadlen + lenbefore + newitemlen,
2251 (char *) array + oldoverheadlen + lenbefore + olditemlen,
2255 * Fill in nulls bitmap if needed
2257 * Note: it's possible we just replaced the last NULL with a non-NULL, and
2258 * could get rid of the bitmap. Seems not worth testing for though.
2262 bits8 *newnullbitmap = ARR_NULLBITMAP(newarray);
2264 /* Zero the bitmap to take care of marking inserted positions null */
2265 MemSet(newnullbitmap, 0, (newnitems + 7) / 8);
2266 /* Fix the inserted value */
2268 array_set_isnull(newnullbitmap, newnitems - 1, isNull);
2270 array_set_isnull(newnullbitmap, offset, isNull);
2271 /* Fix the copied range(s) */
2273 array_bitmap_copy(newnullbitmap, addedbefore,
2278 array_bitmap_copy(newnullbitmap, 0,
2281 if (addedafter == 0)
2282 array_bitmap_copy(newnullbitmap, offset + 1,
2283 oldnullbitmap, offset + 1,
2284 oldnitems - offset - 1);
2293 * This routine sets the value of a range of array locations (specified
2294 * by upper and lower subscript values) to new values passed as
2297 * This handles both ordinary varlena arrays and fixed-length arrays.
2300 * array: the initial array object (mustn't be NULL)
2301 * nSubscripts: number of subscripts supplied (must be same for upper/lower)
2302 * upperIndx[]: the upper subscript values
2303 * lowerIndx[]: the lower subscript values
2304 * srcArray: the source for the inserted values
2305 * isNull: indicates whether srcArray is NULL
2306 * arraytyplen: pg_type.typlen for the array type
2307 * elmlen: pg_type.typlen for the array's element type
2308 * elmbyval: pg_type.typbyval for the array's element type
2309 * elmalign: pg_type.typalign for the array's element type
2312 * A new array is returned, just like the old except for the
2313 * modified range. The original array object is not changed.
2315 * For one-dimensional arrays only, we allow the array to be extended
2316 * by assigning to positions outside the existing subscript range; any
2317 * positions between the existing elements and the new ones are set to NULLs.
2318 * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
2320 * NOTE: we assume it is OK to scribble on the provided index arrays
2321 * lowerIndx[] and upperIndx[]. These are generally just temporaries.
2323 * NOTE: For assignments, we throw an error for silly subscripts etc,
2324 * rather than returning a NULL or empty array as the fetch operations do.
2327 array_set_slice(ArrayType *array,
2331 ArrayType *srcArray,
2338 ArrayType *newarray;
2361 /* Currently, assignment from a NULL source array is a no-op */
2365 if (arraytyplen > 0)
2368 * fixed-length arrays -- not got round to doing this...
2371 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2372 errmsg("updates on slices of fixed-length arrays not implemented")));
2375 /* detoast arrays if necessary */
2376 array = DatumGetArrayTypeP(PointerGetDatum(array));
2377 srcArray = DatumGetArrayTypeP(PointerGetDatum(srcArray));
2379 /* note: we assume srcArray contains no toasted elements */
2381 ndim = ARR_NDIM(array);
2384 * if number of dims is zero, i.e. an empty array, create an array with
2385 * nSubscripts dimensions, and set the upper and lower bounds to the
2386 * supplied subscripts
2393 Oid elmtype = ARR_ELEMTYPE(array);
2395 deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
2396 &dvalues, &dnulls, &nelems);
2398 for (i = 0; i < nSubscripts; i++)
2400 dim[i] = 1 + upperIndx[i] - lowerIndx[i];
2401 lb[i] = lowerIndx[i];
2404 /* complain if too few source items; we ignore extras, however */
2405 if (nelems < ArrayGetNItems(nSubscripts, dim))
2407 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2408 errmsg("source array too small")));
2410 return construct_md_array(dvalues, dnulls, nSubscripts,
2412 elmlen, elmbyval, elmalign);
2415 if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
2417 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2418 errmsg("wrong number of array subscripts")));
2420 /* copy dim/lb since we may modify them */
2421 memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
2422 memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
2424 newhasnulls = (ARR_HASNULL(array) || ARR_HASNULL(srcArray));
2425 addedbefore = addedafter = 0;
2432 Assert(nSubscripts == 1);
2433 if (lowerIndx[0] > upperIndx[0])
2435 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2436 errmsg("upper bound cannot be less than lower bound")));
2437 if (lowerIndx[0] < lb[0])
2439 if (upperIndx[0] < lb[0] - 1)
2440 newhasnulls = true; /* will insert nulls */
2441 addedbefore = lb[0] - lowerIndx[0];
2442 dim[0] += addedbefore;
2443 lb[0] = lowerIndx[0];
2445 if (upperIndx[0] >= (dim[0] + lb[0]))
2447 if (lowerIndx[0] > (dim[0] + lb[0]))
2448 newhasnulls = true; /* will insert nulls */
2449 addedafter = upperIndx[0] - (dim[0] + lb[0]) + 1;
2450 dim[0] += addedafter;
2456 * XXX currently we do not support extending multi-dimensional arrays
2459 for (i = 0; i < nSubscripts; i++)
2461 if (lowerIndx[i] > upperIndx[i])
2463 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2464 errmsg("upper bound cannot be less than lower bound")));
2465 if (lowerIndx[i] < lb[i] ||
2466 upperIndx[i] >= (dim[i] + lb[i]))
2468 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2469 errmsg("array subscript out of range")));
2471 /* fill any missing subscript positions with full array range */
2472 for (; i < ndim; i++)
2474 lowerIndx[i] = lb[i];
2475 upperIndx[i] = dim[i] + lb[i] - 1;
2476 if (lowerIndx[i] > upperIndx[i])
2478 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2479 errmsg("upper bound cannot be less than lower bound")));
2483 /* Do this mainly to check for overflow */
2484 nitems = ArrayGetNItems(ndim, dim);
2487 * Make sure source array has enough entries. Note we ignore the shape of
2488 * the source array and just read entries serially.
2490 mda_get_range(ndim, span, lowerIndx, upperIndx);
2491 nsrcitems = ArrayGetNItems(ndim, span);
2492 if (nsrcitems > ArrayGetNItems(ARR_NDIM(srcArray), ARR_DIMS(srcArray)))
2494 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2495 errmsg("source array too small")));
2498 * Compute space occupied by new entries, space occupied by replaced
2499 * entries, and required space for new array.
2502 overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
2504 overheadlen = ARR_OVERHEAD_NONULLS(ndim);
2505 newitemsize = array_nelems_size(ARR_DATA_PTR(srcArray), 0,
2506 ARR_NULLBITMAP(srcArray), nsrcitems,
2507 elmlen, elmbyval, elmalign);
2508 oldoverheadlen = ARR_DATA_OFFSET(array);
2509 olddatasize = ARR_SIZE(array) - oldoverheadlen;
2513 * here we do not need to cope with extension of the array; it would
2514 * be a lot more complicated if we had to do so...
2516 olditemsize = array_slice_size(ARR_DATA_PTR(array),
2517 ARR_NULLBITMAP(array),
2519 lowerIndx, upperIndx,
2520 elmlen, elmbyval, elmalign);
2521 lenbefore = lenafter = 0; /* keep compiler quiet */
2522 itemsbefore = itemsafter = nolditems = 0;
2527 * here we must allow for possibility of slice larger than orig array
2528 * and/or not adjacent to orig array subscripts
2530 int oldlb = ARR_LBOUND(array)[0];
2531 int oldub = oldlb + ARR_DIMS(array)[0] - 1;
2532 int slicelb = Max(oldlb, lowerIndx[0]);
2533 int sliceub = Min(oldub, upperIndx[0]);
2534 char *oldarraydata = ARR_DATA_PTR(array);
2535 bits8 *oldarraybitmap = ARR_NULLBITMAP(array);
2537 /* count/size of old array entries that will go before the slice */
2538 itemsbefore = Min(slicelb, oldub + 1) - oldlb;
2539 lenbefore = array_nelems_size(oldarraydata, 0, oldarraybitmap,
2541 elmlen, elmbyval, elmalign);
2542 /* count/size of old array entries that will be replaced by slice */
2543 if (slicelb > sliceub)
2550 nolditems = sliceub - slicelb + 1;
2551 olditemsize = array_nelems_size(oldarraydata + lenbefore,
2552 itemsbefore, oldarraybitmap,
2554 elmlen, elmbyval, elmalign);
2556 /* count/size of old array entries that will go after the slice */
2557 itemsafter = oldub + 1 - Max(sliceub + 1, oldlb);
2558 lenafter = olddatasize - lenbefore - olditemsize;
2561 newsize = overheadlen + olddatasize - olditemsize + newitemsize;
2563 newarray = (ArrayType *) palloc0(newsize);
2564 SET_VARSIZE(newarray, newsize);
2565 newarray->ndim = ndim;
2566 newarray->dataoffset = newhasnulls ? overheadlen : 0;
2567 newarray->elemtype = ARR_ELEMTYPE(array);
2568 memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
2569 memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
2574 * here we do not need to cope with extension of the array; it would
2575 * be a lot more complicated if we had to do so...
2577 array_insert_slice(newarray, array, srcArray,
2579 lowerIndx, upperIndx,
2580 elmlen, elmbyval, elmalign);
2585 memcpy((char *) newarray + overheadlen,
2586 (char *) array + oldoverheadlen,
2588 memcpy((char *) newarray + overheadlen + lenbefore,
2589 ARR_DATA_PTR(srcArray),
2591 memcpy((char *) newarray + overheadlen + lenbefore + newitemsize,
2592 (char *) array + oldoverheadlen + lenbefore + olditemsize,
2594 /* fill in nulls bitmap if needed */
2597 bits8 *newnullbitmap = ARR_NULLBITMAP(newarray);
2598 bits8 *oldnullbitmap = ARR_NULLBITMAP(array);
2600 /* Zero the bitmap to handle marking inserted positions null */
2601 MemSet(newnullbitmap, 0, (nitems + 7) / 8);
2602 array_bitmap_copy(newnullbitmap, addedbefore,
2605 array_bitmap_copy(newnullbitmap, lowerIndx[0] - lb[0],
2606 ARR_NULLBITMAP(srcArray), 0,
2608 array_bitmap_copy(newnullbitmap, addedbefore + itemsbefore + nolditems,
2609 oldnullbitmap, itemsbefore + nolditems,
2620 * Map an array through an arbitrary function. Return a new array with
2621 * same dimensions and each source element transformed by fn(). Each
2622 * source element is passed as the first argument to fn(); additional
2623 * arguments to be passed to fn() can be specified by the caller.
2624 * The output array can have a different element type than the input.
2627 * * fcinfo: a function-call data structure pre-constructed by the caller
2628 * to be ready to call the desired function, with everything except the
2629 * first argument position filled in. In particular, flinfo identifies
2630 * the function fn(), and if nargs > 1 then argument positions after the
2631 * first must be preset to the additional values to be passed. The
2632 * first argument position initially holds the input array value.
2633 * * inpType: OID of element type of input array. This must be the same as,
2634 * or binary-compatible with, the first argument type of fn().
2635 * * retType: OID of element type of output array. This must be the same as,
2636 * or binary-compatible with, the result type of fn().
2637 * * amstate: workspace for array_map. Must be zeroed by caller before
2638 * first call, and not touched after that.
2640 * It is legitimate to pass a freshly-zeroed ArrayMapState on each call,
2641 * but better performance can be had if the state can be preserved across
2642 * a series of calls.
2644 * NB: caller must assure that input array is not NULL. NULL elements in
2645 * the array are OK however.
2648 array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType,
2649 ArrayMapState *amstate)
2672 ArrayMetaState *inp_extra;
2673 ArrayMetaState *ret_extra;
2675 /* Get input array */
2676 if (fcinfo->nargs < 1)
2677 elog(ERROR, "invalid nargs: %d", fcinfo->nargs);
2678 if (PG_ARGISNULL(0))
2679 elog(ERROR, "null input array");
2680 v = PG_GETARG_ARRAYTYPE_P(0);
2682 Assert(ARR_ELEMTYPE(v) == inpType);
2686 nitems = ArrayGetNItems(ndim, dim);
2688 /* Check for empty array */
2691 /* Return empty array */
2692 PG_RETURN_ARRAYTYPE_P(construct_empty_array(retType));
2696 * We arrange to look up info about input and return element types only
2697 * once per series of calls, assuming the element type doesn't change
2700 inp_extra = &amstate->inp_extra;
2701 ret_extra = &amstate->ret_extra;
2703 if (inp_extra->element_type != inpType)
2705 get_typlenbyvalalign(inpType,
2707 &inp_extra->typbyval,
2708 &inp_extra->typalign);
2709 inp_extra->element_type = inpType;
2711 inp_typlen = inp_extra->typlen;
2712 inp_typbyval = inp_extra->typbyval;
2713 inp_typalign = inp_extra->typalign;
2715 if (ret_extra->element_type != retType)
2717 get_typlenbyvalalign(retType,
2719 &ret_extra->typbyval,
2720 &ret_extra->typalign);
2721 ret_extra->element_type = retType;
2723 typlen = ret_extra->typlen;
2724 typbyval = ret_extra->typbyval;
2725 typalign = ret_extra->typalign;
2727 /* Allocate temporary arrays for new values */
2728 values = (Datum *) palloc(nitems * sizeof(Datum));
2729 nulls = (bool *) palloc(nitems * sizeof(bool));
2731 /* Loop over source data */
2732 s = ARR_DATA_PTR(v);
2733 bitmap = ARR_NULLBITMAP(v);
2737 for (i = 0; i < nitems; i++)
2741 /* Get source element, checking for NULL */
2742 if (bitmap && (*bitmap & bitmask) == 0)
2744 fcinfo->argnull[0] = true;
2748 elt = fetch_att(s, inp_typbyval, inp_typlen);
2749 s = att_addlength_datum(s, inp_typlen, elt);
2750 s = (char *) att_align_nominal(s, inp_typalign);
2751 fcinfo->arg[0] = elt;
2752 fcinfo->argnull[0] = false;
2756 * Apply the given function to source elt and extra args.
2758 if (fcinfo->flinfo->fn_strict)
2762 for (j = 0; j < fcinfo->nargs; j++)
2764 if (fcinfo->argnull[j])
2774 fcinfo->isnull = false;
2775 values[i] = FunctionCallInvoke(fcinfo);
2778 fcinfo->isnull = true;
2780 nulls[i] = fcinfo->isnull;
2785 /* Ensure data is not toasted */
2787 values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
2788 /* Update total result size */
2789 nbytes = att_addlength_datum(nbytes, typlen, values[i]);
2790 nbytes = att_align_nominal(nbytes, typalign);
2791 /* check for overflow of total request */
2792 if (!AllocSizeIsValid(nbytes))
2794 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2795 errmsg("array size exceeds the maximum allowed (%d)",
2796 (int) MaxAllocSize)));
2799 /* advance bitmap pointer if any */
2803 if (bitmask == 0x100)
2811 /* Allocate and initialize the result array */
2814 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
2815 nbytes += dataoffset;
2819 dataoffset = 0; /* marker for no null bitmap */
2820 nbytes += ARR_OVERHEAD_NONULLS(ndim);
2822 result = (ArrayType *) palloc0(nbytes);
2823 SET_VARSIZE(result, nbytes);
2824 result->ndim = ndim;
2825 result->dataoffset = dataoffset;
2826 result->elemtype = retType;
2827 memcpy(ARR_DIMS(result), ARR_DIMS(v), 2 * ndim * sizeof(int));
2830 * Note: do not risk trying to pfree the results of the called function
2832 CopyArrayEls(result,
2833 values, nulls, nitems,
2834 typlen, typbyval, typalign,
2840 PG_RETURN_ARRAYTYPE_P(result);
2844 * construct_array --- simple method for constructing an array object
2846 * elems: array of Datum items to become the array contents
2847 * (NULL element values are not supported).
2848 * nelems: number of items
2849 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
2851 * A palloc'd 1-D array object is constructed and returned. Note that
2852 * elem values will be copied into the object even if pass-by-ref type.
2854 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
2855 * from the system catalogs, given the elmtype. However, the caller is
2856 * in a better position to cache this info across multiple uses, or even
2857 * to hard-wire values if the element type is hard-wired.
2860 construct_array(Datum *elems, int nelems,
2862 int elmlen, bool elmbyval, char elmalign)
2870 return construct_md_array(elems, NULL, 1, dims, lbs,
2871 elmtype, elmlen, elmbyval, elmalign);
2875 * construct_md_array --- simple method for constructing an array object
2876 * with arbitrary dimensions and possible NULLs
2878 * elems: array of Datum items to become the array contents
2879 * nulls: array of is-null flags (can be NULL if no nulls)
2880 * ndims: number of dimensions
2881 * dims: integer array with size of each dimension
2882 * lbs: integer array with lower bound of each dimension
2883 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
2885 * A palloc'd ndims-D array object is constructed and returned. Note that
2886 * elem values will be copied into the object even if pass-by-ref type.
2888 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
2889 * from the system catalogs, given the elmtype. However, the caller is
2890 * in a better position to cache this info across multiple uses, or even
2891 * to hard-wire values if the element type is hard-wired.
2894 construct_md_array(Datum *elems,
2899 Oid elmtype, int elmlen, bool elmbyval, char elmalign)
2908 if (ndims < 0) /* we do allow zero-dimension arrays */
2910 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2911 errmsg("invalid number of dimensions: %d", ndims)));
2914 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2915 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
2918 /* fast track for empty array */
2920 return construct_empty_array(elmtype);
2922 nelems = ArrayGetNItems(ndims, dims);
2924 /* compute required space */
2927 for (i = 0; i < nelems; i++)
2929 if (nulls && nulls[i])
2934 /* make sure data is not toasted */
2936 elems[i] = PointerGetDatum(PG_DETOAST_DATUM(elems[i]));
2937 nbytes = att_addlength_datum(nbytes, elmlen, elems[i]);
2938 nbytes = att_align_nominal(nbytes, elmalign);
2939 /* check for overflow of total request */
2940 if (!AllocSizeIsValid(nbytes))
2942 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2943 errmsg("array size exceeds the maximum allowed (%d)",
2944 (int) MaxAllocSize)));
2947 /* Allocate and initialize result array */
2950 dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nelems);
2951 nbytes += dataoffset;
2955 dataoffset = 0; /* marker for no null bitmap */
2956 nbytes += ARR_OVERHEAD_NONULLS(ndims);
2958 result = (ArrayType *) palloc0(nbytes);
2959 SET_VARSIZE(result, nbytes);
2960 result->ndim = ndims;
2961 result->dataoffset = dataoffset;
2962 result->elemtype = elmtype;
2963 memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
2964 memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
2966 CopyArrayEls(result,
2967 elems, nulls, nelems,
2968 elmlen, elmbyval, elmalign,
2975 * construct_empty_array --- make a zero-dimensional array of given type
2978 construct_empty_array(Oid elmtype)
2982 result = (ArrayType *) palloc0(sizeof(ArrayType));
2983 SET_VARSIZE(result, sizeof(ArrayType));
2985 result->dataoffset = 0;
2986 result->elemtype = elmtype;
2991 * deconstruct_array --- simple method for extracting data from an array
2993 * array: array object to examine (must not be NULL)
2994 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
2995 * elemsp: return value, set to point to palloc'd array of Datum values
2996 * nullsp: return value, set to point to palloc'd array of isnull markers
2997 * nelemsp: return value, set to number of extracted values
2999 * The caller may pass nullsp == NULL if it does not support NULLs in the
3000 * array. Note that this produces a very uninformative error message,
3001 * so do it only in cases where a NULL is really not expected.
3003 * If array elements are pass-by-ref data type, the returned Datums will
3004 * be pointers into the array object.
3006 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
3007 * from the system catalogs, given the elmtype. However, in most current
3008 * uses the type is hard-wired into the caller and so we can save a lookup
3009 * cycle by hard-wiring the type info as well.
3012 deconstruct_array(ArrayType *array,
3014 int elmlen, bool elmbyval, char elmalign,
3015 Datum **elemsp, bool **nullsp, int *nelemsp)
3025 Assert(ARR_ELEMTYPE(array) == elmtype);
3027 nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
3028 *elemsp = elems = (Datum *) palloc(nelems * sizeof(Datum));
3030 *nullsp = nulls = (bool *) palloc0(nelems * sizeof(bool));
3035 p = ARR_DATA_PTR(array);
3036 bitmap = ARR_NULLBITMAP(array);
3039 for (i = 0; i < nelems; i++)
3041 /* Get source element, checking for NULL */
3042 if (bitmap && (*bitmap & bitmask) == 0)
3044 elems[i] = (Datum) 0;
3049 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
3050 errmsg("null array element not allowed in this context")));
3054 elems[i] = fetch_att(p, elmbyval, elmlen);
3055 p = att_addlength_pointer(p, elmlen, p);
3056 p = (char *) att_align_nominal(p, elmalign);
3059 /* advance bitmap pointer if any */
3063 if (bitmask == 0x100)
3073 * array_contains_nulls --- detect whether an array has any null elements
3075 * This gives an accurate answer, whereas testing ARR_HASNULL only tells
3076 * if the array *might* contain a null.
3079 array_contains_nulls(ArrayType *array)
3085 /* Easy answer if there's no null bitmap */
3086 if (!ARR_HASNULL(array))
3089 nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
3091 bitmap = ARR_NULLBITMAP(array);
3093 /* check whole bytes of the bitmap byte-at-a-time */
3096 if (*bitmap != 0xFF)
3102 /* check last partial byte */
3106 if ((*bitmap & bitmask) == 0)
3118 * compares two arrays for equality
3120 * returns true if the arrays are equal, false otherwise.
3122 * Note: we do not use array_cmp here, since equality may be meaningful in
3123 * datatypes that don't have a total ordering (and hence no btree support).
3126 array_eq(PG_FUNCTION_ARGS)
3128 ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
3129 ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
3130 Oid collation = PG_GET_COLLATION();
3131 int ndims1 = ARR_NDIM(array1);
3132 int ndims2 = ARR_NDIM(array2);
3133 int *dims1 = ARR_DIMS(array1);
3134 int *dims2 = ARR_DIMS(array2);
3135 Oid element_type = ARR_ELEMTYPE(array1);
3138 TypeCacheEntry *typentry;
3148 FunctionCallInfoData locfcinfo;
3150 if (element_type != ARR_ELEMTYPE(array2))
3152 (errcode(ERRCODE_DATATYPE_MISMATCH),
3153 errmsg("cannot compare arrays of different element types")));
3155 /* fast path if the arrays do not have the same dimensionality */
3156 if (ndims1 != ndims2 ||
3157 memcmp(dims1, dims2, 2 * ndims1 * sizeof(int)) != 0)
3162 * We arrange to look up the equality function only once per series of
3163 * calls, assuming the element type doesn't change underneath us. The
3164 * typcache is used so that we have no memory leakage when being used
3165 * as an index support function.
3167 typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3168 if (typentry == NULL ||
3169 typentry->type_id != element_type)
3171 typentry = lookup_type_cache(element_type,
3172 TYPECACHE_EQ_OPR_FINFO);
3173 if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
3175 (errcode(ERRCODE_UNDEFINED_FUNCTION),
3176 errmsg("could not identify an equality operator for type %s",
3177 format_type_be(element_type))));
3178 fcinfo->flinfo->fn_extra = (void *) typentry;
3180 typlen = typentry->typlen;
3181 typbyval = typentry->typbyval;
3182 typalign = typentry->typalign;
3185 * apply the operator to each pair of array elements.
3187 InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
3188 collation, NULL, NULL);
3190 /* Loop over source data */
3191 nitems = ArrayGetNItems(ndims1, dims1);
3192 ptr1 = ARR_DATA_PTR(array1);
3193 ptr2 = ARR_DATA_PTR(array2);
3194 bitmap1 = ARR_NULLBITMAP(array1);
3195 bitmap2 = ARR_NULLBITMAP(array2);
3196 bitmask = 1; /* use same bitmask for both arrays */
3198 for (i = 0; i < nitems; i++)
3206 /* Get elements, checking for NULL */
3207 if (bitmap1 && (*bitmap1 & bitmask) == 0)
3215 elt1 = fetch_att(ptr1, typbyval, typlen);
3216 ptr1 = att_addlength_pointer(ptr1, typlen, ptr1);
3217 ptr1 = (char *) att_align_nominal(ptr1, typalign);
3220 if (bitmap2 && (*bitmap2 & bitmask) == 0)
3228 elt2 = fetch_att(ptr2, typbyval, typlen);
3229 ptr2 = att_addlength_pointer(ptr2, typlen, ptr2);
3230 ptr2 = (char *) att_align_nominal(ptr2, typalign);
3233 /* advance bitmap pointers if any */
3235 if (bitmask == 0x100)
3245 * We consider two NULLs equal; NULL and not-NULL are unequal.
3247 if (isnull1 && isnull2)
3249 if (isnull1 || isnull2)
3256 * Apply the operator to the element pair
3258 locfcinfo.arg[0] = elt1;
3259 locfcinfo.arg[1] = elt2;
3260 locfcinfo.argnull[0] = false;
3261 locfcinfo.argnull[1] = false;
3262 locfcinfo.isnull = false;
3263 oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
3272 /* Avoid leaking memory when handed toasted input. */
3273 PG_FREE_IF_COPY(array1, 0);
3274 PG_FREE_IF_COPY(array2, 1);
3276 PG_RETURN_BOOL(result);
3280 /*-----------------------------------------------------------------------------
3281 * array-array bool operators:
3282 * Given two arrays, iterate comparison operators
3283 * over the array. Uses logic similar to text comparison
3284 * functions, except element-by-element instead of
3285 * character-by-character.
3286 *----------------------------------------------------------------------------
3290 array_ne(PG_FUNCTION_ARGS)
3292 PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo)));
3296 array_lt(PG_FUNCTION_ARGS)
3298 PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
3302 array_gt(PG_FUNCTION_ARGS)
3304 PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
3308 array_le(PG_FUNCTION_ARGS)
3310 PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
3314 array_ge(PG_FUNCTION_ARGS)
3316 PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
3320 btarraycmp(PG_FUNCTION_ARGS)
3322 PG_RETURN_INT32(array_cmp(fcinfo));
3327 * Internal comparison function for arrays.
3329 * Returns -1, 0 or 1
3332 array_cmp(FunctionCallInfo fcinfo)
3334 ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
3335 ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
3336 Oid collation = PG_GET_COLLATION();
3337 int ndims1 = ARR_NDIM(array1);
3338 int ndims2 = ARR_NDIM(array2);
3339 int *dims1 = ARR_DIMS(array1);
3340 int *dims2 = ARR_DIMS(array2);
3341 int nitems1 = ArrayGetNItems(ndims1, dims1);
3342 int nitems2 = ArrayGetNItems(ndims2, dims2);
3343 Oid element_type = ARR_ELEMTYPE(array1);
3345 TypeCacheEntry *typentry;
3356 FunctionCallInfoData locfcinfo;
3358 if (element_type != ARR_ELEMTYPE(array2))
3360 (errcode(ERRCODE_DATATYPE_MISMATCH),
3361 errmsg("cannot compare arrays of different element types")));
3364 * We arrange to look up the comparison function only once per series of
3365 * calls, assuming the element type doesn't change underneath us. The
3366 * typcache is used so that we have no memory leakage when being used as
3367 * an index support function.
3369 typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3370 if (typentry == NULL ||
3371 typentry->type_id != element_type)
3373 typentry = lookup_type_cache(element_type,
3374 TYPECACHE_CMP_PROC_FINFO);
3375 if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
3377 (errcode(ERRCODE_UNDEFINED_FUNCTION),
3378 errmsg("could not identify a comparison function for type %s",
3379 format_type_be(element_type))));
3380 fcinfo->flinfo->fn_extra = (void *) typentry;
3382 typlen = typentry->typlen;
3383 typbyval = typentry->typbyval;
3384 typalign = typentry->typalign;
3387 * apply the operator to each pair of array elements.
3389 InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2,
3390 collation, NULL, NULL);
3392 /* Loop over source data */
3393 min_nitems = Min(nitems1, nitems2);
3394 ptr1 = ARR_DATA_PTR(array1);
3395 ptr2 = ARR_DATA_PTR(array2);
3396 bitmap1 = ARR_NULLBITMAP(array1);
3397 bitmap2 = ARR_NULLBITMAP(array2);
3398 bitmask = 1; /* use same bitmask for both arrays */
3400 for (i = 0; i < min_nitems; i++)
3408 /* Get elements, checking for NULL */
3409 if (bitmap1 && (*bitmap1 & bitmask) == 0)
3417 elt1 = fetch_att(ptr1, typbyval, typlen);
3418 ptr1 = att_addlength_pointer(ptr1, typlen, ptr1);
3419 ptr1 = (char *) att_align_nominal(ptr1, typalign);
3422 if (bitmap2 && (*bitmap2 & bitmask) == 0)
3430 elt2 = fetch_att(ptr2, typbyval, typlen);
3431 ptr2 = att_addlength_pointer(ptr2, typlen, ptr2);
3432 ptr2 = (char *) att_align_nominal(ptr2, typalign);
3435 /* advance bitmap pointers if any */
3437 if (bitmask == 0x100)
3447 * We consider two NULLs equal; NULL > not-NULL.
3449 if (isnull1 && isnull2)
3453 /* arg1 is greater than arg2 */
3459 /* arg1 is less than arg2 */
3464 /* Compare the pair of elements */
3465 locfcinfo.arg[0] = elt1;
3466 locfcinfo.arg[1] = elt2;
3467 locfcinfo.argnull[0] = false;
3468 locfcinfo.argnull[1] = false;
3469 locfcinfo.isnull = false;
3470 cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
3473 continue; /* equal */
3477 /* arg1 is less than arg2 */
3483 /* arg1 is greater than arg2 */
3490 * If arrays contain same data (up to end of shorter one), apply
3491 * additional rules to sort by dimensionality. The relative significance
3492 * of the different bits of information is historical; mainly we just care
3493 * that we don't say "equal" for arrays of different dimensionality.
3497 if (nitems1 != nitems2)
3498 result = (nitems1 < nitems2) ? -1 : 1;
3499 else if (ndims1 != ndims2)
3500 result = (ndims1 < ndims2) ? -1 : 1;
3503 /* this relies on LB array immediately following DIMS array */
3504 for (i = 0; i < ndims1 * 2; i++)
3506 if (dims1[i] != dims2[i])
3508 result = (dims1[i] < dims2[i]) ? -1 : 1;
3515 /* Avoid leaking memory when handed toasted input. */
3516 PG_FREE_IF_COPY(array1, 0);
3517 PG_FREE_IF_COPY(array2, 1);
3523 /*-----------------------------------------------------------------------------
3525 * Hash the elements and combine the results.
3526 *----------------------------------------------------------------------------
3530 hash_array(PG_FUNCTION_ARGS)
3532 ArrayType *array = PG_GETARG_ARRAYTYPE_P(0);
3533 int ndims = ARR_NDIM(array);
3534 int *dims = ARR_DIMS(array);
3535 Oid element_type = ARR_ELEMTYPE(array);
3538 TypeCacheEntry *typentry;
3546 FunctionCallInfoData locfcinfo;
3549 * We arrange to look up the hash function only once per series of calls,
3550 * assuming the element type doesn't change underneath us. The typcache
3551 * is used so that we have no memory leakage when being used as an index
3554 typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3555 if (typentry == NULL ||
3556 typentry->type_id != element_type)
3558 typentry = lookup_type_cache(element_type,
3559 TYPECACHE_HASH_PROC_FINFO);
3560 if (!OidIsValid(typentry->hash_proc_finfo.fn_oid))
3562 (errcode(ERRCODE_UNDEFINED_FUNCTION),
3563 errmsg("could not identify a hash function for type %s",
3564 format_type_be(element_type))));
3565 fcinfo->flinfo->fn_extra = (void *) typentry;
3567 typlen = typentry->typlen;
3568 typbyval = typentry->typbyval;
3569 typalign = typentry->typalign;
3572 * apply the hash function to each array element.
3574 InitFunctionCallInfoData(locfcinfo, &typentry->hash_proc_finfo, 1,
3575 InvalidOid, NULL, NULL);
3577 /* Loop over source data */
3578 nitems = ArrayGetNItems(ndims, dims);
3579 ptr = ARR_DATA_PTR(array);
3580 bitmap = ARR_NULLBITMAP(array);
3583 for (i = 0; i < nitems; i++)
3587 /* Get element, checking for NULL */
3588 if (bitmap && (*bitmap & bitmask) == 0)
3590 /* Treat nulls as having hashvalue 0 */
3597 elt = fetch_att(ptr, typbyval, typlen);
3598 ptr = att_addlength_pointer(ptr, typlen, ptr);
3599 ptr = (char *) att_align_nominal(ptr, typalign);
3601 /* Apply the hash function */
3602 locfcinfo.arg[0] = elt;
3603 locfcinfo.argnull[0] = false;
3604 locfcinfo.isnull = false;
3605 elthash = DatumGetUInt32(FunctionCallInvoke(&locfcinfo));
3608 /* advance bitmap pointer if any */
3612 if (bitmask == 0x100)
3620 * Combine hash values of successive elements by multiplying the
3621 * current value by 31 and adding on the new element's hash value.
3623 * The result is a sum in which each element's hash value is
3624 * multiplied by a different power of 31. This is modulo 2^32
3625 * arithmetic, and the powers of 31 modulo 2^32 form a cyclic group of
3626 * order 2^27. So for arrays of up to 2^27 elements, each element's
3627 * hash value is multiplied by a different (odd) number, resulting in
3628 * a good mixing of all the elements' hash values.
3630 result = (result << 5) - result + elthash;
3633 /* Avoid leaking memory when handed toasted input. */
3634 PG_FREE_IF_COPY(array, 0);
3636 PG_RETURN_UINT32(result);
3640 /*-----------------------------------------------------------------------------
3641 * array overlap/containment comparisons
3642 * These use the same methods of comparing array elements as array_eq.
3643 * We consider only the elements of the arrays, ignoring dimensionality.
3644 *----------------------------------------------------------------------------
3648 * array_contain_compare :
3649 * compares two arrays for overlap/containment
3651 * When matchall is true, return true if all members of array1 are in array2.
3652 * When matchall is false, return true if any members of array1 are in array2.
3655 array_contain_compare(ArrayType *array1, ArrayType *array2, Oid collation,
3656 bool matchall, void **fn_extra)
3658 bool result = matchall;
3659 Oid element_type = ARR_ELEMTYPE(array1);
3660 TypeCacheEntry *typentry;
3673 FunctionCallInfoData locfcinfo;
3675 if (element_type != ARR_ELEMTYPE(array2))
3677 (errcode(ERRCODE_DATATYPE_MISMATCH),
3678 errmsg("cannot compare arrays of different element types")));
3681 * We arrange to look up the equality function only once per series of
3682 * calls, assuming the element type doesn't change underneath us. The
3683 * typcache is used so that we have no memory leakage when being used as
3684 * an index support function.
3686 typentry = (TypeCacheEntry *) *fn_extra;
3687 if (typentry == NULL ||
3688 typentry->type_id != element_type)
3690 typentry = lookup_type_cache(element_type,
3691 TYPECACHE_EQ_OPR_FINFO);
3692 if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
3694 (errcode(ERRCODE_UNDEFINED_FUNCTION),
3695 errmsg("could not identify an equality operator for type %s",
3696 format_type_be(element_type))));
3697 *fn_extra = (void *) typentry;
3699 typlen = typentry->typlen;
3700 typbyval = typentry->typbyval;
3701 typalign = typentry->typalign;
3704 * Since we probably will need to scan array2 multiple times, it's
3705 * worthwhile to use deconstruct_array on it. We scan array1 the hard way
3706 * however, since we very likely won't need to look at all of it.
3708 deconstruct_array(array2, element_type, typlen, typbyval, typalign,
3709 &values2, &nulls2, &nelems2);
3712 * Apply the comparison operator to each pair of array elements.
3714 InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
3715 collation, NULL, NULL);
3717 /* Loop over source data */
3718 nelems1 = ArrayGetNItems(ARR_NDIM(array1), ARR_DIMS(array1));
3719 ptr1 = ARR_DATA_PTR(array1);
3720 bitmap1 = ARR_NULLBITMAP(array1);
3723 for (i = 0; i < nelems1; i++)
3728 /* Get element, checking for NULL */
3729 if (bitmap1 && (*bitmap1 & bitmask) == 0)
3737 elt1 = fetch_att(ptr1, typbyval, typlen);
3738 ptr1 = att_addlength_pointer(ptr1, typlen, ptr1);
3739 ptr1 = (char *) att_align_nominal(ptr1, typalign);
3742 /* advance bitmap pointer if any */
3744 if (bitmask == 0x100)
3752 * We assume that the comparison operator is strict, so a NULL can't
3753 * match anything. XXX this diverges from the "NULL=NULL" behavior of
3754 * array_eq, should we act like that?
3766 for (j = 0; j < nelems2; j++)
3768 Datum elt2 = values2[j];
3769 bool isnull2 = nulls2[j];
3773 continue; /* can't match */
3776 * Apply the operator to the element pair
3778 locfcinfo.arg[0] = elt1;
3779 locfcinfo.arg[1] = elt2;
3780 locfcinfo.argnull[0] = false;
3781 locfcinfo.argnull[1] = false;
3782 locfcinfo.isnull = false;
3783 oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
3790 /* found a match for elt1 */
3799 /* no match for elt1 */
3815 arrayoverlap(PG_FUNCTION_ARGS)
3817 ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
3818 ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
3819 Oid collation = PG_GET_COLLATION();
3822 result = array_contain_compare(array1, array2, collation, false,
3823 &fcinfo->flinfo->fn_extra);
3825 /* Avoid leaking memory when handed toasted input. */
3826 PG_FREE_IF_COPY(array1, 0);
3827 PG_FREE_IF_COPY(array2, 1);
3829 PG_RETURN_BOOL(result);
3833 arraycontains(PG_FUNCTION_ARGS)
3835 ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
3836 ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
3837 Oid collation = PG_GET_COLLATION();
3840 result = array_contain_compare(array2, array1, collation, true,
3841 &fcinfo->flinfo->fn_extra);
3843 /* Avoid leaking memory when handed toasted input. */
3844 PG_FREE_IF_COPY(array1, 0);
3845 PG_FREE_IF_COPY(array2, 1);
3847 PG_RETURN_BOOL(result);
3851 arraycontained(PG_FUNCTION_ARGS)
3853 ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
3854 ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
3855 Oid collation = PG_GET_COLLATION();
3858 result = array_contain_compare(array1, array2, collation, true,
3859 &fcinfo->flinfo->fn_extra);
3861 /* Avoid leaking memory when handed toasted input. */
3862 PG_FREE_IF_COPY(array1, 0);
3863 PG_FREE_IF_COPY(array2, 1);
3865 PG_RETURN_BOOL(result);
3869 /*-----------------------------------------------------------------------------
3870 * Array iteration functions
3871 * These functions are used to iterate efficiently through arrays
3872 *-----------------------------------------------------------------------------
3876 * array_create_iterator --- set up to iterate through an array
3878 * If slice_ndim is zero, we will iterate element-by-element; the returned
3879 * datums are of the array's element type.
3881 * If slice_ndim is 1..ARR_NDIM(arr), we will iterate by slices: the
3882 * returned datums are of the same array type as 'arr', but of size
3883 * equal to the rightmost N dimensions of 'arr'.
3885 * The passed-in array must remain valid for the lifetime of the iterator.
3888 array_create_iterator(ArrayType *arr, int slice_ndim)
3890 ArrayIterator iterator = palloc0(sizeof(ArrayIteratorData));
3893 * Sanity-check inputs --- caller should have got this right already
3895 Assert(PointerIsValid(arr));
3896 if (slice_ndim < 0 || slice_ndim > ARR_NDIM(arr))
3897 elog(ERROR, "invalid arguments to array_create_iterator");
3900 * Remember basic info about the array and its element type
3902 iterator->arr = arr;
3903 iterator->nullbitmap = ARR_NULLBITMAP(arr);
3904 iterator->nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
3905 get_typlenbyvalalign(ARR_ELEMTYPE(arr),
3907 &iterator->typbyval,
3908 &iterator->typalign);
3911 * Remember the slicing parameters.
3913 iterator->slice_ndim = slice_ndim;
3918 * Get pointers into the array's dims and lbound arrays to represent
3919 * the dims/lbound arrays of a slice. These are the same as the
3920 * rightmost N dimensions of the array.
3922 iterator->slice_dims = ARR_DIMS(arr) + ARR_NDIM(arr) - slice_ndim;
3923 iterator->slice_lbound = ARR_LBOUND(arr) + ARR_NDIM(arr) - slice_ndim;
3926 * Compute number of elements in a slice.
3928 iterator->slice_len = ArrayGetNItems(slice_ndim,
3929 iterator->slice_dims);
3932 * Create workspace for building sub-arrays.
3934 iterator->slice_values = (Datum *)
3935 palloc(iterator->slice_len * sizeof(Datum));
3936 iterator->slice_nulls = (bool *)
3937 palloc(iterator->slice_len * sizeof(bool));
3941 * Initialize our data pointer and linear element number. These will
3942 * advance through the array during array_iterate().
3944 iterator->data_ptr = ARR_DATA_PTR(arr);
3945 iterator->current_item = 0;
3951 * Iterate through the array referenced by 'iterator'.
3953 * As long as there is another element (or slice), return it into
3954 * *value / *isnull, and return true. Return false when no more data.
3957 array_iterate(ArrayIterator iterator, Datum *value, bool *isnull)
3959 /* Done if we have reached the end of the array */
3960 if (iterator->current_item >= iterator->nitems)
3963 if (iterator->slice_ndim == 0)
3966 * Scalar case: return one element.
3968 if (array_get_isnull(iterator->nullbitmap, iterator->current_item++))
3975 /* non-NULL, so fetch the individual Datum to return */
3976 char *p = iterator->data_ptr;
3979 *value = fetch_att(p, iterator->typbyval, iterator->typlen);
3981 /* Move our data pointer forward to the next element */
3982 p = att_addlength_pointer(p, iterator->typlen, p);
3983 p = (char *) att_align_nominal(p, iterator->typalign);
3984 iterator->data_ptr = p;
3990 * Slice case: build and return an array of the requested size.
3993 Datum *values = iterator->slice_values;
3994 bool *nulls = iterator->slice_nulls;
3995 char *p = iterator->data_ptr;
3998 for (i = 0; i < iterator->slice_len; i++)
4000 if (array_get_isnull(iterator->nullbitmap,
4001 iterator->current_item++))
4004 values[i] = (Datum) 0;
4009 values[i] = fetch_att(p, iterator->typbyval, iterator->typlen);
4011 /* Move our data pointer forward to the next element */
4012 p = att_addlength_pointer(p, iterator->typlen, p);
4013 p = (char *) att_align_nominal(p, iterator->typalign);
4017 iterator->data_ptr = p;
4019 result = construct_md_array(values,
4021 iterator->slice_ndim,
4022 iterator->slice_dims,
4023 iterator->slice_lbound,
4024 ARR_ELEMTYPE(iterator->arr),
4027 iterator->typalign);
4030 *value = PointerGetDatum(result);
4037 * Release an ArrayIterator data structure
4040 array_free_iterator(ArrayIterator iterator)
4042 if (iterator->slice_ndim > 0)
4044 pfree(iterator->slice_values);
4045 pfree(iterator->slice_nulls);
4051 /***************************************************************************/
4052 /******************| Support Routines |*****************/
4053 /***************************************************************************/
4056 * Check whether a specific array element is NULL
4058 * nullbitmap: pointer to array's null bitmap (NULL if none)
4059 * offset: 0-based linear element number of array element
4062 array_get_isnull(const bits8 *nullbitmap, int offset)
4064 if (nullbitmap == NULL)
4065 return false; /* assume not null */
4066 if (nullbitmap[offset / 8] & (1 << (offset % 8)))
4067 return false; /* not null */
4072 * Set a specific array element's null-bitmap entry
4074 * nullbitmap: pointer to array's null bitmap (mustn't be NULL)
4075 * offset: 0-based linear element number of array element
4076 * isNull: null status to set
4079 array_set_isnull(bits8 *nullbitmap, int offset, bool isNull)
4083 nullbitmap += offset / 8;
4084 bitmask = 1 << (offset % 8);
4086 *nullbitmap &= ~bitmask;
4088 *nullbitmap |= bitmask;
4092 * Fetch array element at pointer, converted correctly to a Datum
4094 * Caller must have handled case of NULL element
4097 ArrayCast(char *value, bool byval, int len)
4099 return fetch_att(value, byval, len);
4103 * Copy datum to *dest and return total space used (including align padding)
4105 * Caller must have handled case of NULL element
4108 ArrayCastAndSet(Datum src,
4119 store_att_byval(dest, src, typlen);
4121 memmove(dest, DatumGetPointer(src), typlen);
4122 inc = att_align_nominal(typlen, typalign);
4127 inc = att_addlength_datum(0, typlen, src);
4128 memmove(dest, DatumGetPointer(src), inc);
4129 inc = att_align_nominal(inc, typalign);
4136 * Advance ptr over nitems array elements
4138 * ptr: starting location in array
4139 * offset: 0-based linear element number of first element (the one at *ptr)
4140 * nullbitmap: start of array's null bitmap, or NULL if none
4141 * nitems: number of array elements to advance over (>= 0)
4142 * typlen, typbyval, typalign: storage parameters of array element datatype
4144 * It is caller's responsibility to ensure that nitems is within range
4147 array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
4148 int typlen, bool typbyval, char typalign)
4153 /* easy if fixed-size elements and no NULLs */
4154 if (typlen > 0 && !nullbitmap)
4155 return ptr + nitems * ((Size) att_align_nominal(typlen, typalign));
4157 /* seems worth having separate loops for NULL and no-NULLs cases */
4160 nullbitmap += offset / 8;
4161 bitmask = 1 << (offset % 8);
4163 for (i = 0; i < nitems; i++)
4165 if (*nullbitmap & bitmask)
4167 ptr = att_addlength_pointer(ptr, typlen, ptr);
4168 ptr = (char *) att_align_nominal(ptr, typalign);
4171 if (bitmask == 0x100)
4180 for (i = 0; i < nitems; i++)
4182 ptr = att_addlength_pointer(ptr, typlen, ptr);
4183 ptr = (char *) att_align_nominal(ptr, typalign);
4190 * Compute total size of the nitems array elements starting at *ptr
4192 * Parameters same as for array_seek
4195 array_nelems_size(char *ptr, int offset, bits8 *nullbitmap, int nitems,
4196 int typlen, bool typbyval, char typalign)
4198 return array_seek(ptr, offset, nullbitmap, nitems,
4199 typlen, typbyval, typalign) - ptr;
4203 * Copy nitems array elements from srcptr to destptr
4205 * destptr: starting destination location (must be enough room!)
4206 * nitems: number of array elements to copy (>= 0)
4207 * srcptr: starting location in source array
4208 * offset: 0-based linear element number of first element (the one at *srcptr)
4209 * nullbitmap: start of source array's null bitmap, or NULL if none
4210 * typlen, typbyval, typalign: storage parameters of array element datatype
4212 * Returns number of bytes copied
4214 * NB: this does not take care of setting up the destination's null bitmap!
4217 array_copy(char *destptr, int nitems,
4218 char *srcptr, int offset, bits8 *nullbitmap,
4219 int typlen, bool typbyval, char typalign)
4223 numbytes = array_nelems_size(srcptr, offset, nullbitmap, nitems,
4224 typlen, typbyval, typalign);
4225 memcpy(destptr, srcptr, numbytes);
4230 * Copy nitems null-bitmap bits from source to destination
4232 * destbitmap: start of destination array's null bitmap (mustn't be NULL)
4233 * destoffset: 0-based linear element number of first dest element
4234 * srcbitmap: start of source array's null bitmap, or NULL if none
4235 * srcoffset: 0-based linear element number of first source element
4236 * nitems: number of bits to copy (>= 0)
4238 * If srcbitmap is NULL then we assume the source is all-non-NULL and
4239 * fill 1's into the destination bitmap. Note that only the specified
4240 * bits in the destination map are changed, not any before or after.
4242 * Note: this could certainly be optimized using standard bitblt methods.
4243 * However, it's not clear that the typical Postgres array has enough elements
4244 * to make it worth worrying too much. For the moment, KISS.
4247 array_bitmap_copy(bits8 *destbitmap, int destoffset,
4248 const bits8 *srcbitmap, int srcoffset,
4258 return; /* don't risk fetch off end of memory */
4259 destbitmap += destoffset / 8;
4260 destbitmask = 1 << (destoffset % 8);
4261 destbitval = *destbitmap;
4264 srcbitmap += srcoffset / 8;
4265 srcbitmask = 1 << (srcoffset % 8);
4266 srcbitval = *srcbitmap;
4267 while (nitems-- > 0)
4269 if (srcbitval & srcbitmask)
4270 destbitval |= destbitmask;
4272 destbitval &= ~destbitmask;
4274 if (destbitmask == 0x100)
4276 *destbitmap++ = destbitval;
4279 destbitval = *destbitmap;
4282 if (srcbitmask == 0x100)
4287 srcbitval = *srcbitmap;
4290 if (destbitmask != 1)
4291 *destbitmap = destbitval;
4295 while (nitems-- > 0)
4297 destbitval |= destbitmask;
4299 if (destbitmask == 0x100)
4301 *destbitmap++ = destbitval;
4304 destbitval = *destbitmap;
4307 if (destbitmask != 1)
4308 *destbitmap = destbitval;
4313 * Compute space needed for a slice of an array
4315 * We assume the caller has verified that the slice coordinates are valid.
4318 array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
4319 int ndim, int *dim, int *lb,
4321 int typlen, bool typbyval, char typalign)
4334 mda_get_range(ndim, span, st, endp);
4336 /* Pretty easy for fixed element length without nulls ... */
4337 if (typlen > 0 && !arraynullsptr)
4338 return ArrayGetNItems(ndim, span) * att_align_nominal(typlen, typalign);
4340 /* Else gotta do it the hard way */
4341 src_offset = ArrayGetOffset(ndim, dim, lb, st);
4342 ptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
4343 typlen, typbyval, typalign);
4344 mda_get_prod(ndim, dim, prod);
4345 mda_get_offset_values(ndim, dist, prod, span);
4346 for (i = 0; i < ndim; i++)
4353 ptr = array_seek(ptr, src_offset, arraynullsptr, dist[j],
4354 typlen, typbyval, typalign);
4355 src_offset += dist[j];
4357 if (!array_get_isnull(arraynullsptr, src_offset))
4359 inc = att_addlength_pointer(0, typlen, ptr);
4360 inc = att_align_nominal(inc, typalign);
4365 } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
4370 * Extract a slice of an array into consecutive elements in the destination
4373 * We assume the caller has verified that the slice coordinates are valid,
4374 * allocated enough storage for the result, and initialized the header
4378 array_extract_slice(ArrayType *newarray,
4383 bits8 *arraynullsptr,
4390 char *destdataptr = ARR_DATA_PTR(newarray);
4391 bits8 *destnullsptr = ARR_NULLBITMAP(newarray);
4403 src_offset = ArrayGetOffset(ndim, dim, lb, st);
4404 srcdataptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
4405 typlen, typbyval, typalign);
4406 mda_get_prod(ndim, dim, prod);
4407 mda_get_range(ndim, span, st, endp);
4408 mda_get_offset_values(ndim, dist, prod, span);
4409 for (i = 0; i < ndim; i++)
4417 /* skip unwanted elements */
4418 srcdataptr = array_seek(srcdataptr, src_offset, arraynullsptr,
4420 typlen, typbyval, typalign);
4421 src_offset += dist[j];
4423 inc = array_copy(destdataptr, 1,
4424 srcdataptr, src_offset, arraynullsptr,
4425 typlen, typbyval, typalign);
4427 array_bitmap_copy(destnullsptr, dest_offset,
4428 arraynullsptr, src_offset,
4434 } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
4438 * Insert a slice into an array.
4440 * ndim/dim[]/lb[] are dimensions of the original array. A new array with
4441 * those same dimensions is to be constructed. destArray must already
4442 * have been allocated and its header initialized.
4444 * st[]/endp[] identify the slice to be replaced. Elements within the slice
4445 * volume are taken from consecutive elements of the srcArray; elements
4446 * outside it are copied from origArray.
4448 * We assume the caller has verified that the slice coordinates are valid.
4451 array_insert_slice(ArrayType *destArray,
4452 ArrayType *origArray,
4453 ArrayType *srcArray,
4463 char *destPtr = ARR_DATA_PTR(destArray);
4464 char *origPtr = ARR_DATA_PTR(origArray);
4465 char *srcPtr = ARR_DATA_PTR(srcArray);
4466 bits8 *destBitmap = ARR_NULLBITMAP(destArray);
4467 bits8 *origBitmap = ARR_NULLBITMAP(origArray);
4468 bits8 *srcBitmap = ARR_NULLBITMAP(srcArray);
4469 int orignitems = ArrayGetNItems(ARR_NDIM(origArray),
4470 ARR_DIMS(origArray));
4482 dest_offset = ArrayGetOffset(ndim, dim, lb, st);
4483 /* copy items before the slice start */
4484 inc = array_copy(destPtr, dest_offset,
4485 origPtr, 0, origBitmap,
4486 typlen, typbyval, typalign);
4490 array_bitmap_copy(destBitmap, 0, origBitmap, 0, dest_offset);
4491 orig_offset = dest_offset;
4492 mda_get_prod(ndim, dim, prod);
4493 mda_get_range(ndim, span, st, endp);
4494 mda_get_offset_values(ndim, dist, prod, span);
4495 for (i = 0; i < ndim; i++)
4501 /* Copy/advance over elements between here and next part of slice */
4504 inc = array_copy(destPtr, dist[j],
4505 origPtr, orig_offset, origBitmap,
4506 typlen, typbyval, typalign);
4510 array_bitmap_copy(destBitmap, dest_offset,
4511 origBitmap, orig_offset,
4513 dest_offset += dist[j];
4514 orig_offset += dist[j];
4516 /* Copy new element at this slice position */
4517 inc = array_copy(destPtr, 1,
4518 srcPtr, src_offset, srcBitmap,
4519 typlen, typbyval, typalign);
4521 array_bitmap_copy(destBitmap, dest_offset,
4522 srcBitmap, src_offset,
4528 /* Advance over old element at this slice position */
4529 origPtr = array_seek(origPtr, orig_offset, origBitmap, 1,
4530 typlen, typbyval, typalign);
4532 } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
4534 /* don't miss any data at the end */
4535 array_copy(destPtr, orignitems - orig_offset,
4536 origPtr, orig_offset, origBitmap,
4537 typlen, typbyval, typalign);
4539 array_bitmap_copy(destBitmap, dest_offset,
4540 origBitmap, orig_offset,
4541 orignitems - orig_offset);
4545 * accumArrayResult - accumulate one (more) Datum for an array result
4547 * astate is working state (NULL on first call)
4548 * rcontext is where to keep working state
4551 accumArrayResult(ArrayBuildState *astate,
4552 Datum dvalue, bool disnull,
4554 MemoryContext rcontext)
4556 MemoryContext arr_context,
4561 /* First time through --- initialize */
4563 /* Make a temporary context to hold all the junk */
4564 arr_context = AllocSetContextCreate(rcontext,
4566 ALLOCSET_DEFAULT_MINSIZE,
4567 ALLOCSET_DEFAULT_INITSIZE,
4568 ALLOCSET_DEFAULT_MAXSIZE);
4569 oldcontext = MemoryContextSwitchTo(arr_context);
4570 astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
4571 astate->mcontext = arr_context;
4572 astate->alen = 64; /* arbitrary starting array size */
4573 astate->dvalues = (Datum *) palloc(astate->alen * sizeof(Datum));
4574 astate->dnulls = (bool *) palloc(astate->alen * sizeof(bool));
4576 astate->element_type = element_type;
4577 get_typlenbyvalalign(element_type,
4584 oldcontext = MemoryContextSwitchTo(astate->mcontext);
4585 Assert(astate->element_type == element_type);
4586 /* enlarge dvalues[]/dnulls[] if needed */
4587 if (astate->nelems >= astate->alen)
4590 astate->dvalues = (Datum *)
4591 repalloc(astate->dvalues, astate->alen * sizeof(Datum));
4592 astate->dnulls = (bool *)
4593 repalloc(astate->dnulls, astate->alen * sizeof(bool));
4598 * Ensure pass-by-ref stuff is copied into mcontext; and detoast it too if
4599 * it's varlena. (You might think that detoasting is not needed here
4600 * because construct_md_array can detoast the array elements later.
4601 * However, we must not let construct_md_array modify the ArrayBuildState
4602 * because that would mean array_agg_finalfn damages its input, which is
4603 * verboten. Also, this way frequently saves one copying step.)
4605 if (!disnull && !astate->typbyval)
4607 if (astate->typlen == -1)
4608 dvalue = PointerGetDatum(PG_DETOAST_DATUM_COPY(dvalue));
4610 dvalue = datumCopy(dvalue, astate->typbyval, astate->typlen);
4613 astate->dvalues[astate->nelems] = dvalue;
4614 astate->dnulls[astate->nelems] = disnull;
4617 MemoryContextSwitchTo(oldcontext);
4623 * makeArrayResult - produce 1-D final result of accumArrayResult
4625 * astate is working state (not NULL)
4626 * rcontext is where to construct result
4629 makeArrayResult(ArrayBuildState *astate,
4630 MemoryContext rcontext)
4635 dims[0] = astate->nelems;
4638 return makeMdArrayResult(astate, 1, dims, lbs, rcontext, true);
4642 * makeMdArrayResult - produce multi-D final result of accumArrayResult
4644 * beware: no check that specified dimensions match the number of values
4647 * astate is working state (not NULL)
4648 * rcontext is where to construct result
4649 * release is true if okay to release working state
4652 makeMdArrayResult(ArrayBuildState *astate,
4656 MemoryContext rcontext,
4660 MemoryContext oldcontext;
4662 /* Build the final array result in rcontext */
4663 oldcontext = MemoryContextSwitchTo(rcontext);
4665 result = construct_md_array(astate->dvalues,
4670 astate->element_type,
4675 MemoryContextSwitchTo(oldcontext);
4677 /* Clean up all the junk */
4679 MemoryContextDelete(astate->mcontext);
4681 return PointerGetDatum(result);
4685 array_larger(PG_FUNCTION_ARGS)
4691 v1 = PG_GETARG_ARRAYTYPE_P(0);
4692 v2 = PG_GETARG_ARRAYTYPE_P(1);
4694 result = ((array_cmp(fcinfo) > 0) ? v1 : v2);
4696 PG_RETURN_ARRAYTYPE_P(result);
4700 array_smaller(PG_FUNCTION_ARGS)
4706 v1 = PG_GETARG_ARRAYTYPE_P(0);
4707 v2 = PG_GETARG_ARRAYTYPE_P(1);
4709 result = ((array_cmp(fcinfo) < 0) ? v1 : v2);
4711 PG_RETURN_ARRAYTYPE_P(result);
4715 typedef struct generate_subscripts_fctx
4720 } generate_subscripts_fctx;
4723 * generate_subscripts(array anyarray, dim int [, reverse bool])
4724 * Returns all subscripts of the array for any dimension
4727 generate_subscripts(PG_FUNCTION_ARGS)
4729 FuncCallContext *funcctx;
4730 MemoryContext oldcontext;
4731 generate_subscripts_fctx *fctx;
4733 /* stuff done only on the first call of the function */
4734 if (SRF_IS_FIRSTCALL())
4736 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
4737 int reqdim = PG_GETARG_INT32(1);
4741 /* create a function context for cross-call persistence */
4742 funcctx = SRF_FIRSTCALL_INIT();
4744 /* Sanity check: does it look like an array at all? */
4745 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
4746 SRF_RETURN_DONE(funcctx);
4748 /* Sanity check: was the requested dim valid */
4749 if (reqdim <= 0 || reqdim > ARR_NDIM(v))
4750 SRF_RETURN_DONE(funcctx);
4753 * switch to memory context appropriate for multiple function calls
4755 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
4756 fctx = (generate_subscripts_fctx *) palloc(sizeof(generate_subscripts_fctx));
4761 fctx->lower = lb[reqdim - 1];
4762 fctx->upper = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
4763 fctx->reverse = (PG_NARGS() < 3) ? false : PG_GETARG_BOOL(2);
4765 funcctx->user_fctx = fctx;
4767 MemoryContextSwitchTo(oldcontext);
4770 funcctx = SRF_PERCALL_SETUP();
4772 fctx = funcctx->user_fctx;
4774 if (fctx->lower <= fctx->upper)
4777 SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->lower++));
4779 SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->upper--));
4782 /* done when there are no more elements left */
4783 SRF_RETURN_DONE(funcctx);
4787 * generate_subscripts_nodir
4788 * Implements the 2-argument version of generate_subscripts
4791 generate_subscripts_nodir(PG_FUNCTION_ARGS)
4793 /* just call the other one -- it can handle both cases */
4794 return generate_subscripts(fcinfo);
4798 * array_fill_with_lower_bounds
4799 * Create and fill array with defined lower bounds.
4802 array_fill_with_lower_bounds(PG_FUNCTION_ARGS)
4811 if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
4813 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
4814 errmsg("dimension array or low bound array cannot be null")));
4816 dims = PG_GETARG_ARRAYTYPE_P(1);
4817 lbs = PG_GETARG_ARRAYTYPE_P(2);
4819 if (!PG_ARGISNULL(0))
4821 value = PG_GETARG_DATUM(0);
4830 elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
4831 if (!OidIsValid(elmtype))
4832 elog(ERROR, "could not determine data type of input");
4834 result = array_fill_internal(dims, lbs, value, isnull, elmtype, fcinfo);
4835 PG_RETURN_ARRAYTYPE_P(result);
4840 * Create and fill array with default lower bounds.
4843 array_fill(PG_FUNCTION_ARGS)
4851 if (PG_ARGISNULL(1))
4853 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
4854 errmsg("dimension array or low bound array cannot be null")));
4856 dims = PG_GETARG_ARRAYTYPE_P(1);
4858 if (!PG_ARGISNULL(0))
4860 value = PG_GETARG_DATUM(0);
4869 elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
4870 if (!OidIsValid(elmtype))
4871 elog(ERROR, "could not determine data type of input");
4873 result = array_fill_internal(dims, NULL, value, isnull, elmtype, fcinfo);
4874 PG_RETURN_ARRAYTYPE_P(result);
4878 create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
4879 Oid elmtype, int dataoffset)
4883 result = (ArrayType *) palloc0(nbytes);
4884 SET_VARSIZE(result, nbytes);
4885 result->ndim = ndims;
4886 result->dataoffset = dataoffset;
4887 result->elemtype = elmtype;
4888 memcpy(ARR_DIMS(result), dimv, ndims * sizeof(int));
4889 memcpy(ARR_LBOUND(result), lbsv, ndims * sizeof(int));
4895 array_fill_internal(ArrayType *dims, ArrayType *lbs,
4896 Datum value, bool isnull, Oid elmtype,
4897 FunctionCallInfo fcinfo)
4908 ArrayMetaState *my_extra;
4913 if (ARR_NDIM(dims) != 1)
4915 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4916 errmsg("wrong number of array subscripts"),
4917 errdetail("Dimension array must be one dimensional.")));
4919 if (ARR_LBOUND(dims)[0] != 1)
4921 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4922 errmsg("wrong range of array subscripts"),
4923 errdetail("Lower bound of dimension array must be one.")));
4925 if (array_contains_nulls(dims))
4927 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
4928 errmsg("dimension values cannot be null")));
4930 dimv = (int *) ARR_DATA_PTR(dims);
4931 ndims = ARR_DIMS(dims)[0];
4933 if (ndims < 0) /* we do allow zero-dimension arrays */
4935 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4936 errmsg("invalid number of dimensions: %d", ndims)));
4939 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
4940 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
4945 if (ARR_NDIM(lbs) != 1)
4947 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4948 errmsg("wrong number of array subscripts"),
4949 errdetail("Dimension array must be one dimensional.")));
4951 if (ARR_LBOUND(lbs)[0] != 1)
4953 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4954 errmsg("wrong range of array subscripts"),
4955 errdetail("Lower bound of dimension array must be one.")));
4957 if (array_contains_nulls(lbs))
4959 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
4960 errmsg("dimension values cannot be null")));
4962 if (ARR_DIMS(lbs)[0] != ndims)
4964 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4965 errmsg("wrong number of array subscripts"),
4966 errdetail("Low bound array has different size than dimensions array.")));
4968 lbsv = (int *) ARR_DATA_PTR(lbs);
4974 for (i = 0; i < MAXDIM; i++)
4980 /* fast track for empty array */
4982 return construct_empty_array(elmtype);
4984 nitems = ArrayGetNItems(ndims, dimv);
4987 * We arrange to look up info about element type only once per series of
4988 * calls, assuming the element type doesn't change underneath us.
4990 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
4991 if (my_extra == NULL)
4993 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
4994 sizeof(ArrayMetaState));
4995 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
4996 my_extra->element_type = InvalidOid;
4999 if (my_extra->element_type != elmtype)
5001 /* Get info about element type */
5002 get_typlenbyvalalign(elmtype,
5004 &my_extra->typbyval,
5005 &my_extra->typalign);
5006 my_extra->element_type = elmtype;
5009 elmlen = my_extra->typlen;
5010 elmbyval = my_extra->typbyval;
5011 elmalign = my_extra->typalign;
5013 /* compute required space */
5021 /* make sure data is not toasted */
5023 value = PointerGetDatum(PG_DETOAST_DATUM(value));
5025 nbytes = att_addlength_datum(0, elmlen, value);
5026 nbytes = att_align_nominal(nbytes, elmalign);
5029 totbytes = nbytes * nitems;
5031 /* check for overflow of multiplication or total request */
5032 if (totbytes / nbytes != nitems ||
5033 !AllocSizeIsValid(totbytes))
5035 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
5036 errmsg("array size exceeds the maximum allowed (%d)",
5037 (int) MaxAllocSize)));
5040 * This addition can't overflow, but it might cause us to go past
5041 * MaxAllocSize. We leave it to palloc to complain in that case.
5043 totbytes += ARR_OVERHEAD_NONULLS(ndims);
5045 result = create_array_envelope(ndims, dimv, lbsv, totbytes,
5048 p = ARR_DATA_PTR(result);
5049 for (i = 0; i < nitems; i++)
5050 p += ArrayCastAndSet(value, elmlen, elmbyval, elmalign, p);
5057 dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
5058 nbytes = dataoffset;
5060 result = create_array_envelope(ndims, dimv, lbsv, nbytes,
5061 elmtype, dataoffset);
5063 /* create_array_envelope already zeroed the bitmap, so we're done */
5074 array_unnest(PG_FUNCTION_ARGS)
5081 char *elemdataptr; /* this moves with nextelem */
5082 bits8 *arraynullsptr; /* this does not */
5086 } array_unnest_fctx;
5088 FuncCallContext *funcctx;
5089 array_unnest_fctx *fctx;
5090 MemoryContext oldcontext;
5092 /* stuff done only on the first call of the function */
5093 if (SRF_IS_FIRSTCALL())
5097 /* create a function context for cross-call persistence */
5098 funcctx = SRF_FIRSTCALL_INIT();
5101 * switch to memory context appropriate for multiple function calls
5103 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
5106 * Get the array value and detoast if needed. We can't do this
5107 * earlier because if we have to detoast, we want the detoasted copy
5108 * to be in multi_call_memory_ctx, so it will go away when we're done
5109 * and not before. (If no detoast happens, we assume the originally
5110 * passed array will stick around till then.)
5112 arr = PG_GETARG_ARRAYTYPE_P(0);
5114 /* allocate memory for user context */
5115 fctx = (array_unnest_fctx *) palloc(sizeof(array_unnest_fctx));
5117 /* initialize state */
5120 fctx->numelems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
5122 fctx->elemdataptr = ARR_DATA_PTR(arr);
5123 fctx->arraynullsptr = ARR_NULLBITMAP(arr);
5125 get_typlenbyvalalign(ARR_ELEMTYPE(arr),
5130 funcctx->user_fctx = fctx;
5131 MemoryContextSwitchTo(oldcontext);
5134 /* stuff done on every call of the function */
5135 funcctx = SRF_PERCALL_SETUP();
5136 fctx = funcctx->user_fctx;
5138 if (fctx->nextelem < fctx->numelems)
5140 int offset = fctx->nextelem++;
5144 * Check for NULL array element
5146 if (array_get_isnull(fctx->arraynullsptr, offset))
5148 fcinfo->isnull = true;
5150 /* elemdataptr does not move */
5155 * OK, get the element
5157 char *ptr = fctx->elemdataptr;
5159 fcinfo->isnull = false;
5160 elem = ArrayCast(ptr, fctx->elmbyval, fctx->elmlen);
5163 * Advance elemdataptr over it
5165 ptr = att_addlength_pointer(ptr, fctx->elmlen, ptr);
5166 ptr = (char *) att_align_nominal(ptr, fctx->elmalign);
5167 fctx->elemdataptr = ptr;
5170 SRF_RETURN_NEXT(funcctx, elem);
5174 /* do when there is no more left */
5175 SRF_RETURN_DONE(funcctx);