1 /*-------------------------------------------------------------------------
4 * Support functions for arrays.
6 * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/backend/utils/adt/arrayfuncs.c
13 *-------------------------------------------------------------------------
19 #include "access/htup_details.h"
21 #include "libpq/pqformat.h"
22 #include "utils/array.h"
23 #include "utils/builtins.h"
24 #include "utils/datum.h"
25 #include "utils/lsyscache.h"
26 #include "utils/memutils.h"
27 #include "utils/typcache.h"
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);
128 static ArrayType *array_replace_internal(ArrayType *array,
129 Datum search, bool search_isnull,
130 Datum replace, bool replace_isnull,
131 bool remove, Oid collation,
132 FunctionCallInfo fcinfo);
137 * converts an array from the external format in "string" to
138 * its internal format.
141 * the internal representation of the input array
144 array_in(PG_FUNCTION_ARGS)
146 char *string = PG_GETARG_CSTRING(0); /* external form */
147 Oid element_type = PG_GETARG_OID(1); /* type of an array
149 int32 typmod = PG_GETARG_INT32(2); /* typmod for array elements */
168 ArrayMetaState *my_extra;
171 * We arrange to look up info about element type, including its input
172 * conversion proc, only once per series of calls, assuming the element
173 * type doesn't change underneath us.
175 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
176 if (my_extra == NULL)
178 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
179 sizeof(ArrayMetaState));
180 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
181 my_extra->element_type = ~element_type;
184 if (my_extra->element_type != element_type)
187 * Get info about element type, including its input conversion proc
189 get_type_io_data(element_type, IOFunc_input,
190 &my_extra->typlen, &my_extra->typbyval,
191 &my_extra->typalign, &my_extra->typdelim,
192 &my_extra->typioparam, &my_extra->typiofunc);
193 fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
194 fcinfo->flinfo->fn_mcxt);
195 my_extra->element_type = element_type;
197 typlen = my_extra->typlen;
198 typbyval = my_extra->typbyval;
199 typalign = my_extra->typalign;
200 typdelim = my_extra->typdelim;
201 typioparam = my_extra->typioparam;
203 /* Make a modifiable copy of the input */
204 string_save = pstrdup(string);
207 * If the input string starts with dimension info, read and use that.
208 * Otherwise, we require the input to be in curly-brace style, and we
209 * prescan the input to determine dimensions.
211 * Dimension info takes the form of one or more [n] or [m:n] items. The
212 * outer loop iterates once per dimension item.
222 * Note: we currently allow whitespace between, but not within,
225 while (array_isspace(*p))
228 break; /* no more dimension items */
232 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
233 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
236 for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++);
237 if (q == p) /* no digits? */
239 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
240 errmsg("missing dimension value")));
246 lBound[ndim] = atoi(p);
248 for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++);
249 if (q == p) /* no digits? */
251 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
252 errmsg("missing dimension value")));
261 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
262 errmsg("missing \"]\" in array dimensions")));
267 if (ub < lBound[ndim])
269 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
270 errmsg("upper bound cannot be less than lower bound")));
272 dim[ndim] = ub - lBound[ndim] + 1;
278 /* No array dimensions, so intuit dimensions from brace structure */
281 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
282 errmsg("array value must start with \"{\" or dimension information")));
283 ndim = ArrayCount(p, dim, typdelim);
284 for (i = 0; i < ndim; i++)
292 /* If array dimensions are given, expect '=' operator */
293 if (strncmp(p, ASSGN, strlen(ASSGN)) != 0)
295 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
296 errmsg("missing assignment operator")));
298 while (array_isspace(*p))
302 * intuit dimensions from brace structure -- it better match what we
307 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
308 errmsg("array value must start with \"{\" or dimension information")));
309 ndim_braces = ArrayCount(p, dim_braces, typdelim);
310 if (ndim_braces != ndim)
312 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
313 errmsg("array dimensions incompatible with array literal")));
314 for (i = 0; i < ndim; ++i)
316 if (dim[i] != dim_braces[i])
318 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
319 errmsg("array dimensions incompatible with array literal")));
324 printf("array_in- ndim %d (", ndim);
325 for (i = 0; i < ndim; i++)
327 printf(" %d", dim[i]);
329 printf(") for %s\n", string);
332 /* This checks for overflow of the array dimensions */
333 nitems = ArrayGetNItems(ndim, dim);
336 PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
338 dataPtr = (Datum *) palloc(nitems * sizeof(Datum));
339 nullsPtr = (bool *) palloc(nitems * sizeof(bool));
340 ReadArrayStr(p, string,
342 &my_extra->proc, typioparam, typmod,
344 typlen, typbyval, typalign,
349 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
350 nbytes += dataoffset;
354 dataoffset = 0; /* marker for no null bitmap */
355 nbytes += ARR_OVERHEAD_NONULLS(ndim);
357 retval = (ArrayType *) palloc0(nbytes);
358 SET_VARSIZE(retval, nbytes);
360 retval->dataoffset = dataoffset;
363 * This comes from the array's pg_type.typelem (which points to the base
364 * data type's pg_type.oid) and stores system oids in user tables. This
365 * oid must be preserved by binary upgrades.
367 retval->elemtype = element_type;
368 memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
369 memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
372 dataPtr, nullsPtr, nitems,
373 typlen, typbyval, typalign,
380 PG_RETURN_ARRAYTYPE_P(retval);
384 * array_isspace() --- a non-locale-dependent isspace()
386 * We used to use isspace() for parsing array values, but that has
387 * undesirable results: an array value might be silently interpreted
388 * differently depending on the locale setting. Now we just hard-wire
389 * the traditional ASCII definition of isspace().
392 array_isspace(char ch)
406 * Determines the dimensions for an array string.
408 * Returns number of dimensions as function result. The axis lengths are
409 * returned in dim[], which must be of size MAXDIM.
412 ArrayCount(const char *str, int *dim, char typdelim)
420 bool in_quotes = false;
421 bool eoArray = false;
422 bool empty_array = true;
424 ArrayParseState parse_state = ARRAY_NO_LEVEL;
426 for (i = 0; i < MAXDIM; ++i)
428 temp[i] = dim[i] = nelems_last[i] = 0;
435 bool itemdone = false;
439 if (parse_state == ARRAY_ELEM_STARTED ||
440 parse_state == ARRAY_QUOTED_ELEM_STARTED)
446 /* Signal a premature end of the string */
448 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
449 errmsg("malformed array literal: \"%s\"", str)));
454 * An escape must be after a level start, after an element
455 * start, or after an element delimiter. In any case we
456 * now must be past an element start.
458 if (parse_state != ARRAY_LEVEL_STARTED &&
459 parse_state != ARRAY_ELEM_STARTED &&
460 parse_state != ARRAY_QUOTED_ELEM_STARTED &&
461 parse_state != ARRAY_ELEM_DELIMITED)
463 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
464 errmsg("malformed array literal: \"%s\"", str)));
465 if (parse_state != ARRAY_QUOTED_ELEM_STARTED)
466 parse_state = ARRAY_ELEM_STARTED;
467 /* skip the escaped character */
472 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
473 errmsg("malformed array literal: \"%s\"", str)));
478 * A quote must be after a level start, after a quoted
479 * element start, or after an element delimiter. In any
480 * case we now must be past an element start.
482 if (parse_state != ARRAY_LEVEL_STARTED &&
483 parse_state != ARRAY_QUOTED_ELEM_STARTED &&
484 parse_state != ARRAY_ELEM_DELIMITED)
486 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
487 errmsg("malformed array literal: \"%s\"", str)));
488 in_quotes = !in_quotes;
490 parse_state = ARRAY_QUOTED_ELEM_STARTED;
492 parse_state = ARRAY_QUOTED_ELEM_COMPLETED;
498 * A left brace can occur if no nesting has occurred
499 * yet, after a level start, or after a level
502 if (parse_state != ARRAY_NO_LEVEL &&
503 parse_state != ARRAY_LEVEL_STARTED &&
504 parse_state != ARRAY_LEVEL_DELIMITED)
506 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
507 errmsg("malformed array literal: \"%s\"", str)));
508 parse_state = ARRAY_LEVEL_STARTED;
509 if (nest_level >= MAXDIM)
511 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
512 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
513 nest_level + 1, MAXDIM)));
514 temp[nest_level] = 0;
516 if (ndim < nest_level)
524 * A right brace can occur after an element start, an
525 * element completion, a quoted element completion, or
526 * a level completion.
528 if (parse_state != ARRAY_ELEM_STARTED &&
529 parse_state != ARRAY_ELEM_COMPLETED &&
530 parse_state != ARRAY_QUOTED_ELEM_COMPLETED &&
531 parse_state != ARRAY_LEVEL_COMPLETED &&
532 !(nest_level == 1 && parse_state == ARRAY_LEVEL_STARTED))
534 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
535 errmsg("malformed array literal: \"%s\"", str)));
536 parse_state = ARRAY_LEVEL_COMPLETED;
539 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
540 errmsg("malformed array literal: \"%s\"", str)));
543 if (nelems_last[nest_level] != 0 &&
544 nelems[nest_level] != nelems_last[nest_level])
546 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
547 errmsg("multidimensional arrays must have "
548 "array expressions with matching "
550 nelems_last[nest_level] = nelems[nest_level];
551 nelems[nest_level] = 1;
553 eoArray = itemdone = true;
557 * We don't set itemdone here; see comments in
560 temp[nest_level - 1]++;
567 if (*ptr == typdelim)
570 * Delimiters can occur after an element start, an
571 * element completion, a quoted element
572 * completion, or a level completion.
574 if (parse_state != ARRAY_ELEM_STARTED &&
575 parse_state != ARRAY_ELEM_COMPLETED &&
576 parse_state != ARRAY_QUOTED_ELEM_COMPLETED &&
577 parse_state != ARRAY_LEVEL_COMPLETED)
579 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
580 errmsg("malformed array literal: \"%s\"", str)));
581 if (parse_state == ARRAY_LEVEL_COMPLETED)
582 parse_state = ARRAY_LEVEL_DELIMITED;
584 parse_state = ARRAY_ELEM_DELIMITED;
586 nelems[nest_level - 1]++;
588 else if (!array_isspace(*ptr))
591 * Other non-space characters must be after a
592 * level start, after an element start, or after
593 * an element delimiter. In any case we now must
594 * be past an element start.
596 if (parse_state != ARRAY_LEVEL_STARTED &&
597 parse_state != ARRAY_ELEM_STARTED &&
598 parse_state != ARRAY_ELEM_DELIMITED)
600 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
601 errmsg("malformed array literal: \"%s\"", str)));
602 parse_state = ARRAY_ELEM_STARTED;
614 /* only whitespace is allowed after the closing brace */
617 if (!array_isspace(*ptr++))
619 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
620 errmsg("malformed array literal: \"%s\"", str)));
623 /* special case for an empty array */
627 for (i = 0; i < ndim; ++i)
635 * parses the array string pointed to by "arrayStr" and converts the values
636 * to internal format. Unspecified elements are initialized to nulls.
637 * The array dimensions must already have been determined.
640 * arrayStr: the string to parse.
641 * CAUTION: the contents of "arrayStr" will be modified!
642 * origStr: the unmodified input string, used only in error messages.
643 * nitems: total number of array elements, as already determined.
644 * ndim: number of array dimensions
645 * dim[]: array axis lengths
646 * inputproc: type-specific input procedure for element datatype.
647 * typioparam, typmod: auxiliary values to pass to inputproc.
648 * typdelim: the value delimiter (type-specific).
649 * typlen, typbyval, typalign: storage parameters of element datatype.
652 * values[]: filled with converted data values.
653 * nulls[]: filled with is-null markers.
654 * *hasnulls: set TRUE iff there are any null elements.
655 * *nbytes: set to total size of data area needed (including alignment
656 * padding but not including array header overhead).
658 * Note that values[] and nulls[] are allocated by the caller, and must have
662 ReadArrayStr(char *arrayStr,
682 bool in_quotes = false;
683 bool eoArray = false;
689 mda_get_prod(ndim, dim, prod);
690 MemSet(indx, 0, sizeof(indx));
692 /* Initialize is-null markers to true */
693 memset(nulls, true, nitems * sizeof(bool));
696 * We have to remove " and \ characters to create a clean item value to
697 * pass to the datatype input routine. We overwrite each item value
698 * in-place within arrayStr to do this. srcptr is the current scan point,
699 * and dstptr is where we are copying to.
701 * We also want to suppress leading and trailing unquoted whitespace. We
702 * use the leadingspace flag to suppress leading space. Trailing space is
703 * tracked by using dstendptr to point to the last significant output
706 * The error checking in this routine is mostly pro-forma, since we expect
707 * that ArrayCount() already validated the string.
712 bool itemdone = false;
713 bool leadingspace = true;
714 bool hasquoting = false;
720 itemstart = dstptr = dstendptr = srcptr;
727 /* Signal a premature end of the string */
729 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
730 errmsg("malformed array literal: \"%s\"",
734 /* Skip backslash, copy next character as-is. */
738 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
739 errmsg("malformed array literal: \"%s\"",
741 *dstptr++ = *srcptr++;
742 /* Treat the escaped character as non-whitespace */
743 leadingspace = false;
745 hasquoting = true; /* can't be a NULL marker */
748 in_quotes = !in_quotes;
750 leadingspace = false;
754 * Advance dstendptr when we exit in_quotes; this
755 * saves having to do it in all the other in_quotes
760 hasquoting = true; /* can't be a NULL marker */
766 if (nest_level >= ndim)
768 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
769 errmsg("malformed array literal: \"%s\"",
772 indx[nest_level - 1] = 0;
776 *dstptr++ = *srcptr++;
783 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
784 errmsg("malformed array literal: \"%s\"",
787 i = ArrayGetOffset0(ndim, indx, prod);
788 indx[nest_level - 1] = 0;
791 eoArray = itemdone = true;
793 indx[nest_level - 1]++;
797 *dstptr++ = *srcptr++;
801 *dstptr++ = *srcptr++;
802 else if (*srcptr == typdelim)
805 i = ArrayGetOffset0(ndim, indx, prod);
810 else if (array_isspace(*srcptr))
813 * If leading space, drop it immediately. Else, copy
814 * but don't advance dstendptr.
819 *dstptr++ = *srcptr++;
823 *dstptr++ = *srcptr++;
824 leadingspace = false;
831 Assert(dstptr < srcptr);
834 if (i < 0 || i >= nitems)
836 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
837 errmsg("malformed array literal: \"%s\"",
840 if (Array_nulls && !hasquoting &&
841 pg_strcasecmp(itemstart, "NULL") == 0)
843 /* it's a NULL item */
844 values[i] = InputFunctionCall(inputproc, NULL,
850 values[i] = InputFunctionCall(inputproc, itemstart,
857 * Check for nulls, compute total data space needed
861 for (i = 0; i < nitems; i++)
867 /* let's just make sure data is not toasted */
869 values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
870 totbytes = att_addlength_datum(totbytes, typlen, values[i]);
871 totbytes = att_align_nominal(totbytes, typalign);
872 /* check for overflow of total request */
873 if (!AllocSizeIsValid(totbytes))
875 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
876 errmsg("array size exceeds the maximum allowed (%d)",
877 (int) MaxAllocSize)));
886 * Copy data into an array object from a temporary array of Datums.
888 * array: array object (with header fields already filled in)
889 * values: array of Datums to be copied
890 * nulls: array of is-null flags (can be NULL if no nulls)
891 * nitems: number of Datums to be copied
892 * typbyval, typlen, typalign: info about element datatype
893 * freedata: if TRUE and element type is pass-by-ref, pfree data values
894 * referenced by Datums after copying them.
896 * If the input data is of varlena type, the caller must have ensured that
897 * the values are not toasted. (Doing it here doesn't work since the
898 * caller has already allocated space for the array...)
901 CopyArrayEls(ArrayType *array,
910 char *p = ARR_DATA_PTR(array);
911 bits8 *bitmap = ARR_NULLBITMAP(array);
919 for (i = 0; i < nitems; i++)
921 if (nulls && nulls[i])
923 if (!bitmap) /* shouldn't happen */
924 elog(ERROR, "null array element where not supported");
925 /* bitmap bit stays 0 */
930 p += ArrayCastAndSet(values[i], typlen, typbyval, typalign, p);
932 pfree(DatumGetPointer(values[i]));
937 if (bitmask == 0x100)
946 if (bitmap && bitmask != 1)
952 * takes the internal representation of an array and returns a string
953 * containing the array in its external format.
956 array_out(PG_FUNCTION_ARGS)
958 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
959 Oid element_type = ARR_ELEMTYPE(v);
968 dims_str[(MAXDIM * 33) + 2];
971 * 33 per dim since we assume 15 digits per number + ':' +'[]'
973 * +2 allows for assignment operator + trailing null
988 ArrayMetaState *my_extra;
991 * We arrange to look up info about element type, including its output
992 * conversion proc, only once per series of calls, assuming the element
993 * type doesn't change underneath us.
995 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
996 if (my_extra == NULL)
998 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
999 sizeof(ArrayMetaState));
1000 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1001 my_extra->element_type = ~element_type;
1004 if (my_extra->element_type != element_type)
1007 * Get info about element type, including its output conversion proc
1009 get_type_io_data(element_type, IOFunc_output,
1010 &my_extra->typlen, &my_extra->typbyval,
1011 &my_extra->typalign, &my_extra->typdelim,
1012 &my_extra->typioparam, &my_extra->typiofunc);
1013 fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1014 fcinfo->flinfo->fn_mcxt);
1015 my_extra->element_type = element_type;
1017 typlen = my_extra->typlen;
1018 typbyval = my_extra->typbyval;
1019 typalign = my_extra->typalign;
1020 typdelim = my_extra->typdelim;
1025 nitems = ArrayGetNItems(ndim, dims);
1029 retval = pstrdup("{}");
1030 PG_RETURN_CSTRING(retval);
1034 * we will need to add explicit dimensions if any dimension has a lower
1035 * bound other than one
1037 for (i = 0; i < ndim; i++)
1047 * Convert all values to string form, count total space needed (including
1048 * any overhead such as escaping backslashes), and detect whether each
1049 * item needs double quotes.
1051 values = (char **) palloc(nitems * sizeof(char *));
1052 needquotes = (bool *) palloc(nitems * sizeof(bool));
1053 overall_length = 1; /* don't forget to count \0 at end. */
1055 p = ARR_DATA_PTR(v);
1056 bitmap = ARR_NULLBITMAP(v);
1059 for (i = 0; i < nitems; i++)
1063 /* Get source element, checking for NULL */
1064 if (bitmap && (*bitmap & bitmask) == 0)
1066 values[i] = pstrdup("NULL");
1067 overall_length += 4;
1074 itemvalue = fetch_att(p, typbyval, typlen);
1075 values[i] = OutputFunctionCall(&my_extra->proc, itemvalue);
1076 p = att_addlength_pointer(p, typlen, p);
1077 p = (char *) att_align_nominal(p, typalign);
1079 /* count data plus backslashes; detect chars needing quotes */
1080 if (values[i][0] == '\0')
1081 needquote = true; /* force quotes for empty string */
1082 else if (pg_strcasecmp(values[i], "NULL") == 0)
1083 needquote = true; /* force quotes for literal NULL */
1087 for (tmp = values[i]; *tmp != '\0'; tmp++)
1091 overall_length += 1;
1092 if (ch == '"' || ch == '\\')
1095 overall_length += 1;
1097 else if (ch == '{' || ch == '}' || ch == typdelim ||
1103 needquotes[i] = needquote;
1105 /* Count the pair of double quotes, if needed */
1107 overall_length += 2;
1109 overall_length += 1;
1111 /* advance bitmap pointer if any */
1115 if (bitmask == 0x100)
1124 * count total number of curly braces in output string
1126 for (i = j = 0, k = 1; i < ndim; i++)
1127 k *= dims[i], j += k;
1131 /* add explicit dimensions if required */
1134 char *ptr = dims_str;
1136 for (i = 0; i < ndim; i++)
1138 sprintf(ptr, "[%d:%d]", lb[i], lb[i] + dims[i] - 1);
1145 retval = (char *) palloc(strlen(dims_str) + overall_length + 2 * j);
1148 #define APPENDSTR(str) (strcpy(p, (str)), p += strlen(p))
1149 #define APPENDCHAR(ch) (*p++ = (ch), *p = '\0')
1152 APPENDSTR(dims_str);
1154 for (i = 0; i < ndim; i++)
1160 for (i = j; i < ndim - 1; i++)
1166 for (tmp = values[k]; *tmp; tmp++)
1170 if (ch == '"' || ch == '\\')
1178 APPENDSTR(values[k]);
1181 for (i = ndim - 1; i >= 0; i--)
1183 indx[i] = (indx[i] + 1) % dims[i];
1186 APPENDCHAR(typdelim);
1201 PG_RETURN_CSTRING(retval);
1206 * converts an array from the external binary format to
1207 * its internal format.
1210 * the internal representation of the input array
1213 array_recv(PG_FUNCTION_ARGS)
1215 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
1216 Oid spec_element_type = PG_GETARG_OID(1); /* type of an array
1218 int32 typmod = PG_GETARG_INT32(2); /* typmod for array elements */
1236 ArrayMetaState *my_extra;
1238 /* Get the array header information */
1239 ndim = pq_getmsgint(buf, 4);
1240 if (ndim < 0) /* we do allow zero-dimension arrays */
1242 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1243 errmsg("invalid number of dimensions: %d", ndim)));
1246 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1247 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
1250 flags = pq_getmsgint(buf, 4);
1251 if (flags != 0 && flags != 1)
1253 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1254 errmsg("invalid array flags")));
1256 element_type = pq_getmsgint(buf, sizeof(Oid));
1257 if (element_type != spec_element_type)
1259 /* XXX Can we allow taking the input element type in any cases? */
1261 (errcode(ERRCODE_DATATYPE_MISMATCH),
1262 errmsg("wrong element type")));
1265 for (i = 0; i < ndim; i++)
1267 dim[i] = pq_getmsgint(buf, 4);
1268 lBound[i] = pq_getmsgint(buf, 4);
1271 * Check overflow of upper bound. (ArrayNItems() below checks that
1276 int ub = lBound[i] + dim[i] - 1;
1280 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
1281 errmsg("integer out of range")));
1285 /* This checks for overflow of array dimensions */
1286 nitems = ArrayGetNItems(ndim, dim);
1289 * We arrange to look up info about element type, including its receive
1290 * conversion proc, only once per series of calls, assuming the element
1291 * type doesn't change underneath us.
1293 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1294 if (my_extra == NULL)
1296 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1297 sizeof(ArrayMetaState));
1298 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1299 my_extra->element_type = ~element_type;
1302 if (my_extra->element_type != element_type)
1304 /* Get info about element type, including its receive proc */
1305 get_type_io_data(element_type, IOFunc_receive,
1306 &my_extra->typlen, &my_extra->typbyval,
1307 &my_extra->typalign, &my_extra->typdelim,
1308 &my_extra->typioparam, &my_extra->typiofunc);
1309 if (!OidIsValid(my_extra->typiofunc))
1311 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1312 errmsg("no binary input function available for type %s",
1313 format_type_be(element_type))));
1314 fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1315 fcinfo->flinfo->fn_mcxt);
1316 my_extra->element_type = element_type;
1321 /* Return empty array ... but not till we've validated element_type */
1322 PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
1325 typlen = my_extra->typlen;
1326 typbyval = my_extra->typbyval;
1327 typalign = my_extra->typalign;
1328 typioparam = my_extra->typioparam;
1330 dataPtr = (Datum *) palloc(nitems * sizeof(Datum));
1331 nullsPtr = (bool *) palloc(nitems * sizeof(bool));
1332 ReadArrayBinary(buf, nitems,
1333 &my_extra->proc, typioparam, typmod,
1334 typlen, typbyval, typalign,
1336 &hasnulls, &nbytes);
1339 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
1340 nbytes += dataoffset;
1344 dataoffset = 0; /* marker for no null bitmap */
1345 nbytes += ARR_OVERHEAD_NONULLS(ndim);
1347 retval = (ArrayType *) palloc0(nbytes);
1348 SET_VARSIZE(retval, nbytes);
1349 retval->ndim = ndim;
1350 retval->dataoffset = dataoffset;
1351 retval->elemtype = element_type;
1352 memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
1353 memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
1355 CopyArrayEls(retval,
1356 dataPtr, nullsPtr, nitems,
1357 typlen, typbyval, typalign,
1363 PG_RETURN_ARRAYTYPE_P(retval);
1368 * collect the data elements of an array being read in binary style.
1371 * buf: the data buffer to read from.
1372 * nitems: total number of array elements (already read).
1373 * receiveproc: type-specific receive procedure for element datatype.
1374 * typioparam, typmod: auxiliary values to pass to receiveproc.
1375 * typlen, typbyval, typalign: storage parameters of element datatype.
1378 * values[]: filled with converted data values.
1379 * nulls[]: filled with is-null markers.
1380 * *hasnulls: set TRUE iff there are any null elements.
1381 * *nbytes: set to total size of data area needed (including alignment
1382 * padding but not including array header overhead).
1384 * Note that values[] and nulls[] are allocated by the caller, and must have
1388 ReadArrayBinary(StringInfo buf,
1390 FmgrInfo *receiveproc,
1405 for (i = 0; i < nitems; i++)
1408 StringInfoData elem_buf;
1411 /* Get and check the item length */
1412 itemlen = pq_getmsgint(buf, 4);
1413 if (itemlen < -1 || itemlen > (buf->len - buf->cursor))
1415 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1416 errmsg("insufficient data left in message")));
1420 /* -1 length means NULL */
1421 values[i] = ReceiveFunctionCall(receiveproc, NULL,
1422 typioparam, typmod);
1428 * Rather than copying data around, we just set up a phony StringInfo
1429 * pointing to the correct portion of the input buffer. We assume we
1430 * can scribble on the input buffer so as to maintain the convention
1431 * that StringInfos have a trailing null.
1433 elem_buf.data = &buf->data[buf->cursor];
1434 elem_buf.maxlen = itemlen + 1;
1435 elem_buf.len = itemlen;
1436 elem_buf.cursor = 0;
1438 buf->cursor += itemlen;
1440 csave = buf->data[buf->cursor];
1441 buf->data[buf->cursor] = '\0';
1443 /* Now call the element's receiveproc */
1444 values[i] = ReceiveFunctionCall(receiveproc, &elem_buf,
1445 typioparam, typmod);
1448 /* Trouble if it didn't eat the whole buffer */
1449 if (elem_buf.cursor != itemlen)
1451 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1452 errmsg("improper binary format in array element %d",
1455 buf->data[buf->cursor] = csave;
1459 * Check for nulls, compute total data space needed
1463 for (i = 0; i < nitems; i++)
1469 /* let's just make sure data is not toasted */
1471 values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
1472 totbytes = att_addlength_datum(totbytes, typlen, values[i]);
1473 totbytes = att_align_nominal(totbytes, typalign);
1474 /* check for overflow of total request */
1475 if (!AllocSizeIsValid(totbytes))
1477 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1478 errmsg("array size exceeds the maximum allowed (%d)",
1479 (int) MaxAllocSize)));
1482 *hasnulls = hasnull;
1489 * takes the internal representation of an array and returns a bytea
1490 * containing the array in its external binary format.
1493 array_send(PG_FUNCTION_ARGS)
1495 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1496 Oid element_type = ARR_ELEMTYPE(v);
1508 ArrayMetaState *my_extra;
1511 * We arrange to look up info about element type, including its send
1512 * conversion proc, only once per series of calls, assuming the element
1513 * type doesn't change underneath us.
1515 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1516 if (my_extra == NULL)
1518 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1519 sizeof(ArrayMetaState));
1520 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1521 my_extra->element_type = ~element_type;
1524 if (my_extra->element_type != element_type)
1526 /* Get info about element type, including its send proc */
1527 get_type_io_data(element_type, IOFunc_send,
1528 &my_extra->typlen, &my_extra->typbyval,
1529 &my_extra->typalign, &my_extra->typdelim,
1530 &my_extra->typioparam, &my_extra->typiofunc);
1531 if (!OidIsValid(my_extra->typiofunc))
1533 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1534 errmsg("no binary output function available for type %s",
1535 format_type_be(element_type))));
1536 fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1537 fcinfo->flinfo->fn_mcxt);
1538 my_extra->element_type = element_type;
1540 typlen = my_extra->typlen;
1541 typbyval = my_extra->typbyval;
1542 typalign = my_extra->typalign;
1546 nitems = ArrayGetNItems(ndim, dim);
1548 pq_begintypsend(&buf);
1550 /* Send the array header information */
1551 pq_sendint(&buf, ndim, 4);
1552 pq_sendint(&buf, ARR_HASNULL(v) ? 1 : 0, 4);
1553 pq_sendint(&buf, element_type, sizeof(Oid));
1554 for (i = 0; i < ndim; i++)
1556 pq_sendint(&buf, ARR_DIMS(v)[i], 4);
1557 pq_sendint(&buf, ARR_LBOUND(v)[i], 4);
1560 /* Send the array elements using the element's own sendproc */
1561 p = ARR_DATA_PTR(v);
1562 bitmap = ARR_NULLBITMAP(v);
1565 for (i = 0; i < nitems; i++)
1567 /* Get source element, checking for NULL */
1568 if (bitmap && (*bitmap & bitmask) == 0)
1570 /* -1 length means a NULL */
1571 pq_sendint(&buf, -1, 4);
1578 itemvalue = fetch_att(p, typbyval, typlen);
1579 outputbytes = SendFunctionCall(&my_extra->proc, itemvalue);
1580 pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
1581 pq_sendbytes(&buf, VARDATA(outputbytes),
1582 VARSIZE(outputbytes) - VARHDRSZ);
1585 p = att_addlength_pointer(p, typlen, p);
1586 p = (char *) att_align_nominal(p, typalign);
1589 /* advance bitmap pointer if any */
1593 if (bitmask == 0x100)
1601 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
1606 * returns the number of dimensions of the array pointed to by "v"
1609 array_ndims(PG_FUNCTION_ARGS)
1611 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1613 /* Sanity check: does it look like an array at all? */
1614 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1617 PG_RETURN_INT32(ARR_NDIM(v));
1622 * returns the dimensions of the array pointed to by "v", as a "text"
1625 array_dims(PG_FUNCTION_ARGS)
1627 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1634 * 33 since we assume 15 digits per number + ':' +'[]'
1636 * +1 for trailing null
1638 char buf[MAXDIM * 33 + 1];
1640 /* Sanity check: does it look like an array at all? */
1641 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1648 for (i = 0; i < ARR_NDIM(v); i++)
1650 sprintf(p, "[%d:%d]", lb[i], dimv[i] + lb[i] - 1);
1654 PG_RETURN_TEXT_P(cstring_to_text(buf));
1659 * returns the lower dimension, of the DIM requested, for
1660 * the array pointed to by "v", as an int4
1663 array_lower(PG_FUNCTION_ARGS)
1665 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1666 int reqdim = PG_GETARG_INT32(1);
1670 /* Sanity check: does it look like an array at all? */
1671 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1674 /* Sanity check: was the requested dim valid */
1675 if (reqdim <= 0 || reqdim > ARR_NDIM(v))
1679 result = lb[reqdim - 1];
1681 PG_RETURN_INT32(result);
1686 * returns the upper dimension, of the DIM requested, for
1687 * the array pointed to by "v", as an int4
1690 array_upper(PG_FUNCTION_ARGS)
1692 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1693 int reqdim = PG_GETARG_INT32(1);
1698 /* Sanity check: does it look like an array at all? */
1699 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1702 /* Sanity check: was the requested dim valid */
1703 if (reqdim <= 0 || reqdim > ARR_NDIM(v))
1709 result = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
1711 PG_RETURN_INT32(result);
1716 * returns the length, of the dimension requested, for
1717 * the array pointed to by "v", as an int4
1720 array_length(PG_FUNCTION_ARGS)
1722 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1723 int reqdim = PG_GETARG_INT32(1);
1727 /* Sanity check: does it look like an array at all? */
1728 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1731 /* Sanity check: was the requested dim valid */
1732 if (reqdim <= 0 || reqdim > ARR_NDIM(v))
1737 result = dimv[reqdim - 1];
1739 PG_RETURN_INT32(result);
1743 * array_cardinality:
1744 * returns the total number of elements in an array
1747 array_cardinality(PG_FUNCTION_ARGS)
1749 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1751 PG_RETURN_INT32(ArrayGetNItems(ARR_NDIM(v), ARR_DIMS(v)));
1757 * This routine takes an array pointer and a subscript array and returns
1758 * the referenced item as a Datum. Note that for a pass-by-reference
1759 * datatype, the returned Datum is a pointer into the array object.
1761 * This handles both ordinary varlena arrays and fixed-length arrays.
1764 * array: the array object (mustn't be NULL)
1765 * nSubscripts: number of subscripts supplied
1766 * indx[]: the subscript values
1767 * arraytyplen: pg_type.typlen for the array type
1768 * elmlen: pg_type.typlen for the array's element type
1769 * elmbyval: pg_type.typbyval for the array's element type
1770 * elmalign: pg_type.typalign for the array's element type
1773 * The return value is the element Datum.
1774 * *isNull is set to indicate whether the element is NULL.
1777 array_ref(ArrayType *array,
1795 bits8 *arraynullsptr;
1797 if (arraytyplen > 0)
1800 * fixed-length arrays -- these are assumed to be 1-d, 0-based
1803 fixedDim[0] = arraytyplen / elmlen;
1807 arraydataptr = (char *) array;
1808 arraynullsptr = NULL;
1812 /* detoast input array if necessary */
1813 array = DatumGetArrayTypeP(PointerGetDatum(array));
1815 ndim = ARR_NDIM(array);
1816 dim = ARR_DIMS(array);
1817 lb = ARR_LBOUND(array);
1818 arraydataptr = ARR_DATA_PTR(array);
1819 arraynullsptr = ARR_NULLBITMAP(array);
1823 * Return NULL for invalid subscript
1825 if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
1830 for (i = 0; i < ndim; i++)
1832 if (indx[i] < lb[i] || indx[i] >= (dim[i] + lb[i]))
1840 * Calculate the element number
1842 offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
1845 * Check for NULL array element
1847 if (array_get_isnull(arraynullsptr, offset))
1854 * OK, get the element
1857 retptr = array_seek(arraydataptr, 0, arraynullsptr, offset,
1858 elmlen, elmbyval, elmalign);
1859 return ArrayCast(retptr, elmbyval, elmlen);
1864 * This routine takes an array and a range of indices (upperIndex and
1865 * lowerIndx), creates a new array structure for the referred elements
1866 * and returns a pointer to it.
1868 * This handles both ordinary varlena arrays and fixed-length arrays.
1871 * array: the array object (mustn't be NULL)
1872 * nSubscripts: number of subscripts supplied (must be same for upper/lower)
1873 * upperIndx[]: the upper subscript values
1874 * lowerIndx[]: the lower subscript values
1875 * arraytyplen: pg_type.typlen for the array type
1876 * elmlen: pg_type.typlen for the array's element type
1877 * elmbyval: pg_type.typbyval for the array's element type
1878 * elmalign: pg_type.typalign for the array's element type
1881 * The return value is the new array Datum (it's never NULL)
1883 * NOTE: we assume it is OK to scribble on the provided subscript arrays
1884 * lowerIndx[] and upperIndx[]. These are generally just temporaries.
1887 array_get_slice(ArrayType *array,
1896 ArrayType *newarray;
1906 bits8 *arraynullsptr;
1911 if (arraytyplen > 0)
1914 * fixed-length arrays -- currently, cannot slice these because parser
1915 * labels output as being of the fixed-length array type! Code below
1916 * shows how we could support it if the parser were changed to label
1917 * output as a suitable varlena array type.
1920 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1921 errmsg("slices of fixed-length arrays not implemented")));
1924 * fixed-length arrays -- these are assumed to be 1-d, 0-based
1926 * XXX where would we get the correct ELEMTYPE from?
1929 fixedDim[0] = arraytyplen / elmlen;
1933 elemtype = InvalidOid; /* XXX */
1934 arraydataptr = (char *) array;
1935 arraynullsptr = NULL;
1939 /* detoast input array if necessary */
1940 array = DatumGetArrayTypeP(PointerGetDatum(array));
1942 ndim = ARR_NDIM(array);
1943 dim = ARR_DIMS(array);
1944 lb = ARR_LBOUND(array);
1945 elemtype = ARR_ELEMTYPE(array);
1946 arraydataptr = ARR_DATA_PTR(array);
1947 arraynullsptr = ARR_NULLBITMAP(array);
1951 * Check provided subscripts. A slice exceeding the current array limits
1952 * is silently truncated to the array limits. If we end up with an empty
1953 * slice, return an empty array.
1955 if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
1956 return construct_empty_array(elemtype);
1958 for (i = 0; i < nSubscripts; i++)
1960 if (lowerIndx[i] < lb[i])
1961 lowerIndx[i] = lb[i];
1962 if (upperIndx[i] >= (dim[i] + lb[i]))
1963 upperIndx[i] = dim[i] + lb[i] - 1;
1964 if (lowerIndx[i] > upperIndx[i])
1965 return construct_empty_array(elemtype);
1967 /* fill any missing subscript positions with full array range */
1968 for (; i < ndim; i++)
1970 lowerIndx[i] = lb[i];
1971 upperIndx[i] = dim[i] + lb[i] - 1;
1972 if (lowerIndx[i] > upperIndx[i])
1973 return construct_empty_array(elemtype);
1976 mda_get_range(ndim, span, lowerIndx, upperIndx);
1978 bytes = array_slice_size(arraydataptr, arraynullsptr,
1980 lowerIndx, upperIndx,
1981 elmlen, elmbyval, elmalign);
1984 * Currently, we put a null bitmap in the result if the source has one;
1985 * could be smarter ...
1989 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, ArrayGetNItems(ndim, span));
1990 bytes += dataoffset;
1994 dataoffset = 0; /* marker for no null bitmap */
1995 bytes += ARR_OVERHEAD_NONULLS(ndim);
1998 newarray = (ArrayType *) palloc0(bytes);
1999 SET_VARSIZE(newarray, bytes);
2000 newarray->ndim = ndim;
2001 newarray->dataoffset = dataoffset;
2002 newarray->elemtype = elemtype;
2003 memcpy(ARR_DIMS(newarray), span, ndim * sizeof(int));
2006 * Lower bounds of the new array are set to 1. Formerly (before 7.3) we
2007 * copied the given lowerIndx values ... but that seems confusing.
2009 newlb = ARR_LBOUND(newarray);
2010 for (i = 0; i < ndim; i++)
2013 array_extract_slice(newarray,
2015 arraydataptr, arraynullsptr,
2016 lowerIndx, upperIndx,
2017 elmlen, elmbyval, elmalign);
2024 * This routine sets the value of an array element (specified by
2025 * a subscript array) to a new value specified by "dataValue".
2027 * This handles both ordinary varlena arrays and fixed-length arrays.
2030 * array: the initial array object (mustn't be NULL)
2031 * nSubscripts: number of subscripts supplied
2032 * indx[]: the subscript values
2033 * dataValue: the datum to be inserted at the given position
2034 * isNull: whether dataValue is NULL
2035 * arraytyplen: pg_type.typlen for the array type
2036 * elmlen: pg_type.typlen for the array's element type
2037 * elmbyval: pg_type.typbyval for the array's element type
2038 * elmalign: pg_type.typalign for the array's element type
2041 * A new array is returned, just like the old except for the one
2042 * modified entry. The original array object is not changed.
2044 * For one-dimensional arrays only, we allow the array to be extended
2045 * by assigning to a position outside the existing subscript range; any
2046 * positions between the existing elements and the new one are set to NULLs.
2047 * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
2049 * NOTE: For assignments, we throw an error for invalid subscripts etc,
2050 * rather than returning a NULL as the fetch operations do.
2053 array_set(ArrayType *array,
2063 ArrayType *newarray;
2071 bits8 *oldnullbitmap;
2085 if (arraytyplen > 0)
2088 * fixed-length arrays -- these are assumed to be 1-d, 0-based. We
2089 * cannot extend them, either.
2091 if (nSubscripts != 1)
2093 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2094 errmsg("wrong number of array subscripts")));
2096 if (indx[0] < 0 || indx[0] * elmlen >= arraytyplen)
2098 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2099 errmsg("array subscript out of range")));
2103 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
2104 errmsg("cannot assign null value to an element of a fixed-length array")));
2106 newarray = (ArrayType *) palloc(arraytyplen);
2107 memcpy(newarray, array, arraytyplen);
2108 elt_ptr = (char *) newarray + indx[0] * elmlen;
2109 ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign, elt_ptr);
2113 if (nSubscripts <= 0 || nSubscripts > MAXDIM)
2115 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2116 errmsg("wrong number of array subscripts")));
2118 /* make sure item to be inserted is not toasted */
2119 if (elmlen == -1 && !isNull)
2120 dataValue = PointerGetDatum(PG_DETOAST_DATUM(dataValue));
2122 /* detoast input array if necessary */
2123 array = DatumGetArrayTypeP(PointerGetDatum(array));
2125 ndim = ARR_NDIM(array);
2128 * if number of dims is zero, i.e. an empty array, create an array with
2129 * nSubscripts dimensions, and set the lower bounds to the supplied
2134 Oid elmtype = ARR_ELEMTYPE(array);
2136 for (i = 0; i < nSubscripts; i++)
2142 return construct_md_array(&dataValue, &isNull, nSubscripts,
2144 elmlen, elmbyval, elmalign);
2147 if (ndim != nSubscripts)
2149 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2150 errmsg("wrong number of array subscripts")));
2152 /* copy dim/lb since we may modify them */
2153 memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
2154 memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
2156 newhasnulls = (ARR_HASNULL(array) || isNull);
2157 addedbefore = addedafter = 0;
2164 if (indx[0] < lb[0])
2166 addedbefore = lb[0] - indx[0];
2167 dim[0] += addedbefore;
2169 if (addedbefore > 1)
2170 newhasnulls = true; /* will insert nulls */
2172 if (indx[0] >= (dim[0] + lb[0]))
2174 addedafter = indx[0] - (dim[0] + lb[0]) + 1;
2175 dim[0] += addedafter;
2177 newhasnulls = true; /* will insert nulls */
2183 * XXX currently we do not support extending multi-dimensional arrays
2186 for (i = 0; i < ndim; i++)
2188 if (indx[i] < lb[i] ||
2189 indx[i] >= (dim[i] + lb[i]))
2191 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2192 errmsg("array subscript out of range")));
2197 * Compute sizes of items and areas to copy
2199 newnitems = ArrayGetNItems(ndim, dim);
2201 overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, newnitems);
2203 overheadlen = ARR_OVERHEAD_NONULLS(ndim);
2204 oldnitems = ArrayGetNItems(ndim, ARR_DIMS(array));
2205 oldnullbitmap = ARR_NULLBITMAP(array);
2206 oldoverheadlen = ARR_DATA_OFFSET(array);
2207 olddatasize = ARR_SIZE(array) - oldoverheadlen;
2213 lenafter = olddatasize;
2215 else if (addedafter)
2218 lenbefore = olddatasize;
2224 offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
2225 elt_ptr = array_seek(ARR_DATA_PTR(array), 0, oldnullbitmap, offset,
2226 elmlen, elmbyval, elmalign);
2227 lenbefore = (int) (elt_ptr - ARR_DATA_PTR(array));
2228 if (array_get_isnull(oldnullbitmap, offset))
2232 olditemlen = att_addlength_pointer(0, elmlen, elt_ptr);
2233 olditemlen = att_align_nominal(olditemlen, elmalign);
2235 lenafter = (int) (olddatasize - lenbefore - olditemlen);
2242 newitemlen = att_addlength_datum(0, elmlen, dataValue);
2243 newitemlen = att_align_nominal(newitemlen, elmalign);
2246 newsize = overheadlen + lenbefore + newitemlen + lenafter;
2249 * OK, create the new array and fill in header/dimensions
2251 newarray = (ArrayType *) palloc0(newsize);
2252 SET_VARSIZE(newarray, newsize);
2253 newarray->ndim = ndim;
2254 newarray->dataoffset = newhasnulls ? overheadlen : 0;
2255 newarray->elemtype = ARR_ELEMTYPE(array);
2256 memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
2257 memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
2262 memcpy((char *) newarray + overheadlen,
2263 (char *) array + oldoverheadlen,
2266 ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign,
2267 (char *) newarray + overheadlen + lenbefore);
2268 memcpy((char *) newarray + overheadlen + lenbefore + newitemlen,
2269 (char *) array + oldoverheadlen + lenbefore + olditemlen,
2273 * Fill in nulls bitmap if needed
2275 * Note: it's possible we just replaced the last NULL with a non-NULL, and
2276 * could get rid of the bitmap. Seems not worth testing for though.
2280 bits8 *newnullbitmap = ARR_NULLBITMAP(newarray);
2282 /* Zero the bitmap to take care of marking inserted positions null */
2283 MemSet(newnullbitmap, 0, (newnitems + 7) / 8);
2284 /* Fix the inserted value */
2286 array_set_isnull(newnullbitmap, newnitems - 1, isNull);
2288 array_set_isnull(newnullbitmap, offset, isNull);
2289 /* Fix the copied range(s) */
2291 array_bitmap_copy(newnullbitmap, addedbefore,
2296 array_bitmap_copy(newnullbitmap, 0,
2299 if (addedafter == 0)
2300 array_bitmap_copy(newnullbitmap, offset + 1,
2301 oldnullbitmap, offset + 1,
2302 oldnitems - offset - 1);
2311 * This routine sets the value of a range of array locations (specified
2312 * by upper and lower subscript values) to new values passed as
2315 * This handles both ordinary varlena arrays and fixed-length arrays.
2318 * array: the initial array object (mustn't be NULL)
2319 * nSubscripts: number of subscripts supplied (must be same for upper/lower)
2320 * upperIndx[]: the upper subscript values
2321 * lowerIndx[]: the lower subscript values
2322 * srcArray: the source for the inserted values
2323 * isNull: indicates whether srcArray is NULL
2324 * arraytyplen: pg_type.typlen for the array type
2325 * elmlen: pg_type.typlen for the array's element type
2326 * elmbyval: pg_type.typbyval for the array's element type
2327 * elmalign: pg_type.typalign for the array's element type
2330 * A new array is returned, just like the old except for the
2331 * modified range. The original array object is not changed.
2333 * For one-dimensional arrays only, we allow the array to be extended
2334 * by assigning to positions outside the existing subscript range; any
2335 * positions between the existing elements and the new ones are set to NULLs.
2336 * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
2338 * NOTE: we assume it is OK to scribble on the provided index arrays
2339 * lowerIndx[] and upperIndx[]. These are generally just temporaries.
2341 * NOTE: For assignments, we throw an error for silly subscripts etc,
2342 * rather than returning a NULL or empty array as the fetch operations do.
2345 array_set_slice(ArrayType *array,
2349 ArrayType *srcArray,
2356 ArrayType *newarray;
2379 /* Currently, assignment from a NULL source array is a no-op */
2383 if (arraytyplen > 0)
2386 * fixed-length arrays -- not got round to doing this...
2389 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2390 errmsg("updates on slices of fixed-length arrays not implemented")));
2393 /* detoast arrays if necessary */
2394 array = DatumGetArrayTypeP(PointerGetDatum(array));
2395 srcArray = DatumGetArrayTypeP(PointerGetDatum(srcArray));
2397 /* note: we assume srcArray contains no toasted elements */
2399 ndim = ARR_NDIM(array);
2402 * if number of dims is zero, i.e. an empty array, create an array with
2403 * nSubscripts dimensions, and set the upper and lower bounds to the
2404 * supplied subscripts
2411 Oid elmtype = ARR_ELEMTYPE(array);
2413 deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
2414 &dvalues, &dnulls, &nelems);
2416 for (i = 0; i < nSubscripts; i++)
2418 dim[i] = 1 + upperIndx[i] - lowerIndx[i];
2419 lb[i] = lowerIndx[i];
2422 /* complain if too few source items; we ignore extras, however */
2423 if (nelems < ArrayGetNItems(nSubscripts, dim))
2425 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2426 errmsg("source array too small")));
2428 return construct_md_array(dvalues, dnulls, nSubscripts,
2430 elmlen, elmbyval, elmalign);
2433 if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
2435 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2436 errmsg("wrong number of array subscripts")));
2438 /* copy dim/lb since we may modify them */
2439 memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
2440 memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
2442 newhasnulls = (ARR_HASNULL(array) || ARR_HASNULL(srcArray));
2443 addedbefore = addedafter = 0;
2450 Assert(nSubscripts == 1);
2451 if (lowerIndx[0] > upperIndx[0])
2453 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2454 errmsg("upper bound cannot be less than lower bound")));
2455 if (lowerIndx[0] < lb[0])
2457 if (upperIndx[0] < lb[0] - 1)
2458 newhasnulls = true; /* will insert nulls */
2459 addedbefore = lb[0] - lowerIndx[0];
2460 dim[0] += addedbefore;
2461 lb[0] = lowerIndx[0];
2463 if (upperIndx[0] >= (dim[0] + lb[0]))
2465 if (lowerIndx[0] > (dim[0] + lb[0]))
2466 newhasnulls = true; /* will insert nulls */
2467 addedafter = upperIndx[0] - (dim[0] + lb[0]) + 1;
2468 dim[0] += addedafter;
2474 * XXX currently we do not support extending multi-dimensional arrays
2477 for (i = 0; i < nSubscripts; i++)
2479 if (lowerIndx[i] > upperIndx[i])
2481 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2482 errmsg("upper bound cannot be less than lower bound")));
2483 if (lowerIndx[i] < lb[i] ||
2484 upperIndx[i] >= (dim[i] + lb[i]))
2486 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2487 errmsg("array subscript out of range")));
2489 /* fill any missing subscript positions with full array range */
2490 for (; i < ndim; i++)
2492 lowerIndx[i] = lb[i];
2493 upperIndx[i] = dim[i] + lb[i] - 1;
2494 if (lowerIndx[i] > upperIndx[i])
2496 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2497 errmsg("upper bound cannot be less than lower bound")));
2501 /* Do this mainly to check for overflow */
2502 nitems = ArrayGetNItems(ndim, dim);
2505 * Make sure source array has enough entries. Note we ignore the shape of
2506 * the source array and just read entries serially.
2508 mda_get_range(ndim, span, lowerIndx, upperIndx);
2509 nsrcitems = ArrayGetNItems(ndim, span);
2510 if (nsrcitems > ArrayGetNItems(ARR_NDIM(srcArray), ARR_DIMS(srcArray)))
2512 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2513 errmsg("source array too small")));
2516 * Compute space occupied by new entries, space occupied by replaced
2517 * entries, and required space for new array.
2520 overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
2522 overheadlen = ARR_OVERHEAD_NONULLS(ndim);
2523 newitemsize = array_nelems_size(ARR_DATA_PTR(srcArray), 0,
2524 ARR_NULLBITMAP(srcArray), nsrcitems,
2525 elmlen, elmbyval, elmalign);
2526 oldoverheadlen = ARR_DATA_OFFSET(array);
2527 olddatasize = ARR_SIZE(array) - oldoverheadlen;
2531 * here we do not need to cope with extension of the array; it would
2532 * be a lot more complicated if we had to do so...
2534 olditemsize = array_slice_size(ARR_DATA_PTR(array),
2535 ARR_NULLBITMAP(array),
2537 lowerIndx, upperIndx,
2538 elmlen, elmbyval, elmalign);
2539 lenbefore = lenafter = 0; /* keep compiler quiet */
2540 itemsbefore = itemsafter = nolditems = 0;
2545 * here we must allow for possibility of slice larger than orig array
2546 * and/or not adjacent to orig array subscripts
2548 int oldlb = ARR_LBOUND(array)[0];
2549 int oldub = oldlb + ARR_DIMS(array)[0] - 1;
2550 int slicelb = Max(oldlb, lowerIndx[0]);
2551 int sliceub = Min(oldub, upperIndx[0]);
2552 char *oldarraydata = ARR_DATA_PTR(array);
2553 bits8 *oldarraybitmap = ARR_NULLBITMAP(array);
2555 /* count/size of old array entries that will go before the slice */
2556 itemsbefore = Min(slicelb, oldub + 1) - oldlb;
2557 lenbefore = array_nelems_size(oldarraydata, 0, oldarraybitmap,
2559 elmlen, elmbyval, elmalign);
2560 /* count/size of old array entries that will be replaced by slice */
2561 if (slicelb > sliceub)
2568 nolditems = sliceub - slicelb + 1;
2569 olditemsize = array_nelems_size(oldarraydata + lenbefore,
2570 itemsbefore, oldarraybitmap,
2572 elmlen, elmbyval, elmalign);
2574 /* count/size of old array entries that will go after the slice */
2575 itemsafter = oldub + 1 - Max(sliceub + 1, oldlb);
2576 lenafter = olddatasize - lenbefore - olditemsize;
2579 newsize = overheadlen + olddatasize - olditemsize + newitemsize;
2581 newarray = (ArrayType *) palloc0(newsize);
2582 SET_VARSIZE(newarray, newsize);
2583 newarray->ndim = ndim;
2584 newarray->dataoffset = newhasnulls ? overheadlen : 0;
2585 newarray->elemtype = ARR_ELEMTYPE(array);
2586 memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
2587 memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
2592 * here we do not need to cope with extension of the array; it would
2593 * be a lot more complicated if we had to do so...
2595 array_insert_slice(newarray, array, srcArray,
2597 lowerIndx, upperIndx,
2598 elmlen, elmbyval, elmalign);
2603 memcpy((char *) newarray + overheadlen,
2604 (char *) array + oldoverheadlen,
2606 memcpy((char *) newarray + overheadlen + lenbefore,
2607 ARR_DATA_PTR(srcArray),
2609 memcpy((char *) newarray + overheadlen + lenbefore + newitemsize,
2610 (char *) array + oldoverheadlen + lenbefore + olditemsize,
2612 /* fill in nulls bitmap if needed */
2615 bits8 *newnullbitmap = ARR_NULLBITMAP(newarray);
2616 bits8 *oldnullbitmap = ARR_NULLBITMAP(array);
2618 /* Zero the bitmap to handle marking inserted positions null */
2619 MemSet(newnullbitmap, 0, (nitems + 7) / 8);
2620 array_bitmap_copy(newnullbitmap, addedbefore,
2623 array_bitmap_copy(newnullbitmap, lowerIndx[0] - lb[0],
2624 ARR_NULLBITMAP(srcArray), 0,
2626 array_bitmap_copy(newnullbitmap, addedbefore + itemsbefore + nolditems,
2627 oldnullbitmap, itemsbefore + nolditems,
2638 * Map an array through an arbitrary function. Return a new array with
2639 * same dimensions and each source element transformed by fn(). Each
2640 * source element is passed as the first argument to fn(); additional
2641 * arguments to be passed to fn() can be specified by the caller.
2642 * The output array can have a different element type than the input.
2645 * * fcinfo: a function-call data structure pre-constructed by the caller
2646 * to be ready to call the desired function, with everything except the
2647 * first argument position filled in. In particular, flinfo identifies
2648 * the function fn(), and if nargs > 1 then argument positions after the
2649 * first must be preset to the additional values to be passed. The
2650 * first argument position initially holds the input array value.
2651 * * inpType: OID of element type of input array. This must be the same as,
2652 * or binary-compatible with, the first argument type of fn().
2653 * * retType: OID of element type of output array. This must be the same as,
2654 * or binary-compatible with, the result type of fn().
2655 * * amstate: workspace for array_map. Must be zeroed by caller before
2656 * first call, and not touched after that.
2658 * It is legitimate to pass a freshly-zeroed ArrayMapState on each call,
2659 * but better performance can be had if the state can be preserved across
2660 * a series of calls.
2662 * NB: caller must assure that input array is not NULL. NULL elements in
2663 * the array are OK however.
2666 array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType,
2667 ArrayMapState *amstate)
2690 ArrayMetaState *inp_extra;
2691 ArrayMetaState *ret_extra;
2693 /* Get input array */
2694 if (fcinfo->nargs < 1)
2695 elog(ERROR, "invalid nargs: %d", fcinfo->nargs);
2696 if (PG_ARGISNULL(0))
2697 elog(ERROR, "null input array");
2698 v = PG_GETARG_ARRAYTYPE_P(0);
2700 Assert(ARR_ELEMTYPE(v) == inpType);
2704 nitems = ArrayGetNItems(ndim, dim);
2706 /* Check for empty array */
2709 /* Return empty array */
2710 PG_RETURN_ARRAYTYPE_P(construct_empty_array(retType));
2714 * We arrange to look up info about input and return element types only
2715 * once per series of calls, assuming the element type doesn't change
2718 inp_extra = &amstate->inp_extra;
2719 ret_extra = &amstate->ret_extra;
2721 if (inp_extra->element_type != inpType)
2723 get_typlenbyvalalign(inpType,
2725 &inp_extra->typbyval,
2726 &inp_extra->typalign);
2727 inp_extra->element_type = inpType;
2729 inp_typlen = inp_extra->typlen;
2730 inp_typbyval = inp_extra->typbyval;
2731 inp_typalign = inp_extra->typalign;
2733 if (ret_extra->element_type != retType)
2735 get_typlenbyvalalign(retType,
2737 &ret_extra->typbyval,
2738 &ret_extra->typalign);
2739 ret_extra->element_type = retType;
2741 typlen = ret_extra->typlen;
2742 typbyval = ret_extra->typbyval;
2743 typalign = ret_extra->typalign;
2745 /* Allocate temporary arrays for new values */
2746 values = (Datum *) palloc(nitems * sizeof(Datum));
2747 nulls = (bool *) palloc(nitems * sizeof(bool));
2749 /* Loop over source data */
2750 s = ARR_DATA_PTR(v);
2751 bitmap = ARR_NULLBITMAP(v);
2755 for (i = 0; i < nitems; i++)
2759 /* Get source element, checking for NULL */
2760 if (bitmap && (*bitmap & bitmask) == 0)
2762 fcinfo->argnull[0] = true;
2766 elt = fetch_att(s, inp_typbyval, inp_typlen);
2767 s = att_addlength_datum(s, inp_typlen, elt);
2768 s = (char *) att_align_nominal(s, inp_typalign);
2769 fcinfo->arg[0] = elt;
2770 fcinfo->argnull[0] = false;
2774 * Apply the given function to source elt and extra args.
2776 if (fcinfo->flinfo->fn_strict)
2780 for (j = 0; j < fcinfo->nargs; j++)
2782 if (fcinfo->argnull[j])
2792 fcinfo->isnull = false;
2793 values[i] = FunctionCallInvoke(fcinfo);
2796 fcinfo->isnull = true;
2798 nulls[i] = fcinfo->isnull;
2803 /* Ensure data is not toasted */
2805 values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
2806 /* Update total result size */
2807 nbytes = att_addlength_datum(nbytes, typlen, values[i]);
2808 nbytes = att_align_nominal(nbytes, typalign);
2809 /* check for overflow of total request */
2810 if (!AllocSizeIsValid(nbytes))
2812 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2813 errmsg("array size exceeds the maximum allowed (%d)",
2814 (int) MaxAllocSize)));
2817 /* advance bitmap pointer if any */
2821 if (bitmask == 0x100)
2829 /* Allocate and initialize the result array */
2832 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
2833 nbytes += dataoffset;
2837 dataoffset = 0; /* marker for no null bitmap */
2838 nbytes += ARR_OVERHEAD_NONULLS(ndim);
2840 result = (ArrayType *) palloc0(nbytes);
2841 SET_VARSIZE(result, nbytes);
2842 result->ndim = ndim;
2843 result->dataoffset = dataoffset;
2844 result->elemtype = retType;
2845 memcpy(ARR_DIMS(result), ARR_DIMS(v), 2 * ndim * sizeof(int));
2848 * Note: do not risk trying to pfree the results of the called function
2850 CopyArrayEls(result,
2851 values, nulls, nitems,
2852 typlen, typbyval, typalign,
2858 PG_RETURN_ARRAYTYPE_P(result);
2862 * construct_array --- simple method for constructing an array object
2864 * elems: array of Datum items to become the array contents
2865 * (NULL element values are not supported).
2866 * nelems: number of items
2867 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
2869 * A palloc'd 1-D array object is constructed and returned. Note that
2870 * elem values will be copied into the object even if pass-by-ref type.
2872 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
2873 * from the system catalogs, given the elmtype. However, the caller is
2874 * in a better position to cache this info across multiple uses, or even
2875 * to hard-wire values if the element type is hard-wired.
2878 construct_array(Datum *elems, int nelems,
2880 int elmlen, bool elmbyval, char elmalign)
2888 return construct_md_array(elems, NULL, 1, dims, lbs,
2889 elmtype, elmlen, elmbyval, elmalign);
2893 * construct_md_array --- simple method for constructing an array object
2894 * with arbitrary dimensions and possible NULLs
2896 * elems: array of Datum items to become the array contents
2897 * nulls: array of is-null flags (can be NULL if no nulls)
2898 * ndims: number of dimensions
2899 * dims: integer array with size of each dimension
2900 * lbs: integer array with lower bound of each dimension
2901 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
2903 * A palloc'd ndims-D array object is constructed and returned. Note that
2904 * elem values will be copied into the object even if pass-by-ref type.
2906 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
2907 * from the system catalogs, given the elmtype. However, the caller is
2908 * in a better position to cache this info across multiple uses, or even
2909 * to hard-wire values if the element type is hard-wired.
2912 construct_md_array(Datum *elems,
2917 Oid elmtype, int elmlen, bool elmbyval, char elmalign)
2926 if (ndims < 0) /* we do allow zero-dimension arrays */
2928 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2929 errmsg("invalid number of dimensions: %d", ndims)));
2932 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2933 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
2936 /* fast track for empty array */
2938 return construct_empty_array(elmtype);
2940 nelems = ArrayGetNItems(ndims, dims);
2942 /* compute required space */
2945 for (i = 0; i < nelems; i++)
2947 if (nulls && nulls[i])
2952 /* make sure data is not toasted */
2954 elems[i] = PointerGetDatum(PG_DETOAST_DATUM(elems[i]));
2955 nbytes = att_addlength_datum(nbytes, elmlen, elems[i]);
2956 nbytes = att_align_nominal(nbytes, elmalign);
2957 /* check for overflow of total request */
2958 if (!AllocSizeIsValid(nbytes))
2960 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2961 errmsg("array size exceeds the maximum allowed (%d)",
2962 (int) MaxAllocSize)));
2965 /* Allocate and initialize result array */
2968 dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nelems);
2969 nbytes += dataoffset;
2973 dataoffset = 0; /* marker for no null bitmap */
2974 nbytes += ARR_OVERHEAD_NONULLS(ndims);
2976 result = (ArrayType *) palloc0(nbytes);
2977 SET_VARSIZE(result, nbytes);
2978 result->ndim = ndims;
2979 result->dataoffset = dataoffset;
2980 result->elemtype = elmtype;
2981 memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
2982 memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
2984 CopyArrayEls(result,
2985 elems, nulls, nelems,
2986 elmlen, elmbyval, elmalign,
2993 * construct_empty_array --- make a zero-dimensional array of given type
2996 construct_empty_array(Oid elmtype)
3000 result = (ArrayType *) palloc0(sizeof(ArrayType));
3001 SET_VARSIZE(result, sizeof(ArrayType));
3003 result->dataoffset = 0;
3004 result->elemtype = elmtype;
3009 * deconstruct_array --- simple method for extracting data from an array
3011 * array: array object to examine (must not be NULL)
3012 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
3013 * elemsp: return value, set to point to palloc'd array of Datum values
3014 * nullsp: return value, set to point to palloc'd array of isnull markers
3015 * nelemsp: return value, set to number of extracted values
3017 * The caller may pass nullsp == NULL if it does not support NULLs in the
3018 * array. Note that this produces a very uninformative error message,
3019 * so do it only in cases where a NULL is really not expected.
3021 * If array elements are pass-by-ref data type, the returned Datums will
3022 * be pointers into the array object.
3024 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
3025 * from the system catalogs, given the elmtype. However, in most current
3026 * uses the type is hard-wired into the caller and so we can save a lookup
3027 * cycle by hard-wiring the type info as well.
3030 deconstruct_array(ArrayType *array,
3032 int elmlen, bool elmbyval, char elmalign,
3033 Datum **elemsp, bool **nullsp, int *nelemsp)
3043 Assert(ARR_ELEMTYPE(array) == elmtype);
3045 nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
3046 *elemsp = elems = (Datum *) palloc(nelems * sizeof(Datum));
3048 *nullsp = nulls = (bool *) palloc0(nelems * sizeof(bool));
3053 p = ARR_DATA_PTR(array);
3054 bitmap = ARR_NULLBITMAP(array);
3057 for (i = 0; i < nelems; i++)
3059 /* Get source element, checking for NULL */
3060 if (bitmap && (*bitmap & bitmask) == 0)
3062 elems[i] = (Datum) 0;
3067 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
3068 errmsg("null array element not allowed in this context")));
3072 elems[i] = fetch_att(p, elmbyval, elmlen);
3073 p = att_addlength_pointer(p, elmlen, p);
3074 p = (char *) att_align_nominal(p, elmalign);
3077 /* advance bitmap pointer if any */
3081 if (bitmask == 0x100)
3091 * array_contains_nulls --- detect whether an array has any null elements
3093 * This gives an accurate answer, whereas testing ARR_HASNULL only tells
3094 * if the array *might* contain a null.
3097 array_contains_nulls(ArrayType *array)
3103 /* Easy answer if there's no null bitmap */
3104 if (!ARR_HASNULL(array))
3107 nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
3109 bitmap = ARR_NULLBITMAP(array);
3111 /* check whole bytes of the bitmap byte-at-a-time */
3114 if (*bitmap != 0xFF)
3120 /* check last partial byte */
3124 if ((*bitmap & bitmask) == 0)
3136 * compares two arrays for equality
3138 * returns true if the arrays are equal, false otherwise.
3140 * Note: we do not use array_cmp here, since equality may be meaningful in
3141 * datatypes that don't have a total ordering (and hence no btree support).
3144 array_eq(PG_FUNCTION_ARGS)
3146 ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
3147 ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
3148 Oid collation = PG_GET_COLLATION();
3149 int ndims1 = ARR_NDIM(array1);
3150 int ndims2 = ARR_NDIM(array2);
3151 int *dims1 = ARR_DIMS(array1);
3152 int *dims2 = ARR_DIMS(array2);
3153 Oid element_type = ARR_ELEMTYPE(array1);
3156 TypeCacheEntry *typentry;
3166 FunctionCallInfoData locfcinfo;
3168 if (element_type != ARR_ELEMTYPE(array2))
3170 (errcode(ERRCODE_DATATYPE_MISMATCH),
3171 errmsg("cannot compare arrays of different element types")));
3173 /* fast path if the arrays do not have the same dimensionality */
3174 if (ndims1 != ndims2 ||
3175 memcmp(dims1, dims2, 2 * ndims1 * sizeof(int)) != 0)
3180 * We arrange to look up the equality function only once per series of
3181 * calls, assuming the element type doesn't change underneath us. The
3182 * typcache is used so that we have no memory leakage when being used
3183 * as an index support function.
3185 typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3186 if (typentry == NULL ||
3187 typentry->type_id != element_type)
3189 typentry = lookup_type_cache(element_type,
3190 TYPECACHE_EQ_OPR_FINFO);
3191 if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
3193 (errcode(ERRCODE_UNDEFINED_FUNCTION),
3194 errmsg("could not identify an equality operator for type %s",
3195 format_type_be(element_type))));
3196 fcinfo->flinfo->fn_extra = (void *) typentry;
3198 typlen = typentry->typlen;
3199 typbyval = typentry->typbyval;
3200 typalign = typentry->typalign;
3203 * apply the operator to each pair of array elements.
3205 InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
3206 collation, NULL, NULL);
3208 /* Loop over source data */
3209 nitems = ArrayGetNItems(ndims1, dims1);
3210 ptr1 = ARR_DATA_PTR(array1);
3211 ptr2 = ARR_DATA_PTR(array2);
3212 bitmap1 = ARR_NULLBITMAP(array1);
3213 bitmap2 = ARR_NULLBITMAP(array2);
3214 bitmask = 1; /* use same bitmask for both arrays */
3216 for (i = 0; i < nitems; i++)
3224 /* Get elements, checking for NULL */
3225 if (bitmap1 && (*bitmap1 & bitmask) == 0)
3233 elt1 = fetch_att(ptr1, typbyval, typlen);
3234 ptr1 = att_addlength_pointer(ptr1, typlen, ptr1);
3235 ptr1 = (char *) att_align_nominal(ptr1, typalign);
3238 if (bitmap2 && (*bitmap2 & bitmask) == 0)
3246 elt2 = fetch_att(ptr2, typbyval, typlen);
3247 ptr2 = att_addlength_pointer(ptr2, typlen, ptr2);
3248 ptr2 = (char *) att_align_nominal(ptr2, typalign);
3251 /* advance bitmap pointers if any */
3253 if (bitmask == 0x100)
3263 * We consider two NULLs equal; NULL and not-NULL are unequal.
3265 if (isnull1 && isnull2)
3267 if (isnull1 || isnull2)
3274 * Apply the operator to the element pair
3276 locfcinfo.arg[0] = elt1;
3277 locfcinfo.arg[1] = elt2;
3278 locfcinfo.argnull[0] = false;
3279 locfcinfo.argnull[1] = false;
3280 locfcinfo.isnull = false;
3281 oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
3290 /* Avoid leaking memory when handed toasted input. */
3291 PG_FREE_IF_COPY(array1, 0);
3292 PG_FREE_IF_COPY(array2, 1);
3294 PG_RETURN_BOOL(result);
3298 /*-----------------------------------------------------------------------------
3299 * array-array bool operators:
3300 * Given two arrays, iterate comparison operators
3301 * over the array. Uses logic similar to text comparison
3302 * functions, except element-by-element instead of
3303 * character-by-character.
3304 *----------------------------------------------------------------------------
3308 array_ne(PG_FUNCTION_ARGS)
3310 PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo)));
3314 array_lt(PG_FUNCTION_ARGS)
3316 PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
3320 array_gt(PG_FUNCTION_ARGS)
3322 PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
3326 array_le(PG_FUNCTION_ARGS)
3328 PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
3332 array_ge(PG_FUNCTION_ARGS)
3334 PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
3338 btarraycmp(PG_FUNCTION_ARGS)
3340 PG_RETURN_INT32(array_cmp(fcinfo));
3345 * Internal comparison function for arrays.
3347 * Returns -1, 0 or 1
3350 array_cmp(FunctionCallInfo fcinfo)
3352 ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
3353 ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
3354 Oid collation = PG_GET_COLLATION();
3355 int ndims1 = ARR_NDIM(array1);
3356 int ndims2 = ARR_NDIM(array2);
3357 int *dims1 = ARR_DIMS(array1);
3358 int *dims2 = ARR_DIMS(array2);
3359 int nitems1 = ArrayGetNItems(ndims1, dims1);
3360 int nitems2 = ArrayGetNItems(ndims2, dims2);
3361 Oid element_type = ARR_ELEMTYPE(array1);
3363 TypeCacheEntry *typentry;
3374 FunctionCallInfoData locfcinfo;
3376 if (element_type != ARR_ELEMTYPE(array2))
3378 (errcode(ERRCODE_DATATYPE_MISMATCH),
3379 errmsg("cannot compare arrays of different element types")));
3382 * We arrange to look up the comparison function only once per series of
3383 * calls, assuming the element type doesn't change underneath us. The
3384 * typcache is used so that we have no memory leakage when being used as
3385 * an index support function.
3387 typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3388 if (typentry == NULL ||
3389 typentry->type_id != element_type)
3391 typentry = lookup_type_cache(element_type,
3392 TYPECACHE_CMP_PROC_FINFO);
3393 if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
3395 (errcode(ERRCODE_UNDEFINED_FUNCTION),
3396 errmsg("could not identify a comparison function for type %s",
3397 format_type_be(element_type))));
3398 fcinfo->flinfo->fn_extra = (void *) typentry;
3400 typlen = typentry->typlen;
3401 typbyval = typentry->typbyval;
3402 typalign = typentry->typalign;
3405 * apply the operator to each pair of array elements.
3407 InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2,
3408 collation, NULL, NULL);
3410 /* Loop over source data */
3411 min_nitems = Min(nitems1, nitems2);
3412 ptr1 = ARR_DATA_PTR(array1);
3413 ptr2 = ARR_DATA_PTR(array2);
3414 bitmap1 = ARR_NULLBITMAP(array1);
3415 bitmap2 = ARR_NULLBITMAP(array2);
3416 bitmask = 1; /* use same bitmask for both arrays */
3418 for (i = 0; i < min_nitems; i++)
3426 /* Get elements, checking for NULL */
3427 if (bitmap1 && (*bitmap1 & bitmask) == 0)
3435 elt1 = fetch_att(ptr1, typbyval, typlen);
3436 ptr1 = att_addlength_pointer(ptr1, typlen, ptr1);
3437 ptr1 = (char *) att_align_nominal(ptr1, typalign);
3440 if (bitmap2 && (*bitmap2 & bitmask) == 0)
3448 elt2 = fetch_att(ptr2, typbyval, typlen);
3449 ptr2 = att_addlength_pointer(ptr2, typlen, ptr2);
3450 ptr2 = (char *) att_align_nominal(ptr2, typalign);
3453 /* advance bitmap pointers if any */
3455 if (bitmask == 0x100)
3465 * We consider two NULLs equal; NULL > not-NULL.
3467 if (isnull1 && isnull2)
3471 /* arg1 is greater than arg2 */
3477 /* arg1 is less than arg2 */
3482 /* Compare the pair of elements */
3483 locfcinfo.arg[0] = elt1;
3484 locfcinfo.arg[1] = elt2;
3485 locfcinfo.argnull[0] = false;
3486 locfcinfo.argnull[1] = false;
3487 locfcinfo.isnull = false;
3488 cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
3491 continue; /* equal */
3495 /* arg1 is less than arg2 */
3501 /* arg1 is greater than arg2 */
3508 * If arrays contain same data (up to end of shorter one), apply
3509 * additional rules to sort by dimensionality. The relative significance
3510 * of the different bits of information is historical; mainly we just care
3511 * that we don't say "equal" for arrays of different dimensionality.
3515 if (nitems1 != nitems2)
3516 result = (nitems1 < nitems2) ? -1 : 1;
3517 else if (ndims1 != ndims2)
3518 result = (ndims1 < ndims2) ? -1 : 1;
3521 /* this relies on LB array immediately following DIMS array */
3522 for (i = 0; i < ndims1 * 2; i++)
3524 if (dims1[i] != dims2[i])
3526 result = (dims1[i] < dims2[i]) ? -1 : 1;
3533 /* Avoid leaking memory when handed toasted input. */
3534 PG_FREE_IF_COPY(array1, 0);
3535 PG_FREE_IF_COPY(array2, 1);
3541 /*-----------------------------------------------------------------------------
3543 * Hash the elements and combine the results.
3544 *----------------------------------------------------------------------------
3548 hash_array(PG_FUNCTION_ARGS)
3550 ArrayType *array = PG_GETARG_ARRAYTYPE_P(0);
3551 int ndims = ARR_NDIM(array);
3552 int *dims = ARR_DIMS(array);
3553 Oid element_type = ARR_ELEMTYPE(array);
3556 TypeCacheEntry *typentry;
3564 FunctionCallInfoData locfcinfo;
3567 * We arrange to look up the hash function only once per series of calls,
3568 * assuming the element type doesn't change underneath us. The typcache
3569 * is used so that we have no memory leakage when being used as an index
3572 typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3573 if (typentry == NULL ||
3574 typentry->type_id != element_type)
3576 typentry = lookup_type_cache(element_type,
3577 TYPECACHE_HASH_PROC_FINFO);
3578 if (!OidIsValid(typentry->hash_proc_finfo.fn_oid))
3580 (errcode(ERRCODE_UNDEFINED_FUNCTION),
3581 errmsg("could not identify a hash function for type %s",
3582 format_type_be(element_type))));
3583 fcinfo->flinfo->fn_extra = (void *) typentry;
3585 typlen = typentry->typlen;
3586 typbyval = typentry->typbyval;
3587 typalign = typentry->typalign;
3590 * apply the hash function to each array element.
3592 InitFunctionCallInfoData(locfcinfo, &typentry->hash_proc_finfo, 1,
3593 InvalidOid, NULL, NULL);
3595 /* Loop over source data */
3596 nitems = ArrayGetNItems(ndims, dims);
3597 ptr = ARR_DATA_PTR(array);
3598 bitmap = ARR_NULLBITMAP(array);
3601 for (i = 0; i < nitems; i++)
3605 /* Get element, checking for NULL */
3606 if (bitmap && (*bitmap & bitmask) == 0)
3608 /* Treat nulls as having hashvalue 0 */
3615 elt = fetch_att(ptr, typbyval, typlen);
3616 ptr = att_addlength_pointer(ptr, typlen, ptr);
3617 ptr = (char *) att_align_nominal(ptr, typalign);
3619 /* Apply the hash function */
3620 locfcinfo.arg[0] = elt;
3621 locfcinfo.argnull[0] = false;
3622 locfcinfo.isnull = false;
3623 elthash = DatumGetUInt32(FunctionCallInvoke(&locfcinfo));
3626 /* advance bitmap pointer if any */
3630 if (bitmask == 0x100)
3638 * Combine hash values of successive elements by multiplying the
3639 * current value by 31 and adding on the new element's hash value.
3641 * The result is a sum in which each element's hash value is
3642 * multiplied by a different power of 31. This is modulo 2^32
3643 * arithmetic, and the powers of 31 modulo 2^32 form a cyclic group of
3644 * order 2^27. So for arrays of up to 2^27 elements, each element's
3645 * hash value is multiplied by a different (odd) number, resulting in
3646 * a good mixing of all the elements' hash values.
3648 result = (result << 5) - result + elthash;
3651 /* Avoid leaking memory when handed toasted input. */
3652 PG_FREE_IF_COPY(array, 0);
3654 PG_RETURN_UINT32(result);
3658 /*-----------------------------------------------------------------------------
3659 * array overlap/containment comparisons
3660 * These use the same methods of comparing array elements as array_eq.
3661 * We consider only the elements of the arrays, ignoring dimensionality.
3662 *----------------------------------------------------------------------------
3666 * array_contain_compare :
3667 * compares two arrays for overlap/containment
3669 * When matchall is true, return true if all members of array1 are in array2.
3670 * When matchall is false, return true if any members of array1 are in array2.
3673 array_contain_compare(ArrayType *array1, ArrayType *array2, Oid collation,
3674 bool matchall, void **fn_extra)
3676 bool result = matchall;
3677 Oid element_type = ARR_ELEMTYPE(array1);
3678 TypeCacheEntry *typentry;
3691 FunctionCallInfoData locfcinfo;
3693 if (element_type != ARR_ELEMTYPE(array2))
3695 (errcode(ERRCODE_DATATYPE_MISMATCH),
3696 errmsg("cannot compare arrays of different element types")));
3699 * We arrange to look up the equality function only once per series of
3700 * calls, assuming the element type doesn't change underneath us. The
3701 * typcache is used so that we have no memory leakage when being used as
3702 * an index support function.
3704 typentry = (TypeCacheEntry *) *fn_extra;
3705 if (typentry == NULL ||
3706 typentry->type_id != element_type)
3708 typentry = lookup_type_cache(element_type,
3709 TYPECACHE_EQ_OPR_FINFO);
3710 if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
3712 (errcode(ERRCODE_UNDEFINED_FUNCTION),
3713 errmsg("could not identify an equality operator for type %s",
3714 format_type_be(element_type))));
3715 *fn_extra = (void *) typentry;
3717 typlen = typentry->typlen;
3718 typbyval = typentry->typbyval;
3719 typalign = typentry->typalign;
3722 * Since we probably will need to scan array2 multiple times, it's
3723 * worthwhile to use deconstruct_array on it. We scan array1 the hard way
3724 * however, since we very likely won't need to look at all of it.
3726 deconstruct_array(array2, element_type, typlen, typbyval, typalign,
3727 &values2, &nulls2, &nelems2);
3730 * Apply the comparison operator to each pair of array elements.
3732 InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
3733 collation, NULL, NULL);
3735 /* Loop over source data */
3736 nelems1 = ArrayGetNItems(ARR_NDIM(array1), ARR_DIMS(array1));
3737 ptr1 = ARR_DATA_PTR(array1);
3738 bitmap1 = ARR_NULLBITMAP(array1);
3741 for (i = 0; i < nelems1; i++)
3746 /* Get element, checking for NULL */
3747 if (bitmap1 && (*bitmap1 & bitmask) == 0)
3755 elt1 = fetch_att(ptr1, typbyval, typlen);
3756 ptr1 = att_addlength_pointer(ptr1, typlen, ptr1);
3757 ptr1 = (char *) att_align_nominal(ptr1, typalign);
3760 /* advance bitmap pointer if any */
3762 if (bitmask == 0x100)
3770 * We assume that the comparison operator is strict, so a NULL can't
3771 * match anything. XXX this diverges from the "NULL=NULL" behavior of
3772 * array_eq, should we act like that?
3784 for (j = 0; j < nelems2; j++)
3786 Datum elt2 = values2[j];
3787 bool isnull2 = nulls2[j];
3791 continue; /* can't match */
3794 * Apply the operator to the element pair
3796 locfcinfo.arg[0] = elt1;
3797 locfcinfo.arg[1] = elt2;
3798 locfcinfo.argnull[0] = false;
3799 locfcinfo.argnull[1] = false;
3800 locfcinfo.isnull = false;
3801 oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
3808 /* found a match for elt1 */
3817 /* no match for elt1 */
3833 arrayoverlap(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(array1, array2, collation, false,
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 arraycontains(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(array2, array1, 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 arraycontained(PG_FUNCTION_ARGS)
3871 ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
3872 ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
3873 Oid collation = PG_GET_COLLATION();
3876 result = array_contain_compare(array1, array2, collation, true,
3877 &fcinfo->flinfo->fn_extra);
3879 /* Avoid leaking memory when handed toasted input. */
3880 PG_FREE_IF_COPY(array1, 0);
3881 PG_FREE_IF_COPY(array2, 1);
3883 PG_RETURN_BOOL(result);
3887 /*-----------------------------------------------------------------------------
3888 * Array iteration functions
3889 * These functions are used to iterate efficiently through arrays
3890 *-----------------------------------------------------------------------------
3894 * array_create_iterator --- set up to iterate through an array
3896 * If slice_ndim is zero, we will iterate element-by-element; the returned
3897 * datums are of the array's element type.
3899 * If slice_ndim is 1..ARR_NDIM(arr), we will iterate by slices: the
3900 * returned datums are of the same array type as 'arr', but of size
3901 * equal to the rightmost N dimensions of 'arr'.
3903 * The passed-in array must remain valid for the lifetime of the iterator.
3906 array_create_iterator(ArrayType *arr, int slice_ndim)
3908 ArrayIterator iterator = palloc0(sizeof(ArrayIteratorData));
3911 * Sanity-check inputs --- caller should have got this right already
3913 Assert(PointerIsValid(arr));
3914 if (slice_ndim < 0 || slice_ndim > ARR_NDIM(arr))
3915 elog(ERROR, "invalid arguments to array_create_iterator");
3918 * Remember basic info about the array and its element type
3920 iterator->arr = arr;
3921 iterator->nullbitmap = ARR_NULLBITMAP(arr);
3922 iterator->nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
3923 get_typlenbyvalalign(ARR_ELEMTYPE(arr),
3925 &iterator->typbyval,
3926 &iterator->typalign);
3929 * Remember the slicing parameters.
3931 iterator->slice_ndim = slice_ndim;
3936 * Get pointers into the array's dims and lbound arrays to represent
3937 * the dims/lbound arrays of a slice. These are the same as the
3938 * rightmost N dimensions of the array.
3940 iterator->slice_dims = ARR_DIMS(arr) + ARR_NDIM(arr) - slice_ndim;
3941 iterator->slice_lbound = ARR_LBOUND(arr) + ARR_NDIM(arr) - slice_ndim;
3944 * Compute number of elements in a slice.
3946 iterator->slice_len = ArrayGetNItems(slice_ndim,
3947 iterator->slice_dims);
3950 * Create workspace for building sub-arrays.
3952 iterator->slice_values = (Datum *)
3953 palloc(iterator->slice_len * sizeof(Datum));
3954 iterator->slice_nulls = (bool *)
3955 palloc(iterator->slice_len * sizeof(bool));
3959 * Initialize our data pointer and linear element number. These will
3960 * advance through the array during array_iterate().
3962 iterator->data_ptr = ARR_DATA_PTR(arr);
3963 iterator->current_item = 0;
3969 * Iterate through the array referenced by 'iterator'.
3971 * As long as there is another element (or slice), return it into
3972 * *value / *isnull, and return true. Return false when no more data.
3975 array_iterate(ArrayIterator iterator, Datum *value, bool *isnull)
3977 /* Done if we have reached the end of the array */
3978 if (iterator->current_item >= iterator->nitems)
3981 if (iterator->slice_ndim == 0)
3984 * Scalar case: return one element.
3986 if (array_get_isnull(iterator->nullbitmap, iterator->current_item++))
3993 /* non-NULL, so fetch the individual Datum to return */
3994 char *p = iterator->data_ptr;
3997 *value = fetch_att(p, iterator->typbyval, iterator->typlen);
3999 /* Move our data pointer forward to the next element */
4000 p = att_addlength_pointer(p, iterator->typlen, p);
4001 p = (char *) att_align_nominal(p, iterator->typalign);
4002 iterator->data_ptr = p;
4008 * Slice case: build and return an array of the requested size.
4011 Datum *values = iterator->slice_values;
4012 bool *nulls = iterator->slice_nulls;
4013 char *p = iterator->data_ptr;
4016 for (i = 0; i < iterator->slice_len; i++)
4018 if (array_get_isnull(iterator->nullbitmap,
4019 iterator->current_item++))
4022 values[i] = (Datum) 0;
4027 values[i] = fetch_att(p, iterator->typbyval, iterator->typlen);
4029 /* Move our data pointer forward to the next element */
4030 p = att_addlength_pointer(p, iterator->typlen, p);
4031 p = (char *) att_align_nominal(p, iterator->typalign);
4035 iterator->data_ptr = p;
4037 result = construct_md_array(values,
4039 iterator->slice_ndim,
4040 iterator->slice_dims,
4041 iterator->slice_lbound,
4042 ARR_ELEMTYPE(iterator->arr),
4045 iterator->typalign);
4048 *value = PointerGetDatum(result);
4055 * Release an ArrayIterator data structure
4058 array_free_iterator(ArrayIterator iterator)
4060 if (iterator->slice_ndim > 0)
4062 pfree(iterator->slice_values);
4063 pfree(iterator->slice_nulls);
4069 /***************************************************************************/
4070 /******************| Support Routines |*****************/
4071 /***************************************************************************/
4074 * Check whether a specific array element is NULL
4076 * nullbitmap: pointer to array's null bitmap (NULL if none)
4077 * offset: 0-based linear element number of array element
4080 array_get_isnull(const bits8 *nullbitmap, int offset)
4082 if (nullbitmap == NULL)
4083 return false; /* assume not null */
4084 if (nullbitmap[offset / 8] & (1 << (offset % 8)))
4085 return false; /* not null */
4090 * Set a specific array element's null-bitmap entry
4092 * nullbitmap: pointer to array's null bitmap (mustn't be NULL)
4093 * offset: 0-based linear element number of array element
4094 * isNull: null status to set
4097 array_set_isnull(bits8 *nullbitmap, int offset, bool isNull)
4101 nullbitmap += offset / 8;
4102 bitmask = 1 << (offset % 8);
4104 *nullbitmap &= ~bitmask;
4106 *nullbitmap |= bitmask;
4110 * Fetch array element at pointer, converted correctly to a Datum
4112 * Caller must have handled case of NULL element
4115 ArrayCast(char *value, bool byval, int len)
4117 return fetch_att(value, byval, len);
4121 * Copy datum to *dest and return total space used (including align padding)
4123 * Caller must have handled case of NULL element
4126 ArrayCastAndSet(Datum src,
4137 store_att_byval(dest, src, typlen);
4139 memmove(dest, DatumGetPointer(src), typlen);
4140 inc = att_align_nominal(typlen, typalign);
4145 inc = att_addlength_datum(0, typlen, src);
4146 memmove(dest, DatumGetPointer(src), inc);
4147 inc = att_align_nominal(inc, typalign);
4154 * Advance ptr over nitems array elements
4156 * ptr: starting location in array
4157 * offset: 0-based linear element number of first element (the one at *ptr)
4158 * nullbitmap: start of array's null bitmap, or NULL if none
4159 * nitems: number of array elements to advance over (>= 0)
4160 * typlen, typbyval, typalign: storage parameters of array element datatype
4162 * It is caller's responsibility to ensure that nitems is within range
4165 array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
4166 int typlen, bool typbyval, char typalign)
4171 /* easy if fixed-size elements and no NULLs */
4172 if (typlen > 0 && !nullbitmap)
4173 return ptr + nitems * ((Size) att_align_nominal(typlen, typalign));
4175 /* seems worth having separate loops for NULL and no-NULLs cases */
4178 nullbitmap += offset / 8;
4179 bitmask = 1 << (offset % 8);
4181 for (i = 0; i < nitems; i++)
4183 if (*nullbitmap & bitmask)
4185 ptr = att_addlength_pointer(ptr, typlen, ptr);
4186 ptr = (char *) att_align_nominal(ptr, typalign);
4189 if (bitmask == 0x100)
4198 for (i = 0; i < nitems; i++)
4200 ptr = att_addlength_pointer(ptr, typlen, ptr);
4201 ptr = (char *) att_align_nominal(ptr, typalign);
4208 * Compute total size of the nitems array elements starting at *ptr
4210 * Parameters same as for array_seek
4213 array_nelems_size(char *ptr, int offset, bits8 *nullbitmap, int nitems,
4214 int typlen, bool typbyval, char typalign)
4216 return array_seek(ptr, offset, nullbitmap, nitems,
4217 typlen, typbyval, typalign) - ptr;
4221 * Copy nitems array elements from srcptr to destptr
4223 * destptr: starting destination location (must be enough room!)
4224 * nitems: number of array elements to copy (>= 0)
4225 * srcptr: starting location in source array
4226 * offset: 0-based linear element number of first element (the one at *srcptr)
4227 * nullbitmap: start of source array's null bitmap, or NULL if none
4228 * typlen, typbyval, typalign: storage parameters of array element datatype
4230 * Returns number of bytes copied
4232 * NB: this does not take care of setting up the destination's null bitmap!
4235 array_copy(char *destptr, int nitems,
4236 char *srcptr, int offset, bits8 *nullbitmap,
4237 int typlen, bool typbyval, char typalign)
4241 numbytes = array_nelems_size(srcptr, offset, nullbitmap, nitems,
4242 typlen, typbyval, typalign);
4243 memcpy(destptr, srcptr, numbytes);
4248 * Copy nitems null-bitmap bits from source to destination
4250 * destbitmap: start of destination array's null bitmap (mustn't be NULL)
4251 * destoffset: 0-based linear element number of first dest element
4252 * srcbitmap: start of source array's null bitmap, or NULL if none
4253 * srcoffset: 0-based linear element number of first source element
4254 * nitems: number of bits to copy (>= 0)
4256 * If srcbitmap is NULL then we assume the source is all-non-NULL and
4257 * fill 1's into the destination bitmap. Note that only the specified
4258 * bits in the destination map are changed, not any before or after.
4260 * Note: this could certainly be optimized using standard bitblt methods.
4261 * However, it's not clear that the typical Postgres array has enough elements
4262 * to make it worth worrying too much. For the moment, KISS.
4265 array_bitmap_copy(bits8 *destbitmap, int destoffset,
4266 const bits8 *srcbitmap, int srcoffset,
4276 return; /* don't risk fetch off end of memory */
4277 destbitmap += destoffset / 8;
4278 destbitmask = 1 << (destoffset % 8);
4279 destbitval = *destbitmap;
4282 srcbitmap += srcoffset / 8;
4283 srcbitmask = 1 << (srcoffset % 8);
4284 srcbitval = *srcbitmap;
4285 while (nitems-- > 0)
4287 if (srcbitval & srcbitmask)
4288 destbitval |= destbitmask;
4290 destbitval &= ~destbitmask;
4292 if (destbitmask == 0x100)
4294 *destbitmap++ = destbitval;
4297 destbitval = *destbitmap;
4300 if (srcbitmask == 0x100)
4305 srcbitval = *srcbitmap;
4308 if (destbitmask != 1)
4309 *destbitmap = destbitval;
4313 while (nitems-- > 0)
4315 destbitval |= destbitmask;
4317 if (destbitmask == 0x100)
4319 *destbitmap++ = destbitval;
4322 destbitval = *destbitmap;
4325 if (destbitmask != 1)
4326 *destbitmap = destbitval;
4331 * Compute space needed for a slice of an array
4333 * We assume the caller has verified that the slice coordinates are valid.
4336 array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
4337 int ndim, int *dim, int *lb,
4339 int typlen, bool typbyval, char typalign)
4352 mda_get_range(ndim, span, st, endp);
4354 /* Pretty easy for fixed element length without nulls ... */
4355 if (typlen > 0 && !arraynullsptr)
4356 return ArrayGetNItems(ndim, span) * att_align_nominal(typlen, typalign);
4358 /* Else gotta do it the hard way */
4359 src_offset = ArrayGetOffset(ndim, dim, lb, st);
4360 ptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
4361 typlen, typbyval, typalign);
4362 mda_get_prod(ndim, dim, prod);
4363 mda_get_offset_values(ndim, dist, prod, span);
4364 for (i = 0; i < ndim; i++)
4371 ptr = array_seek(ptr, src_offset, arraynullsptr, dist[j],
4372 typlen, typbyval, typalign);
4373 src_offset += dist[j];
4375 if (!array_get_isnull(arraynullsptr, src_offset))
4377 inc = att_addlength_pointer(0, typlen, ptr);
4378 inc = att_align_nominal(inc, typalign);
4383 } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
4388 * Extract a slice of an array into consecutive elements in the destination
4391 * We assume the caller has verified that the slice coordinates are valid,
4392 * allocated enough storage for the result, and initialized the header
4396 array_extract_slice(ArrayType *newarray,
4401 bits8 *arraynullsptr,
4408 char *destdataptr = ARR_DATA_PTR(newarray);
4409 bits8 *destnullsptr = ARR_NULLBITMAP(newarray);
4421 src_offset = ArrayGetOffset(ndim, dim, lb, st);
4422 srcdataptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
4423 typlen, typbyval, typalign);
4424 mda_get_prod(ndim, dim, prod);
4425 mda_get_range(ndim, span, st, endp);
4426 mda_get_offset_values(ndim, dist, prod, span);
4427 for (i = 0; i < ndim; i++)
4435 /* skip unwanted elements */
4436 srcdataptr = array_seek(srcdataptr, src_offset, arraynullsptr,
4438 typlen, typbyval, typalign);
4439 src_offset += dist[j];
4441 inc = array_copy(destdataptr, 1,
4442 srcdataptr, src_offset, arraynullsptr,
4443 typlen, typbyval, typalign);
4445 array_bitmap_copy(destnullsptr, dest_offset,
4446 arraynullsptr, src_offset,
4452 } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
4456 * Insert a slice into an array.
4458 * ndim/dim[]/lb[] are dimensions of the original array. A new array with
4459 * those same dimensions is to be constructed. destArray must already
4460 * have been allocated and its header initialized.
4462 * st[]/endp[] identify the slice to be replaced. Elements within the slice
4463 * volume are taken from consecutive elements of the srcArray; elements
4464 * outside it are copied from origArray.
4466 * We assume the caller has verified that the slice coordinates are valid.
4469 array_insert_slice(ArrayType *destArray,
4470 ArrayType *origArray,
4471 ArrayType *srcArray,
4481 char *destPtr = ARR_DATA_PTR(destArray);
4482 char *origPtr = ARR_DATA_PTR(origArray);
4483 char *srcPtr = ARR_DATA_PTR(srcArray);
4484 bits8 *destBitmap = ARR_NULLBITMAP(destArray);
4485 bits8 *origBitmap = ARR_NULLBITMAP(origArray);
4486 bits8 *srcBitmap = ARR_NULLBITMAP(srcArray);
4487 int orignitems = ArrayGetNItems(ARR_NDIM(origArray),
4488 ARR_DIMS(origArray));
4500 dest_offset = ArrayGetOffset(ndim, dim, lb, st);
4501 /* copy items before the slice start */
4502 inc = array_copy(destPtr, dest_offset,
4503 origPtr, 0, origBitmap,
4504 typlen, typbyval, typalign);
4508 array_bitmap_copy(destBitmap, 0, origBitmap, 0, dest_offset);
4509 orig_offset = dest_offset;
4510 mda_get_prod(ndim, dim, prod);
4511 mda_get_range(ndim, span, st, endp);
4512 mda_get_offset_values(ndim, dist, prod, span);
4513 for (i = 0; i < ndim; i++)
4519 /* Copy/advance over elements between here and next part of slice */
4522 inc = array_copy(destPtr, dist[j],
4523 origPtr, orig_offset, origBitmap,
4524 typlen, typbyval, typalign);
4528 array_bitmap_copy(destBitmap, dest_offset,
4529 origBitmap, orig_offset,
4531 dest_offset += dist[j];
4532 orig_offset += dist[j];
4534 /* Copy new element at this slice position */
4535 inc = array_copy(destPtr, 1,
4536 srcPtr, src_offset, srcBitmap,
4537 typlen, typbyval, typalign);
4539 array_bitmap_copy(destBitmap, dest_offset,
4540 srcBitmap, src_offset,
4546 /* Advance over old element at this slice position */
4547 origPtr = array_seek(origPtr, orig_offset, origBitmap, 1,
4548 typlen, typbyval, typalign);
4550 } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
4552 /* don't miss any data at the end */
4553 array_copy(destPtr, orignitems - orig_offset,
4554 origPtr, orig_offset, origBitmap,
4555 typlen, typbyval, typalign);
4557 array_bitmap_copy(destBitmap, dest_offset,
4558 origBitmap, orig_offset,
4559 orignitems - orig_offset);
4563 * accumArrayResult - accumulate one (more) Datum for an array result
4565 * astate is working state (NULL on first call)
4566 * rcontext is where to keep working state
4569 accumArrayResult(ArrayBuildState *astate,
4570 Datum dvalue, bool disnull,
4572 MemoryContext rcontext)
4574 MemoryContext arr_context,
4579 /* First time through --- initialize */
4581 /* Make a temporary context to hold all the junk */
4582 arr_context = AllocSetContextCreate(rcontext,
4584 ALLOCSET_DEFAULT_MINSIZE,
4585 ALLOCSET_DEFAULT_INITSIZE,
4586 ALLOCSET_DEFAULT_MAXSIZE);
4587 oldcontext = MemoryContextSwitchTo(arr_context);
4588 astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
4589 astate->mcontext = arr_context;
4590 astate->alen = 64; /* arbitrary starting array size */
4591 astate->dvalues = (Datum *) palloc(astate->alen * sizeof(Datum));
4592 astate->dnulls = (bool *) palloc(astate->alen * sizeof(bool));
4594 astate->element_type = element_type;
4595 get_typlenbyvalalign(element_type,
4602 oldcontext = MemoryContextSwitchTo(astate->mcontext);
4603 Assert(astate->element_type == element_type);
4604 /* enlarge dvalues[]/dnulls[] if needed */
4605 if (astate->nelems >= astate->alen)
4608 astate->dvalues = (Datum *)
4609 repalloc(astate->dvalues, astate->alen * sizeof(Datum));
4610 astate->dnulls = (bool *)
4611 repalloc(astate->dnulls, astate->alen * sizeof(bool));
4616 * Ensure pass-by-ref stuff is copied into mcontext; and detoast it too if
4617 * it's varlena. (You might think that detoasting is not needed here
4618 * because construct_md_array can detoast the array elements later.
4619 * However, we must not let construct_md_array modify the ArrayBuildState
4620 * because that would mean array_agg_finalfn damages its input, which is
4621 * verboten. Also, this way frequently saves one copying step.)
4623 if (!disnull && !astate->typbyval)
4625 if (astate->typlen == -1)
4626 dvalue = PointerGetDatum(PG_DETOAST_DATUM_COPY(dvalue));
4628 dvalue = datumCopy(dvalue, astate->typbyval, astate->typlen);
4631 astate->dvalues[astate->nelems] = dvalue;
4632 astate->dnulls[astate->nelems] = disnull;
4635 MemoryContextSwitchTo(oldcontext);
4641 * makeArrayResult - produce 1-D final result of accumArrayResult
4643 * astate is working state (not NULL)
4644 * rcontext is where to construct result
4647 makeArrayResult(ArrayBuildState *astate,
4648 MemoryContext rcontext)
4653 dims[0] = astate->nelems;
4656 return makeMdArrayResult(astate, 1, dims, lbs, rcontext, true);
4660 * makeMdArrayResult - produce multi-D final result of accumArrayResult
4662 * beware: no check that specified dimensions match the number of values
4665 * astate is working state (not NULL)
4666 * rcontext is where to construct result
4667 * release is true if okay to release working state
4670 makeMdArrayResult(ArrayBuildState *astate,
4674 MemoryContext rcontext,
4678 MemoryContext oldcontext;
4680 /* Build the final array result in rcontext */
4681 oldcontext = MemoryContextSwitchTo(rcontext);
4683 result = construct_md_array(astate->dvalues,
4688 astate->element_type,
4693 MemoryContextSwitchTo(oldcontext);
4695 /* Clean up all the junk */
4697 MemoryContextDelete(astate->mcontext);
4699 return PointerGetDatum(result);
4703 array_larger(PG_FUNCTION_ARGS)
4709 v1 = PG_GETARG_ARRAYTYPE_P(0);
4710 v2 = PG_GETARG_ARRAYTYPE_P(1);
4712 result = ((array_cmp(fcinfo) > 0) ? v1 : v2);
4714 PG_RETURN_ARRAYTYPE_P(result);
4718 array_smaller(PG_FUNCTION_ARGS)
4724 v1 = PG_GETARG_ARRAYTYPE_P(0);
4725 v2 = PG_GETARG_ARRAYTYPE_P(1);
4727 result = ((array_cmp(fcinfo) < 0) ? v1 : v2);
4729 PG_RETURN_ARRAYTYPE_P(result);
4733 typedef struct generate_subscripts_fctx
4738 } generate_subscripts_fctx;
4741 * generate_subscripts(array anyarray, dim int [, reverse bool])
4742 * Returns all subscripts of the array for any dimension
4745 generate_subscripts(PG_FUNCTION_ARGS)
4747 FuncCallContext *funcctx;
4748 MemoryContext oldcontext;
4749 generate_subscripts_fctx *fctx;
4751 /* stuff done only on the first call of the function */
4752 if (SRF_IS_FIRSTCALL())
4754 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
4755 int reqdim = PG_GETARG_INT32(1);
4759 /* create a function context for cross-call persistence */
4760 funcctx = SRF_FIRSTCALL_INIT();
4762 /* Sanity check: does it look like an array at all? */
4763 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
4764 SRF_RETURN_DONE(funcctx);
4766 /* Sanity check: was the requested dim valid */
4767 if (reqdim <= 0 || reqdim > ARR_NDIM(v))
4768 SRF_RETURN_DONE(funcctx);
4771 * switch to memory context appropriate for multiple function calls
4773 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
4774 fctx = (generate_subscripts_fctx *) palloc(sizeof(generate_subscripts_fctx));
4779 fctx->lower = lb[reqdim - 1];
4780 fctx->upper = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
4781 fctx->reverse = (PG_NARGS() < 3) ? false : PG_GETARG_BOOL(2);
4783 funcctx->user_fctx = fctx;
4785 MemoryContextSwitchTo(oldcontext);
4788 funcctx = SRF_PERCALL_SETUP();
4790 fctx = funcctx->user_fctx;
4792 if (fctx->lower <= fctx->upper)
4795 SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->lower++));
4797 SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->upper--));
4800 /* done when there are no more elements left */
4801 SRF_RETURN_DONE(funcctx);
4805 * generate_subscripts_nodir
4806 * Implements the 2-argument version of generate_subscripts
4809 generate_subscripts_nodir(PG_FUNCTION_ARGS)
4811 /* just call the other one -- it can handle both cases */
4812 return generate_subscripts(fcinfo);
4816 * array_fill_with_lower_bounds
4817 * Create and fill array with defined lower bounds.
4820 array_fill_with_lower_bounds(PG_FUNCTION_ARGS)
4829 if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
4831 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
4832 errmsg("dimension array or low bound array cannot be null")));
4834 dims = PG_GETARG_ARRAYTYPE_P(1);
4835 lbs = PG_GETARG_ARRAYTYPE_P(2);
4837 if (!PG_ARGISNULL(0))
4839 value = PG_GETARG_DATUM(0);
4848 elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
4849 if (!OidIsValid(elmtype))
4850 elog(ERROR, "could not determine data type of input");
4852 result = array_fill_internal(dims, lbs, value, isnull, elmtype, fcinfo);
4853 PG_RETURN_ARRAYTYPE_P(result);
4858 * Create and fill array with default lower bounds.
4861 array_fill(PG_FUNCTION_ARGS)
4869 if (PG_ARGISNULL(1))
4871 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
4872 errmsg("dimension array or low bound array cannot be null")));
4874 dims = PG_GETARG_ARRAYTYPE_P(1);
4876 if (!PG_ARGISNULL(0))
4878 value = PG_GETARG_DATUM(0);
4887 elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
4888 if (!OidIsValid(elmtype))
4889 elog(ERROR, "could not determine data type of input");
4891 result = array_fill_internal(dims, NULL, value, isnull, elmtype, fcinfo);
4892 PG_RETURN_ARRAYTYPE_P(result);
4896 create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
4897 Oid elmtype, int dataoffset)
4901 result = (ArrayType *) palloc0(nbytes);
4902 SET_VARSIZE(result, nbytes);
4903 result->ndim = ndims;
4904 result->dataoffset = dataoffset;
4905 result->elemtype = elmtype;
4906 memcpy(ARR_DIMS(result), dimv, ndims * sizeof(int));
4907 memcpy(ARR_LBOUND(result), lbsv, ndims * sizeof(int));
4913 array_fill_internal(ArrayType *dims, ArrayType *lbs,
4914 Datum value, bool isnull, Oid elmtype,
4915 FunctionCallInfo fcinfo)
4926 ArrayMetaState *my_extra;
4931 if (ARR_NDIM(dims) != 1)
4933 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4934 errmsg("wrong number of array subscripts"),
4935 errdetail("Dimension array must be one dimensional.")));
4937 if (ARR_LBOUND(dims)[0] != 1)
4939 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4940 errmsg("wrong range of array subscripts"),
4941 errdetail("Lower bound of dimension array must be one.")));
4943 if (array_contains_nulls(dims))
4945 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
4946 errmsg("dimension values cannot be null")));
4948 dimv = (int *) ARR_DATA_PTR(dims);
4949 ndims = ARR_DIMS(dims)[0];
4951 if (ndims < 0) /* we do allow zero-dimension arrays */
4953 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4954 errmsg("invalid number of dimensions: %d", ndims)));
4957 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
4958 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
4963 if (ARR_NDIM(lbs) != 1)
4965 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4966 errmsg("wrong number of array subscripts"),
4967 errdetail("Dimension array must be one dimensional.")));
4969 if (ARR_LBOUND(lbs)[0] != 1)
4971 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4972 errmsg("wrong range of array subscripts"),
4973 errdetail("Lower bound of dimension array must be one.")));
4975 if (array_contains_nulls(lbs))
4977 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
4978 errmsg("dimension values cannot be null")));
4980 if (ARR_DIMS(lbs)[0] != ndims)
4982 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4983 errmsg("wrong number of array subscripts"),
4984 errdetail("Low bound array has different size than dimensions array.")));
4986 lbsv = (int *) ARR_DATA_PTR(lbs);
4992 for (i = 0; i < MAXDIM; i++)
4998 /* fast track for empty array */
5000 return construct_empty_array(elmtype);
5002 nitems = ArrayGetNItems(ndims, dimv);
5005 * We arrange to look up info about element type only once per series of
5006 * calls, assuming the element type doesn't change underneath us.
5008 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
5009 if (my_extra == NULL)
5011 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
5012 sizeof(ArrayMetaState));
5013 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
5014 my_extra->element_type = InvalidOid;
5017 if (my_extra->element_type != elmtype)
5019 /* Get info about element type */
5020 get_typlenbyvalalign(elmtype,
5022 &my_extra->typbyval,
5023 &my_extra->typalign);
5024 my_extra->element_type = elmtype;
5027 elmlen = my_extra->typlen;
5028 elmbyval = my_extra->typbyval;
5029 elmalign = my_extra->typalign;
5031 /* compute required space */
5039 /* make sure data is not toasted */
5041 value = PointerGetDatum(PG_DETOAST_DATUM(value));
5043 nbytes = att_addlength_datum(0, elmlen, value);
5044 nbytes = att_align_nominal(nbytes, elmalign);
5047 totbytes = nbytes * nitems;
5049 /* check for overflow of multiplication or total request */
5050 if (totbytes / nbytes != nitems ||
5051 !AllocSizeIsValid(totbytes))
5053 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
5054 errmsg("array size exceeds the maximum allowed (%d)",
5055 (int) MaxAllocSize)));
5058 * This addition can't overflow, but it might cause us to go past
5059 * MaxAllocSize. We leave it to palloc to complain in that case.
5061 totbytes += ARR_OVERHEAD_NONULLS(ndims);
5063 result = create_array_envelope(ndims, dimv, lbsv, totbytes,
5066 p = ARR_DATA_PTR(result);
5067 for (i = 0; i < nitems; i++)
5068 p += ArrayCastAndSet(value, elmlen, elmbyval, elmalign, p);
5075 dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
5076 nbytes = dataoffset;
5078 result = create_array_envelope(ndims, dimv, lbsv, nbytes,
5079 elmtype, dataoffset);
5081 /* create_array_envelope already zeroed the bitmap, so we're done */
5092 array_unnest(PG_FUNCTION_ARGS)
5099 char *elemdataptr; /* this moves with nextelem */
5100 bits8 *arraynullsptr; /* this does not */
5104 } array_unnest_fctx;
5106 FuncCallContext *funcctx;
5107 array_unnest_fctx *fctx;
5108 MemoryContext oldcontext;
5110 /* stuff done only on the first call of the function */
5111 if (SRF_IS_FIRSTCALL())
5115 /* create a function context for cross-call persistence */
5116 funcctx = SRF_FIRSTCALL_INIT();
5119 * switch to memory context appropriate for multiple function calls
5121 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
5124 * Get the array value and detoast if needed. We can't do this
5125 * earlier because if we have to detoast, we want the detoasted copy
5126 * to be in multi_call_memory_ctx, so it will go away when we're done
5127 * and not before. (If no detoast happens, we assume the originally
5128 * passed array will stick around till then.)
5130 arr = PG_GETARG_ARRAYTYPE_P(0);
5132 /* allocate memory for user context */
5133 fctx = (array_unnest_fctx *) palloc(sizeof(array_unnest_fctx));
5135 /* initialize state */
5138 fctx->numelems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
5140 fctx->elemdataptr = ARR_DATA_PTR(arr);
5141 fctx->arraynullsptr = ARR_NULLBITMAP(arr);
5143 get_typlenbyvalalign(ARR_ELEMTYPE(arr),
5148 funcctx->user_fctx = fctx;
5149 MemoryContextSwitchTo(oldcontext);
5152 /* stuff done on every call of the function */
5153 funcctx = SRF_PERCALL_SETUP();
5154 fctx = funcctx->user_fctx;
5156 if (fctx->nextelem < fctx->numelems)
5158 int offset = fctx->nextelem++;
5162 * Check for NULL array element
5164 if (array_get_isnull(fctx->arraynullsptr, offset))
5166 fcinfo->isnull = true;
5168 /* elemdataptr does not move */
5173 * OK, get the element
5175 char *ptr = fctx->elemdataptr;
5177 fcinfo->isnull = false;
5178 elem = ArrayCast(ptr, fctx->elmbyval, fctx->elmlen);
5181 * Advance elemdataptr over it
5183 ptr = att_addlength_pointer(ptr, fctx->elmlen, ptr);
5184 ptr = (char *) att_align_nominal(ptr, fctx->elmalign);
5185 fctx->elemdataptr = ptr;
5188 SRF_RETURN_NEXT(funcctx, elem);
5192 /* do when there is no more left */
5193 SRF_RETURN_DONE(funcctx);
5199 * array_replace/array_remove support
5201 * Find all array entries matching (not distinct from) search/search_isnull,
5202 * and delete them if remove is true, else replace them with
5203 * replace/replace_isnull. Comparisons are done using the specified
5204 * collation. fcinfo is passed only for caching purposes.
5207 array_replace_internal(ArrayType *array,
5208 Datum search, bool search_isnull,
5209 Datum replace, bool replace_isnull,
5210 bool remove, Oid collation,
5211 FunctionCallInfo fcinfo)
5231 bool changed = false;
5232 TypeCacheEntry *typentry;
5233 FunctionCallInfoData locfcinfo;
5235 element_type = ARR_ELEMTYPE(array);
5236 ndim = ARR_NDIM(array);
5237 dim = ARR_DIMS(array);
5238 nitems = ArrayGetNItems(ndim, dim);
5240 /* Return input array unmodified if it is empty */
5245 * We can't remove elements from multi-dimensional arrays, since the
5246 * result might not be rectangular.
5248 if (remove && ndim > 1)
5250 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5251 errmsg("removing elements from multidimensional arrays is not supported")));
5254 * We arrange to look up the equality function only once per series of
5255 * calls, assuming the element type doesn't change underneath us.
5257 typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
5258 if (typentry == NULL ||
5259 typentry->type_id != element_type)
5261 typentry = lookup_type_cache(element_type,
5262 TYPECACHE_EQ_OPR_FINFO);
5263 if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
5265 (errcode(ERRCODE_UNDEFINED_FUNCTION),
5266 errmsg("could not identify an equality operator for type %s",
5267 format_type_be(element_type))));
5268 fcinfo->flinfo->fn_extra = (void *) typentry;
5270 typlen = typentry->typlen;
5271 typbyval = typentry->typbyval;
5272 typalign = typentry->typalign;
5275 * Detoast values if they are toasted. The replacement value must be
5276 * detoasted for insertion into the result array, while detoasting the
5277 * search value only once saves cycles.
5282 search = PointerGetDatum(PG_DETOAST_DATUM(search));
5283 if (!replace_isnull)
5284 replace = PointerGetDatum(PG_DETOAST_DATUM(replace));
5287 /* Prepare to apply the comparison operator */
5288 InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
5289 collation, NULL, NULL);
5291 /* Allocate temporary arrays for new values */
5292 values = (Datum *) palloc(nitems * sizeof(Datum));
5293 nulls = (bool *) palloc(nitems * sizeof(bool));
5295 /* Loop over source data */
5296 arraydataptr = ARR_DATA_PTR(array);
5297 bitmap = ARR_NULLBITMAP(array);
5302 for (i = 0; i < nitems; i++)
5309 /* Get source element, checking for NULL */
5310 if (bitmap && (*bitmap & bitmask) == 0)
5313 /* If searching for NULL, we have a match */
5321 else if (!replace_isnull)
5323 values[nresult] = replace;
5332 elt = fetch_att(arraydataptr, typbyval, typlen);
5333 arraydataptr = att_addlength_datum(arraydataptr, typlen, elt);
5334 arraydataptr = (char *) att_align_nominal(arraydataptr, typalign);
5338 /* no match possible, keep element */
5339 values[nresult] = elt;
5344 * Apply the operator to the element pair
5346 locfcinfo.arg[0] = elt;
5347 locfcinfo.arg[1] = search;
5348 locfcinfo.argnull[0] = false;
5349 locfcinfo.argnull[1] = false;
5350 locfcinfo.isnull = false;
5351 oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
5354 /* no match, keep element */
5355 values[nresult] = elt;
5359 /* match, so replace or delete */
5365 values[nresult] = replace;
5366 isNull = replace_isnull;
5374 nulls[nresult] = isNull;
5379 /* Update total result size */
5380 nbytes = att_addlength_datum(nbytes, typlen, values[nresult]);
5381 nbytes = att_align_nominal(nbytes, typalign);
5382 /* check for overflow of total request */
5383 if (!AllocSizeIsValid(nbytes))
5385 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
5386 errmsg("array size exceeds the maximum allowed (%d)",
5387 (int) MaxAllocSize)));
5392 /* advance bitmap pointer if any */
5396 if (bitmask == 0x100)
5405 * If not changed just return the original array
5414 /* If all elements were removed return an empty array */
5419 return construct_empty_array(element_type);
5422 /* Allocate and initialize the result array */
5425 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nresult);
5426 nbytes += dataoffset;
5430 dataoffset = 0; /* marker for no null bitmap */
5431 nbytes += ARR_OVERHEAD_NONULLS(ndim);
5433 result = (ArrayType *) palloc0(nbytes);
5434 SET_VARSIZE(result, nbytes);
5435 result->ndim = ndim;
5436 result->dataoffset = dataoffset;
5437 result->elemtype = element_type;
5438 memcpy(ARR_DIMS(result), ARR_DIMS(array), 2 * ndim * sizeof(int));
5442 /* Adjust the result length */
5443 ARR_DIMS(result)[0] = nresult;
5446 /* Insert data into result array */
5447 CopyArrayEls(result,
5448 values, nulls, nresult,
5449 typlen, typbyval, typalign,
5459 * Remove any occurrences of an element from an array
5461 * If used on a multi-dimensional array this will raise an error.
5464 array_remove(PG_FUNCTION_ARGS)
5467 Datum search = PG_GETARG_DATUM(1);
5468 bool search_isnull = PG_ARGISNULL(1);
5470 if (PG_ARGISNULL(0))
5472 array = PG_GETARG_ARRAYTYPE_P(0);
5474 array = array_replace_internal(array,
5475 search, search_isnull,
5477 true, PG_GET_COLLATION(),
5479 PG_RETURN_ARRAYTYPE_P(array);
5483 * Replace any occurrences of an element in an array
5486 array_replace(PG_FUNCTION_ARGS)
5489 Datum search = PG_GETARG_DATUM(1);
5490 bool search_isnull = PG_ARGISNULL(1);
5491 Datum replace = PG_GETARG_DATUM(2);
5492 bool replace_isnull = PG_ARGISNULL(2);
5494 if (PG_ARGISNULL(0))
5496 array = PG_GETARG_ARRAYTYPE_P(0);
5498 array = array_replace_internal(array,
5499 search, search_isnull,
5500 replace, replace_isnull,
5501 false, PG_GET_COLLATION(),
5503 PG_RETURN_ARRAYTYPE_P(array);