1 /*-------------------------------------------------------------------------
4 * Support functions for arrays.
6 * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.129 2006/07/11 16:35:32 momjian Exp $
13 *-------------------------------------------------------------------------
19 #include "access/tupmacs.h"
20 #include "catalog/catalog.h"
21 #include "catalog/pg_type.h"
22 #include "libpq/pqformat.h"
23 #include "parser/parse_coerce.h"
24 #include "parser/parse_oper.h"
25 #include "utils/array.h"
26 #include "utils/builtins.h"
27 #include "utils/datum.h"
28 #include "utils/lsyscache.h"
29 #include "utils/memutils.h"
30 #include "utils/syscache.h"
31 #include "utils/typcache.h"
37 bool Array_nulls = true;
50 ARRAY_QUOTED_ELEM_STARTED,
51 ARRAY_QUOTED_ELEM_COMPLETED,
53 ARRAY_LEVEL_COMPLETED,
57 static int ArrayCount(const char *str, int *dim, char typdelim);
58 static void ReadArrayStr(char *arrayStr, const char *origStr,
59 int nitems, int ndim, int *dim,
60 FmgrInfo *inputproc, Oid typioparam, int32 typmod,
62 int typlen, bool typbyval, char typalign,
63 Datum *values, bool *nulls,
64 bool *hasnulls, int32 *nbytes);
65 static void ReadArrayBinary(StringInfo buf, int nitems,
66 FmgrInfo *receiveproc, Oid typioparam, int32 typmod,
67 int typlen, bool typbyval, char typalign,
68 Datum *values, bool *nulls,
69 bool *hasnulls, int32 *nbytes);
70 static void CopyArrayEls(ArrayType *array,
71 Datum *values, bool *nulls, int nitems,
72 int typlen, bool typbyval, char typalign,
74 static bool array_get_isnull(const bits8 *nullbitmap, int offset);
75 static void array_set_isnull(bits8 *nullbitmap, int offset, bool isNull);
76 static Datum ArrayCast(char *value, bool byval, int len);
77 static int ArrayCastAndSet(Datum src,
78 int typlen, bool typbyval, char typalign,
80 static char *array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
81 int typlen, bool typbyval, char typalign);
82 static int array_nelems_size(char *ptr, int offset, bits8 *nullbitmap,
83 int nitems, int typlen, bool typbyval, char typalign);
84 static int array_copy(char *destptr, int nitems,
85 char *srcptr, int offset, bits8 *nullbitmap,
86 int typlen, bool typbyval, char typalign);
87 static int array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
88 int ndim, int *dim, int *lb,
90 int typlen, bool typbyval, char typalign);
91 static void array_extract_slice(ArrayType *newarray,
92 int ndim, int *dim, int *lb,
93 char *arraydataptr, bits8 *arraynullsptr,
95 int typlen, bool typbyval, char typalign);
96 static void array_insert_slice(ArrayType *destArray, ArrayType *origArray,
98 int ndim, int *dim, int *lb,
100 int typlen, bool typbyval, char typalign);
101 static int array_cmp(FunctionCallInfo fcinfo);
102 static Datum array_type_length_coerce_internal(ArrayType *src,
105 FmgrInfo *fmgr_info);
110 * converts an array from the external format in "string" to
111 * its internal format.
114 * the internal representation of the input array
117 array_in(PG_FUNCTION_ARGS)
119 char *string = PG_GETARG_CSTRING(0); /* external form */
120 Oid element_type = PG_GETARG_OID(1); /* type of an array
122 int32 typmod = PG_GETARG_INT32(2); /* typmod for array elements */
141 ArrayMetaState *my_extra;
144 * We arrange to look up info about element type, including its input
145 * conversion proc, only once per series of calls, assuming the element
146 * type doesn't change underneath us.
148 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
149 if (my_extra == NULL)
151 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
152 sizeof(ArrayMetaState));
153 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
154 my_extra->element_type = ~element_type;
157 if (my_extra->element_type != element_type)
160 * Get info about element type, including its input conversion proc
162 get_type_io_data(element_type, IOFunc_input,
163 &my_extra->typlen, &my_extra->typbyval,
164 &my_extra->typalign, &my_extra->typdelim,
165 &my_extra->typioparam, &my_extra->typiofunc);
166 fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
167 fcinfo->flinfo->fn_mcxt);
168 my_extra->element_type = element_type;
170 typlen = my_extra->typlen;
171 typbyval = my_extra->typbyval;
172 typalign = my_extra->typalign;
173 typdelim = my_extra->typdelim;
174 typioparam = my_extra->typioparam;
176 /* Make a modifiable copy of the input */
177 string_save = pstrdup(string);
180 * If the input string starts with dimension info, read and use that.
181 * Otherwise, we require the input to be in curly-brace style, and we
182 * prescan the input to determine dimensions.
184 * Dimension info takes the form of one or more [n] or [m:n] items. The
185 * outer loop iterates once per dimension item.
195 * Note: we currently allow whitespace between, but not within,
198 while (isspace((unsigned char) *p))
201 break; /* no more dimension items */
205 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
206 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
209 for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++);
210 if (q == p) /* no digits? */
212 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
213 errmsg("missing dimension value")));
219 lBound[ndim] = atoi(p);
221 for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++);
222 if (q == p) /* no digits? */
224 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
225 errmsg("missing dimension value")));
234 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
235 errmsg("missing \"]\" in array dimensions")));
240 if (ub < lBound[ndim])
242 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
243 errmsg("upper bound cannot be less than lower bound")));
245 dim[ndim] = ub - lBound[ndim] + 1;
251 /* No array dimensions, so intuit dimensions from brace structure */
254 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
255 errmsg("array value must start with \"{\" or dimension information")));
256 ndim = ArrayCount(p, dim, typdelim);
257 for (i = 0; i < ndim; i++)
265 /* If array dimensions are given, expect '=' operator */
266 if (strncmp(p, ASSGN, strlen(ASSGN)) != 0)
268 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
269 errmsg("missing assignment operator")));
271 while (isspace((unsigned char) *p))
275 * intuit dimensions from brace structure -- it better match what we
280 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
281 errmsg("array value must start with \"{\" or dimension information")));
282 ndim_braces = ArrayCount(p, dim_braces, typdelim);
283 if (ndim_braces != ndim)
285 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
286 errmsg("array dimensions incompatible with array literal")));
287 for (i = 0; i < ndim; ++i)
289 if (dim[i] != dim_braces[i])
291 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
292 errmsg("array dimensions incompatible with array literal")));
297 printf("array_in- ndim %d (", ndim);
298 for (i = 0; i < ndim; i++)
300 printf(" %d", dim[i]);
302 printf(") for %s\n", string);
305 /* This checks for overflow of the array dimensions */
306 nitems = ArrayGetNItems(ndim, dim);
309 PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
311 dataPtr = (Datum *) palloc(nitems * sizeof(Datum));
312 nullsPtr = (bool *) palloc(nitems * sizeof(bool));
313 ReadArrayStr(p, string,
315 &my_extra->proc, typioparam, typmod,
317 typlen, typbyval, typalign,
322 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
323 nbytes += dataoffset;
327 dataoffset = 0; /* marker for no null bitmap */
328 nbytes += ARR_OVERHEAD_NONULLS(ndim);
330 retval = (ArrayType *) palloc(nbytes);
331 retval->size = nbytes;
333 retval->dataoffset = dataoffset;
334 retval->elemtype = element_type;
335 memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
336 memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
339 dataPtr, nullsPtr, nitems,
340 typlen, typbyval, typalign,
347 PG_RETURN_ARRAYTYPE_P(retval);
352 * Determines the dimensions for an array string.
354 * Returns number of dimensions as function result. The axis lengths are
355 * returned in dim[], which must be of size MAXDIM.
358 ArrayCount(const char *str, int *dim, char typdelim)
366 bool in_quotes = false;
367 bool eoArray = false;
368 bool empty_array = true;
370 ArrayParseState parse_state = ARRAY_NO_LEVEL;
372 for (i = 0; i < MAXDIM; ++i)
374 temp[i] = dim[i] = 0;
375 nelems_last[i] = nelems[i] = 1;
381 bool itemdone = false;
385 if (parse_state == ARRAY_ELEM_STARTED ||
386 parse_state == ARRAY_QUOTED_ELEM_STARTED)
392 /* Signal a premature end of the string */
394 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
395 errmsg("malformed array literal: \"%s\"", str)));
400 * An escape must be after a level start, after an element
401 * start, or after an element delimiter. In any case we
402 * now must be past an element start.
404 if (parse_state != ARRAY_LEVEL_STARTED &&
405 parse_state != ARRAY_ELEM_STARTED &&
406 parse_state != ARRAY_QUOTED_ELEM_STARTED &&
407 parse_state != ARRAY_ELEM_DELIMITED)
409 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
410 errmsg("malformed array literal: \"%s\"", str)));
411 if (parse_state != ARRAY_QUOTED_ELEM_STARTED)
412 parse_state = ARRAY_ELEM_STARTED;
413 /* skip the escaped character */
418 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
419 errmsg("malformed array literal: \"%s\"", str)));
424 * A quote must be after a level start, after a quoted
425 * element start, or after an element delimiter. In any
426 * case we now must be past an element start.
428 if (parse_state != ARRAY_LEVEL_STARTED &&
429 parse_state != ARRAY_QUOTED_ELEM_STARTED &&
430 parse_state != ARRAY_ELEM_DELIMITED)
432 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
433 errmsg("malformed array literal: \"%s\"", str)));
434 in_quotes = !in_quotes;
436 parse_state = ARRAY_QUOTED_ELEM_STARTED;
438 parse_state = ARRAY_QUOTED_ELEM_COMPLETED;
444 * A left brace can occur if no nesting has occurred
445 * yet, after a level start, or after a level
448 if (parse_state != ARRAY_NO_LEVEL &&
449 parse_state != ARRAY_LEVEL_STARTED &&
450 parse_state != ARRAY_LEVEL_DELIMITED)
452 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
453 errmsg("malformed array literal: \"%s\"", str)));
454 parse_state = ARRAY_LEVEL_STARTED;
455 if (nest_level >= MAXDIM)
457 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
458 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
459 nest_level, MAXDIM)));
460 temp[nest_level] = 0;
462 if (ndim < nest_level)
470 * A right brace can occur after an element start, an
471 * element completion, a quoted element completion, or
472 * a level completion.
474 if (parse_state != ARRAY_ELEM_STARTED &&
475 parse_state != ARRAY_ELEM_COMPLETED &&
476 parse_state != ARRAY_QUOTED_ELEM_COMPLETED &&
477 parse_state != ARRAY_LEVEL_COMPLETED &&
478 !(nest_level == 1 && parse_state == ARRAY_LEVEL_STARTED))
480 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
481 errmsg("malformed array literal: \"%s\"", str)));
482 parse_state = ARRAY_LEVEL_COMPLETED;
485 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
486 errmsg("malformed array literal: \"%s\"", str)));
489 if ((nelems_last[nest_level] != 1) &&
490 (nelems[nest_level] != nelems_last[nest_level]))
492 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
493 errmsg("multidimensional arrays must have "
494 "array expressions with matching "
496 nelems_last[nest_level] = nelems[nest_level];
497 nelems[nest_level] = 1;
499 eoArray = itemdone = true;
503 * We don't set itemdone here; see comments in
506 temp[nest_level - 1]++;
513 if (*ptr == typdelim)
516 * Delimiters can occur after an element start, an
517 * element completion, a quoted element
518 * completion, or a level completion.
520 if (parse_state != ARRAY_ELEM_STARTED &&
521 parse_state != ARRAY_ELEM_COMPLETED &&
522 parse_state != ARRAY_QUOTED_ELEM_COMPLETED &&
523 parse_state != ARRAY_LEVEL_COMPLETED)
525 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
526 errmsg("malformed array literal: \"%s\"", str)));
527 if (parse_state == ARRAY_LEVEL_COMPLETED)
528 parse_state = ARRAY_LEVEL_DELIMITED;
530 parse_state = ARRAY_ELEM_DELIMITED;
532 nelems[nest_level - 1]++;
534 else if (!isspace((unsigned char) *ptr))
537 * Other non-space characters must be after a
538 * level start, after an element start, or after
539 * an element delimiter. In any case we now must
540 * be past an element start.
542 if (parse_state != ARRAY_LEVEL_STARTED &&
543 parse_state != ARRAY_ELEM_STARTED &&
544 parse_state != ARRAY_ELEM_DELIMITED)
546 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
547 errmsg("malformed array literal: \"%s\"", str)));
548 parse_state = ARRAY_ELEM_STARTED;
560 /* only whitespace is allowed after the closing brace */
563 if (!isspace((unsigned char) *ptr++))
565 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
566 errmsg("malformed array literal: \"%s\"", str)));
569 /* special case for an empty array */
573 for (i = 0; i < ndim; ++i)
581 * parses the array string pointed to by "arrayStr" and converts the values
582 * to internal format. Unspecified elements are initialized to nulls.
583 * The array dimensions must already have been determined.
586 * arrayStr: the string to parse.
587 * CAUTION: the contents of "arrayStr" will be modified!
588 * origStr: the unmodified input string, used only in error messages.
589 * nitems: total number of array elements, as already determined.
590 * ndim: number of array dimensions
591 * dim[]: array axis lengths
592 * inputproc: type-specific input procedure for element datatype.
593 * typioparam, typmod: auxiliary values to pass to inputproc.
594 * typdelim: the value delimiter (type-specific).
595 * typlen, typbyval, typalign: storage parameters of element datatype.
598 * values[]: filled with converted data values.
599 * nulls[]: filled with is-null markers.
600 * *hasnulls: set TRUE iff there are any null elements.
601 * *nbytes: set to total size of data area needed (including alignment
602 * padding but not including array header overhead).
604 * Note that values[] and nulls[] are allocated by the caller, and must have
608 ReadArrayStr(char *arrayStr,
628 bool in_quotes = false;
629 bool eoArray = false;
635 mda_get_prod(ndim, dim, prod);
636 MemSet(indx, 0, sizeof(indx));
638 /* Initialize is-null markers to true */
639 memset(nulls, true, nitems * sizeof(bool));
642 * We have to remove " and \ characters to create a clean item value to
643 * pass to the datatype input routine. We overwrite each item value
644 * in-place within arrayStr to do this. srcptr is the current scan point,
645 * and dstptr is where we are copying to.
647 * We also want to suppress leading and trailing unquoted whitespace. We
648 * use the leadingspace flag to suppress leading space. Trailing space is
649 * tracked by using dstendptr to point to the last significant output
652 * The error checking in this routine is mostly pro-forma, since we expect
653 * that ArrayCount() already validated the string.
658 bool itemdone = false;
659 bool leadingspace = true;
660 bool hasquoting = false;
666 itemstart = dstptr = dstendptr = srcptr;
673 /* Signal a premature end of the string */
675 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
676 errmsg("malformed array literal: \"%s\"",
680 /* Skip backslash, copy next character as-is. */
684 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
685 errmsg("malformed array literal: \"%s\"",
687 *dstptr++ = *srcptr++;
688 /* Treat the escaped character as non-whitespace */
689 leadingspace = false;
691 hasquoting = true; /* can't be a NULL marker */
694 in_quotes = !in_quotes;
696 leadingspace = false;
700 * Advance dstendptr when we exit in_quotes; this
701 * saves having to do it in all the other in_quotes
706 hasquoting = true; /* can't be a NULL marker */
712 if (nest_level >= ndim)
714 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
715 errmsg("malformed array literal: \"%s\"",
718 indx[nest_level - 1] = 0;
722 *dstptr++ = *srcptr++;
729 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
730 errmsg("malformed array literal: \"%s\"",
733 i = ArrayGetOffset0(ndim, indx, prod);
734 indx[nest_level - 1] = 0;
737 eoArray = itemdone = true;
739 indx[nest_level - 1]++;
743 *dstptr++ = *srcptr++;
747 *dstptr++ = *srcptr++;
748 else if (*srcptr == typdelim)
751 i = ArrayGetOffset0(ndim, indx, prod);
756 else if (isspace((unsigned char) *srcptr))
759 * If leading space, drop it immediately. Else, copy
760 * but don't advance dstendptr.
765 *dstptr++ = *srcptr++;
769 *dstptr++ = *srcptr++;
770 leadingspace = false;
777 Assert(dstptr < srcptr);
780 if (i < 0 || i >= nitems)
782 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
783 errmsg("malformed array literal: \"%s\"",
786 if (Array_nulls && !hasquoting &&
787 pg_strcasecmp(itemstart, "NULL") == 0)
789 /* it's a NULL item */
790 values[i] = InputFunctionCall(inputproc, NULL,
796 values[i] = InputFunctionCall(inputproc, itemstart,
803 * Check for nulls, compute total data space needed
807 for (i = 0; i < nitems; i++)
813 /* let's just make sure data is not toasted */
815 values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
816 totbytes = att_addlength(totbytes, typlen, values[i]);
817 totbytes = att_align(totbytes, typalign);
818 /* check for overflow of total request */
819 if (!AllocSizeIsValid(totbytes))
821 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
822 errmsg("array size exceeds the maximum allowed (%d)",
823 (int) MaxAllocSize)));
832 * Copy data into an array object from a temporary array of Datums.
834 * array: array object (with header fields already filled in)
835 * values: array of Datums to be copied
836 * nulls: array of is-null flags (can be NULL if no nulls)
837 * nitems: number of Datums to be copied
838 * typbyval, typlen, typalign: info about element datatype
839 * freedata: if TRUE and element type is pass-by-ref, pfree data values
840 * referenced by Datums after copying them.
842 * If the input data is of varlena type, the caller must have ensured that
843 * the values are not toasted. (Doing it here doesn't work since the
844 * caller has already allocated space for the array...)
847 CopyArrayEls(ArrayType *array,
856 char *p = ARR_DATA_PTR(array);
857 bits8 *bitmap = ARR_NULLBITMAP(array);
865 for (i = 0; i < nitems; i++)
867 if (nulls && nulls[i])
869 if (!bitmap) /* shouldn't happen */
870 elog(ERROR, "null array element where not supported");
871 /* bitmap bit stays 0 */
876 p += ArrayCastAndSet(values[i], typlen, typbyval, typalign, p);
878 pfree(DatumGetPointer(values[i]));
883 if (bitmask == 0x100)
892 if (bitmap && bitmask != 1)
898 * takes the internal representation of an array and returns a string
899 * containing the array in its external format.
902 array_out(PG_FUNCTION_ARGS)
904 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
905 Oid element_type = ARR_ELEMTYPE(v);
914 dims_str[(MAXDIM * 33) + 2];
917 * 33 per dim since we assume 15 digits per number + ':' +'[]'
919 * +2 allows for assignment operator + trailing null
934 ArrayMetaState *my_extra;
937 * We arrange to look up info about element type, including its output
938 * conversion proc, only once per series of calls, assuming the element
939 * type doesn't change underneath us.
941 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
942 if (my_extra == NULL)
944 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
945 sizeof(ArrayMetaState));
946 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
947 my_extra->element_type = ~element_type;
950 if (my_extra->element_type != element_type)
953 * Get info about element type, including its output conversion proc
955 get_type_io_data(element_type, IOFunc_output,
956 &my_extra->typlen, &my_extra->typbyval,
957 &my_extra->typalign, &my_extra->typdelim,
958 &my_extra->typioparam, &my_extra->typiofunc);
959 fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
960 fcinfo->flinfo->fn_mcxt);
961 my_extra->element_type = element_type;
963 typlen = my_extra->typlen;
964 typbyval = my_extra->typbyval;
965 typalign = my_extra->typalign;
966 typdelim = my_extra->typdelim;
971 nitems = ArrayGetNItems(ndim, dims);
975 retval = pstrdup("{}");
976 PG_RETURN_CSTRING(retval);
980 * we will need to add explicit dimensions if any dimension has a lower
981 * bound other than one
983 for (i = 0; i < ndim; i++)
993 * Convert all values to string form, count total space needed (including
994 * any overhead such as escaping backslashes), and detect whether each
995 * item needs double quotes.
997 values = (char **) palloc(nitems * sizeof(char *));
998 needquotes = (bool *) palloc(nitems * sizeof(bool));
999 overall_length = 1; /* don't forget to count \0 at end. */
1001 p = ARR_DATA_PTR(v);
1002 bitmap = ARR_NULLBITMAP(v);
1005 for (i = 0; i < nitems; i++)
1009 /* Get source element, checking for NULL */
1010 if (bitmap && (*bitmap & bitmask) == 0)
1012 values[i] = pstrdup("NULL");
1013 overall_length += 4;
1020 itemvalue = fetch_att(p, typbyval, typlen);
1021 values[i] = OutputFunctionCall(&my_extra->proc, itemvalue);
1022 p = att_addlength(p, typlen, PointerGetDatum(p));
1023 p = (char *) att_align(p, typalign);
1025 /* count data plus backslashes; detect chars needing quotes */
1026 if (values[i][0] == '\0')
1027 needquote = true; /* force quotes for empty string */
1028 else if (pg_strcasecmp(values[i], "NULL") == 0)
1029 needquote = true; /* force quotes for literal NULL */
1033 for (tmp = values[i]; *tmp != '\0'; tmp++)
1037 overall_length += 1;
1038 if (ch == '"' || ch == '\\')
1042 overall_length += 1;
1045 else if (ch == '{' || ch == '}' || ch == typdelim ||
1046 isspace((unsigned char) ch))
1051 needquotes[i] = needquote;
1053 /* Count the pair of double quotes, if needed */
1055 overall_length += 2;
1057 overall_length += 1;
1059 /* advance bitmap pointer if any */
1063 if (bitmask == 0x100)
1072 * count total number of curly braces in output string
1074 for (i = j = 0, k = 1; i < ndim; i++)
1075 k *= dims[i], j += k;
1079 /* add explicit dimensions if required */
1082 char *ptr = dims_str;
1084 for (i = 0; i < ndim; i++)
1086 sprintf(ptr, "[%d:%d]", lb[i], lb[i] + dims[i] - 1);
1093 retval = (char *) palloc(strlen(dims_str) + overall_length + 2 * j);
1096 #define APPENDSTR(str) (strcpy(p, (str)), p += strlen(p))
1097 #define APPENDCHAR(ch) (*p++ = (ch), *p = '\0')
1100 APPENDSTR(dims_str);
1102 for (i = 0; i < ndim; i++)
1108 for (i = j; i < ndim - 1; i++)
1115 for (tmp = values[k]; *tmp; tmp++)
1119 if (ch == '"' || ch == '\\')
1125 APPENDSTR(values[k]);
1130 APPENDSTR(values[k]);
1133 for (i = ndim - 1; i >= 0; i--)
1135 indx[i] = (indx[i] + 1) % dims[i];
1138 APPENDCHAR(typdelim);
1153 PG_RETURN_CSTRING(retval);
1158 * converts an array from the external binary format to
1159 * its internal format.
1162 * the internal representation of the input array
1165 array_recv(PG_FUNCTION_ARGS)
1167 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
1168 Oid spec_element_type = PG_GETARG_OID(1); /* type of an array
1170 int32 typmod = PG_GETARG_INT32(2); /* typmod for array elements */
1188 ArrayMetaState *my_extra;
1190 /* Get the array header information */
1191 ndim = pq_getmsgint(buf, 4);
1192 if (ndim < 0) /* we do allow zero-dimension arrays */
1194 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1195 errmsg("invalid number of dimensions: %d", ndim)));
1198 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1199 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
1202 flags = pq_getmsgint(buf, 4);
1203 if (flags != 0 && flags != 1)
1205 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1206 errmsg("invalid array flags")));
1208 element_type = pq_getmsgint(buf, sizeof(Oid));
1209 if (element_type != spec_element_type)
1211 /* XXX Can we allow taking the input element type in any cases? */
1213 (errcode(ERRCODE_DATATYPE_MISMATCH),
1214 errmsg("wrong element type")));
1217 for (i = 0; i < ndim; i++)
1219 dim[i] = pq_getmsgint(buf, 4);
1220 lBound[i] = pq_getmsgint(buf, 4);
1223 /* This checks for overflow of array dimensions */
1224 nitems = ArrayGetNItems(ndim, dim);
1227 * We arrange to look up info about element type, including its receive
1228 * conversion proc, only once per series of calls, assuming the element
1229 * type doesn't change underneath us.
1231 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1232 if (my_extra == NULL)
1234 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1235 sizeof(ArrayMetaState));
1236 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1237 my_extra->element_type = ~element_type;
1240 if (my_extra->element_type != element_type)
1242 /* Get info about element type, including its receive proc */
1243 get_type_io_data(element_type, IOFunc_receive,
1244 &my_extra->typlen, &my_extra->typbyval,
1245 &my_extra->typalign, &my_extra->typdelim,
1246 &my_extra->typioparam, &my_extra->typiofunc);
1247 if (!OidIsValid(my_extra->typiofunc))
1249 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1250 errmsg("no binary input function available for type %s",
1251 format_type_be(element_type))));
1252 fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1253 fcinfo->flinfo->fn_mcxt);
1254 my_extra->element_type = element_type;
1259 /* Return empty array ... but not till we've validated element_type */
1260 PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
1263 typlen = my_extra->typlen;
1264 typbyval = my_extra->typbyval;
1265 typalign = my_extra->typalign;
1266 typioparam = my_extra->typioparam;
1268 dataPtr = (Datum *) palloc(nitems * sizeof(Datum));
1269 nullsPtr = (bool *) palloc(nitems * sizeof(bool));
1270 ReadArrayBinary(buf, nitems,
1271 &my_extra->proc, typioparam, typmod,
1272 typlen, typbyval, typalign,
1274 &hasnulls, &nbytes);
1277 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
1278 nbytes += dataoffset;
1282 dataoffset = 0; /* marker for no null bitmap */
1283 nbytes += ARR_OVERHEAD_NONULLS(ndim);
1285 retval = (ArrayType *) palloc(nbytes);
1286 retval->size = nbytes;
1287 retval->ndim = ndim;
1288 retval->dataoffset = dataoffset;
1289 retval->elemtype = element_type;
1290 memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
1291 memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
1293 CopyArrayEls(retval,
1294 dataPtr, nullsPtr, nitems,
1295 typlen, typbyval, typalign,
1301 PG_RETURN_ARRAYTYPE_P(retval);
1306 * collect the data elements of an array being read in binary style.
1309 * buf: the data buffer to read from.
1310 * nitems: total number of array elements (already read).
1311 * receiveproc: type-specific receive procedure for element datatype.
1312 * typioparam, typmod: auxiliary values to pass to receiveproc.
1313 * typlen, typbyval, typalign: storage parameters of element datatype.
1316 * values[]: filled with converted data values.
1317 * nulls[]: filled with is-null markers.
1318 * *hasnulls: set TRUE iff there are any null elements.
1319 * *nbytes: set to total size of data area needed (including alignment
1320 * padding but not including array header overhead).
1322 * Note that values[] and nulls[] are allocated by the caller, and must have
1326 ReadArrayBinary(StringInfo buf,
1328 FmgrInfo *receiveproc,
1343 for (i = 0; i < nitems; i++)
1346 StringInfoData elem_buf;
1349 /* Get and check the item length */
1350 itemlen = pq_getmsgint(buf, 4);
1351 if (itemlen < -1 || itemlen > (buf->len - buf->cursor))
1353 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1354 errmsg("insufficient data left in message")));
1358 /* -1 length means NULL */
1359 values[i] = ReceiveFunctionCall(receiveproc, NULL,
1360 typioparam, typmod);
1366 * Rather than copying data around, we just set up a phony StringInfo
1367 * pointing to the correct portion of the input buffer. We assume we
1368 * can scribble on the input buffer so as to maintain the convention
1369 * that StringInfos have a trailing null.
1371 elem_buf.data = &buf->data[buf->cursor];
1372 elem_buf.maxlen = itemlen + 1;
1373 elem_buf.len = itemlen;
1374 elem_buf.cursor = 0;
1376 buf->cursor += itemlen;
1378 csave = buf->data[buf->cursor];
1379 buf->data[buf->cursor] = '\0';
1381 /* Now call the element's receiveproc */
1382 values[i] = ReceiveFunctionCall(receiveproc, &elem_buf,
1383 typioparam, typmod);
1386 /* Trouble if it didn't eat the whole buffer */
1387 if (elem_buf.cursor != itemlen)
1389 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1390 errmsg("improper binary format in array element %d",
1393 buf->data[buf->cursor] = csave;
1397 * Check for nulls, compute total data space needed
1401 for (i = 0; i < nitems; i++)
1407 /* let's just make sure data is not toasted */
1409 values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
1410 totbytes = att_addlength(totbytes, typlen, values[i]);
1411 totbytes = att_align(totbytes, typalign);
1412 /* check for overflow of total request */
1413 if (!AllocSizeIsValid(totbytes))
1415 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1416 errmsg("array size exceeds the maximum allowed (%d)",
1417 (int) MaxAllocSize)));
1420 *hasnulls = hasnull;
1427 * takes the internal representation of an array and returns a bytea
1428 * containing the array in its external binary format.
1431 array_send(PG_FUNCTION_ARGS)
1433 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1434 Oid element_type = ARR_ELEMTYPE(v);
1446 ArrayMetaState *my_extra;
1449 * We arrange to look up info about element type, including its send
1450 * conversion proc, only once per series of calls, assuming the element
1451 * type doesn't change underneath us.
1453 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1454 if (my_extra == NULL)
1456 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1457 sizeof(ArrayMetaState));
1458 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1459 my_extra->element_type = ~element_type;
1462 if (my_extra->element_type != element_type)
1464 /* Get info about element type, including its send proc */
1465 get_type_io_data(element_type, IOFunc_send,
1466 &my_extra->typlen, &my_extra->typbyval,
1467 &my_extra->typalign, &my_extra->typdelim,
1468 &my_extra->typioparam, &my_extra->typiofunc);
1469 if (!OidIsValid(my_extra->typiofunc))
1471 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1472 errmsg("no binary output function available for type %s",
1473 format_type_be(element_type))));
1474 fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1475 fcinfo->flinfo->fn_mcxt);
1476 my_extra->element_type = element_type;
1478 typlen = my_extra->typlen;
1479 typbyval = my_extra->typbyval;
1480 typalign = my_extra->typalign;
1484 nitems = ArrayGetNItems(ndim, dim);
1486 pq_begintypsend(&buf);
1488 /* Send the array header information */
1489 pq_sendint(&buf, ndim, 4);
1490 pq_sendint(&buf, ARR_HASNULL(v) ? 1 : 0, 4);
1491 pq_sendint(&buf, element_type, sizeof(Oid));
1492 for (i = 0; i < ndim; i++)
1494 pq_sendint(&buf, ARR_DIMS(v)[i], 4);
1495 pq_sendint(&buf, ARR_LBOUND(v)[i], 4);
1498 /* Send the array elements using the element's own sendproc */
1499 p = ARR_DATA_PTR(v);
1500 bitmap = ARR_NULLBITMAP(v);
1503 for (i = 0; i < nitems; i++)
1505 /* Get source element, checking for NULL */
1506 if (bitmap && (*bitmap & bitmask) == 0)
1508 /* -1 length means a NULL */
1509 pq_sendint(&buf, -1, 4);
1516 itemvalue = fetch_att(p, typbyval, typlen);
1517 outputbytes = SendFunctionCall(&my_extra->proc, itemvalue);
1518 pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
1519 pq_sendbytes(&buf, VARDATA(outputbytes),
1520 VARSIZE(outputbytes) - VARHDRSZ);
1523 p = att_addlength(p, typlen, PointerGetDatum(p));
1524 p = (char *) att_align(p, typalign);
1527 /* advance bitmap pointer if any */
1531 if (bitmask == 0x100)
1539 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
1544 * returns the dimensions of the array pointed to by "v", as a "text"
1547 array_dims(PG_FUNCTION_ARGS)
1549 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1557 /* Sanity check: does it look like an array at all? */
1558 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1561 nbytes = ARR_NDIM(v) * 33 + 1;
1564 * 33 since we assume 15 digits per number + ':' +'[]'
1566 * +1 allows for temp trailing null
1569 result = (text *) palloc(nbytes + VARHDRSZ);
1570 p = VARDATA(result);
1575 for (i = 0; i < ARR_NDIM(v); i++)
1577 sprintf(p, "[%d:%d]", lb[i], dimv[i] + lb[i] - 1);
1580 VARATT_SIZEP(result) = strlen(VARDATA(result)) + VARHDRSZ;
1582 PG_RETURN_TEXT_P(result);
1587 * returns the lower dimension, of the DIM requested, for
1588 * the array pointed to by "v", as an int4
1591 array_lower(PG_FUNCTION_ARGS)
1593 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1594 int reqdim = PG_GETARG_INT32(1);
1598 /* Sanity check: does it look like an array at all? */
1599 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1602 /* Sanity check: was the requested dim valid */
1603 if (reqdim <= 0 || reqdim > ARR_NDIM(v))
1607 result = lb[reqdim - 1];
1609 PG_RETURN_INT32(result);
1614 * returns the upper dimension, of the DIM requested, for
1615 * the array pointed to by "v", as an int4
1618 array_upper(PG_FUNCTION_ARGS)
1620 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1621 int reqdim = PG_GETARG_INT32(1);
1626 /* Sanity check: does it look like an array at all? */
1627 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1630 /* Sanity check: was the requested dim valid */
1631 if (reqdim <= 0 || reqdim > ARR_NDIM(v))
1637 result = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
1639 PG_RETURN_INT32(result);
1644 * This routine takes an array pointer and a subscript array and returns
1645 * the referenced item as a Datum. Note that for a pass-by-reference
1646 * datatype, the returned Datum is a pointer into the array object.
1648 * This handles both ordinary varlena arrays and fixed-length arrays.
1651 * array: the array object (mustn't be NULL)
1652 * nSubscripts: number of subscripts supplied
1653 * indx[]: the subscript values
1654 * arraytyplen: pg_type.typlen for the array type
1655 * elmlen: pg_type.typlen for the array's element type
1656 * elmbyval: pg_type.typbyval for the array's element type
1657 * elmalign: pg_type.typalign for the array's element type
1660 * The return value is the element Datum.
1661 * *isNull is set to indicate whether the element is NULL.
1664 array_ref(ArrayType *array,
1682 bits8 *arraynullsptr;
1684 if (arraytyplen > 0)
1687 * fixed-length arrays -- these are assumed to be 1-d, 0-based
1690 fixedDim[0] = arraytyplen / elmlen;
1694 arraydataptr = (char *) array;
1695 arraynullsptr = NULL;
1699 /* detoast input array if necessary */
1700 array = DatumGetArrayTypeP(PointerGetDatum(array));
1702 ndim = ARR_NDIM(array);
1703 dim = ARR_DIMS(array);
1704 lb = ARR_LBOUND(array);
1705 arraydataptr = ARR_DATA_PTR(array);
1706 arraynullsptr = ARR_NULLBITMAP(array);
1710 * Return NULL for invalid subscript
1712 if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
1717 for (i = 0; i < ndim; i++)
1719 if (indx[i] < lb[i] || indx[i] >= (dim[i] + lb[i]))
1727 * Calculate the element number
1729 offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
1732 * Check for NULL array element
1734 if (array_get_isnull(arraynullsptr, offset))
1741 * OK, get the element
1744 retptr = array_seek(arraydataptr, 0, arraynullsptr, offset,
1745 elmlen, elmbyval, elmalign);
1746 return ArrayCast(retptr, elmbyval, elmlen);
1751 * This routine takes an array and a range of indices (upperIndex and
1752 * lowerIndx), creates a new array structure for the referred elements
1753 * and returns a pointer to it.
1755 * This handles both ordinary varlena arrays and fixed-length arrays.
1758 * array: the array object (mustn't be NULL)
1759 * nSubscripts: number of subscripts supplied (must be same for upper/lower)
1760 * upperIndx[]: the upper subscript values
1761 * lowerIndx[]: the lower subscript values
1762 * arraytyplen: pg_type.typlen for the array type
1763 * elmlen: pg_type.typlen for the array's element type
1764 * elmbyval: pg_type.typbyval for the array's element type
1765 * elmalign: pg_type.typalign for the array's element type
1768 * The return value is the new array Datum (it's never NULL)
1770 * NOTE: we assume it is OK to scribble on the provided subscript arrays
1771 * lowerIndx[] and upperIndx[]. These are generally just temporaries.
1774 array_get_slice(ArrayType *array,
1783 ArrayType *newarray;
1793 bits8 *arraynullsptr;
1798 if (arraytyplen > 0)
1801 * fixed-length arrays -- currently, cannot slice these because parser
1802 * labels output as being of the fixed-length array type! Code below
1803 * shows how we could support it if the parser were changed to label
1804 * output as a suitable varlena array type.
1807 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1808 errmsg("slices of fixed-length arrays not implemented")));
1811 * fixed-length arrays -- these are assumed to be 1-d, 0-based
1813 * XXX where would we get the correct ELEMTYPE from?
1816 fixedDim[0] = arraytyplen / elmlen;
1820 elemtype = InvalidOid; /* XXX */
1821 arraydataptr = (char *) array;
1822 arraynullsptr = NULL;
1826 /* detoast input array if necessary */
1827 array = DatumGetArrayTypeP(PointerGetDatum(array));
1829 ndim = ARR_NDIM(array);
1830 dim = ARR_DIMS(array);
1831 lb = ARR_LBOUND(array);
1832 elemtype = ARR_ELEMTYPE(array);
1833 arraydataptr = ARR_DATA_PTR(array);
1834 arraynullsptr = ARR_NULLBITMAP(array);
1838 * Check provided subscripts. A slice exceeding the current array limits
1839 * is silently truncated to the array limits. If we end up with an empty
1840 * slice, return an empty array.
1842 if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
1843 return construct_empty_array(elemtype);
1845 for (i = 0; i < nSubscripts; i++)
1847 if (lowerIndx[i] < lb[i])
1848 lowerIndx[i] = lb[i];
1849 if (upperIndx[i] >= (dim[i] + lb[i]))
1850 upperIndx[i] = dim[i] + lb[i] - 1;
1851 if (lowerIndx[i] > upperIndx[i])
1852 return construct_empty_array(elemtype);
1854 /* fill any missing subscript positions with full array range */
1855 for (; i < ndim; i++)
1857 lowerIndx[i] = lb[i];
1858 upperIndx[i] = dim[i] + lb[i] - 1;
1859 if (lowerIndx[i] > upperIndx[i])
1860 return construct_empty_array(elemtype);
1863 mda_get_range(ndim, span, lowerIndx, upperIndx);
1865 bytes = array_slice_size(arraydataptr, arraynullsptr,
1867 lowerIndx, upperIndx,
1868 elmlen, elmbyval, elmalign);
1871 * Currently, we put a null bitmap in the result if the source has one;
1872 * could be smarter ...
1876 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, ArrayGetNItems(ndim, span));
1877 bytes += dataoffset;
1881 dataoffset = 0; /* marker for no null bitmap */
1882 bytes += ARR_OVERHEAD_NONULLS(ndim);
1885 newarray = (ArrayType *) palloc(bytes);
1886 newarray->size = bytes;
1887 newarray->ndim = ndim;
1888 newarray->dataoffset = dataoffset;
1889 newarray->elemtype = elemtype;
1890 memcpy(ARR_DIMS(newarray), span, ndim * sizeof(int));
1893 * Lower bounds of the new array are set to 1. Formerly (before 7.3) we
1894 * copied the given lowerIndx values ... but that seems confusing.
1896 newlb = ARR_LBOUND(newarray);
1897 for (i = 0; i < ndim; i++)
1900 array_extract_slice(newarray,
1902 arraydataptr, arraynullsptr,
1903 lowerIndx, upperIndx,
1904 elmlen, elmbyval, elmalign);
1911 * This routine sets the value of an array element (specified by
1912 * a subscript array) to a new value specified by "dataValue".
1914 * This handles both ordinary varlena arrays and fixed-length arrays.
1917 * array: the initial array object (mustn't be NULL)
1918 * nSubscripts: number of subscripts supplied
1919 * indx[]: the subscript values
1920 * dataValue: the datum to be inserted at the given position
1921 * isNull: whether dataValue is NULL
1922 * arraytyplen: pg_type.typlen for the array type
1923 * elmlen: pg_type.typlen for the array's element type
1924 * elmbyval: pg_type.typbyval for the array's element type
1925 * elmalign: pg_type.typalign for the array's element type
1928 * A new array is returned, just like the old except for the one
1929 * modified entry. The original array object is not changed.
1931 * For one-dimensional arrays only, we allow the array to be extended
1932 * by assigning to the position one above or one below the existing range.
1933 * (XXX we could be more flexible: perhaps allow NULL fill?)
1935 * NOTE: For assignments, we throw an error for invalid subscripts etc,
1936 * rather than returning a NULL as the fetch operations do.
1939 array_set(ArrayType *array,
1949 ArrayType *newarray;
1956 bool extendbefore = false;
1957 bool extendafter = false;
1959 bits8 *oldnullbitmap;
1970 if (arraytyplen > 0)
1973 * fixed-length arrays -- these are assumed to be 1-d, 0-based. We
1974 * cannot extend them, either.
1976 if (nSubscripts != 1)
1978 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1979 errmsg("invalid array subscripts")));
1981 if (indx[0] < 0 || indx[0] * elmlen >= arraytyplen)
1983 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1984 errmsg("invalid array subscripts")));
1988 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
1989 errmsg("cannot assign NULL to an element of a fixed-length array")));
1991 newarray = (ArrayType *) palloc(arraytyplen);
1992 memcpy(newarray, array, arraytyplen);
1993 elt_ptr = (char *) newarray + indx[0] * elmlen;
1994 ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign, elt_ptr);
1998 if (nSubscripts <= 0 || nSubscripts > MAXDIM)
2000 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2001 errmsg("invalid array subscripts")));
2003 /* make sure item to be inserted is not toasted */
2004 if (elmlen == -1 && !isNull)
2005 dataValue = PointerGetDatum(PG_DETOAST_DATUM(dataValue));
2007 /* detoast input array if necessary */
2008 array = DatumGetArrayTypeP(PointerGetDatum(array));
2010 ndim = ARR_NDIM(array);
2013 * if number of dims is zero, i.e. an empty array, create an array with
2014 * nSubscripts dimensions, and set the lower bounds to the supplied
2019 Oid elmtype = ARR_ELEMTYPE(array);
2021 for (i = 0; i < nSubscripts; i++)
2027 return construct_md_array(&dataValue, &isNull, nSubscripts,
2029 elmlen, elmbyval, elmalign);
2032 if (ndim != nSubscripts)
2034 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2035 errmsg("invalid array subscripts")));
2037 /* copy dim/lb since we may modify them */
2038 memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
2039 memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
2044 for (i = 0; i < ndim; i++)
2046 if (indx[i] < lb[i])
2048 if (ndim == 1 && indx[i] == lb[i] - 1)
2052 extendbefore = true;
2056 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2057 errmsg("invalid array subscripts")));
2059 if (indx[i] >= (dim[i] + lb[i]))
2061 if (ndim == 1 && indx[i] == (dim[i] + lb[i]))
2068 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2069 errmsg("invalid array subscripts")));
2074 * Compute sizes of items and areas to copy
2076 if (ARR_HASNULL(array) || isNull)
2079 overheadlen = ARR_OVERHEAD_WITHNULLS(ndim,
2080 ArrayGetNItems(ndim, dim));
2084 newhasnulls = false;
2085 overheadlen = ARR_OVERHEAD_NONULLS(ndim);
2087 oldnitems = ArrayGetNItems(ndim, ARR_DIMS(array));
2088 oldnullbitmap = ARR_NULLBITMAP(array);
2089 oldoverheadlen = ARR_DATA_OFFSET(array);
2090 olddatasize = ARR_SIZE(array) - oldoverheadlen;
2096 lenafter = olddatasize;
2098 else if (extendafter)
2101 lenbefore = olddatasize;
2107 offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
2108 elt_ptr = array_seek(ARR_DATA_PTR(array), 0, oldnullbitmap, offset,
2109 elmlen, elmbyval, elmalign);
2110 lenbefore = (int) (elt_ptr - ARR_DATA_PTR(array));
2111 if (array_get_isnull(oldnullbitmap, offset))
2115 olditemlen = att_addlength(0, elmlen, PointerGetDatum(elt_ptr));
2116 olditemlen = att_align(olditemlen, elmalign);
2118 lenafter = (int) (olddatasize - lenbefore - olditemlen);
2125 newitemlen = att_addlength(0, elmlen, dataValue);
2126 newitemlen = att_align(newitemlen, elmalign);
2129 newsize = overheadlen + lenbefore + newitemlen + lenafter;
2132 * OK, create the new array and fill in header/dimensions
2134 newarray = (ArrayType *) palloc(newsize);
2135 newarray->size = newsize;
2136 newarray->ndim = ndim;
2137 newarray->dataoffset = newhasnulls ? overheadlen : 0;
2138 newarray->elemtype = ARR_ELEMTYPE(array);
2139 memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
2140 memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
2145 memcpy((char *) newarray + overheadlen,
2146 (char *) array + oldoverheadlen,
2149 ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign,
2150 (char *) newarray + overheadlen + lenbefore);
2151 memcpy((char *) newarray + overheadlen + lenbefore + newitemlen,
2152 (char *) array + oldoverheadlen + lenbefore + olditemlen,
2156 * Fill in nulls bitmap if needed
2158 * Note: it's possible we just replaced the last NULL with a non-NULL, and
2159 * could get rid of the bitmap. Seems not worth testing for though.
2163 bits8 *newnullbitmap = ARR_NULLBITMAP(newarray);
2165 array_set_isnull(newnullbitmap, offset, isNull);
2167 array_bitmap_copy(newnullbitmap, 1,
2172 array_bitmap_copy(newnullbitmap, 0,
2176 array_bitmap_copy(newnullbitmap, offset + 1,
2177 oldnullbitmap, offset + 1,
2178 oldnitems - offset - 1);
2187 * This routine sets the value of a range of array locations (specified
2188 * by upper and lower subscript values) to new values passed as
2191 * This handles both ordinary varlena arrays and fixed-length arrays.
2194 * array: the initial array object (mustn't be NULL)
2195 * nSubscripts: number of subscripts supplied (must be same for upper/lower)
2196 * upperIndx[]: the upper subscript values
2197 * lowerIndx[]: the lower subscript values
2198 * srcArray: the source for the inserted values
2199 * isNull: indicates whether srcArray is NULL
2200 * arraytyplen: pg_type.typlen for the array type
2201 * elmlen: pg_type.typlen for the array's element type
2202 * elmbyval: pg_type.typbyval for the array's element type
2203 * elmalign: pg_type.typalign for the array's element type
2206 * A new array is returned, just like the old except for the
2207 * modified range. The original array object is not changed.
2209 * NOTE: we assume it is OK to scribble on the provided index arrays
2210 * lowerIndx[] and upperIndx[]. These are generally just temporaries.
2212 * NOTE: For assignments, we throw an error for silly subscripts etc,
2213 * rather than returning a NULL or empty array as the fetch operations do.
2216 array_set_slice(ArrayType *array,
2220 ArrayType *srcArray,
2227 ArrayType *newarray;
2248 /* Currently, assignment from a NULL source array is a no-op */
2252 if (arraytyplen > 0)
2255 * fixed-length arrays -- not got round to doing this...
2258 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2259 errmsg("updates on slices of fixed-length arrays not implemented")));
2262 /* detoast arrays if necessary */
2263 array = DatumGetArrayTypeP(PointerGetDatum(array));
2264 srcArray = DatumGetArrayTypeP(PointerGetDatum(srcArray));
2266 /* note: we assume srcArray contains no toasted elements */
2268 ndim = ARR_NDIM(array);
2271 * if number of dims is zero, i.e. an empty array, create an array with
2272 * nSubscripts dimensions, and set the upper and lower bounds to the
2273 * supplied subscripts
2280 Oid elmtype = ARR_ELEMTYPE(array);
2282 deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
2283 &dvalues, &dnulls, &nelems);
2285 for (i = 0; i < nSubscripts; i++)
2287 dim[i] = 1 + upperIndx[i] - lowerIndx[i];
2288 lb[i] = lowerIndx[i];
2291 /* complain if too few source items; we ignore extras, however */
2292 if (nelems < ArrayGetNItems(nSubscripts, dim))
2294 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2295 errmsg("source array too small")));
2297 return construct_md_array(dvalues, dnulls, nSubscripts,
2299 elmlen, elmbyval, elmalign);
2302 if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
2304 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2305 errmsg("invalid array subscripts")));
2307 /* copy dim/lb since we may modify them */
2308 memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
2309 memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
2312 * Check provided subscripts. A slice exceeding the current array limits
2313 * throws an error, *except* in the 1-D case where we will extend the
2314 * array as long as no hole is created. An empty slice is an error, too.
2316 for (i = 0; i < nSubscripts; i++)
2318 if (lowerIndx[i] > upperIndx[i])
2320 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2321 errmsg("invalid array subscripts")));
2322 if (lowerIndx[i] < lb[i])
2324 if (ndim == 1 && upperIndx[i] >= lb[i] - 1)
2326 dim[i] += lb[i] - lowerIndx[i];
2327 lb[i] = lowerIndx[i];
2331 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2332 errmsg("invalid array subscripts")));
2334 if (upperIndx[i] >= (dim[i] + lb[i]))
2336 if (ndim == 1 && lowerIndx[i] <= (dim[i] + lb[i]))
2337 dim[i] = upperIndx[i] - lb[i] + 1;
2340 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2341 errmsg("invalid array subscripts")));
2344 /* fill any missing subscript positions with full array range */
2345 for (; i < ndim; i++)
2347 lowerIndx[i] = lb[i];
2348 upperIndx[i] = dim[i] + lb[i] - 1;
2349 if (lowerIndx[i] > upperIndx[i])
2351 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2352 errmsg("invalid array subscripts")));
2355 /* Do this mainly to check for overflow */
2356 nitems = ArrayGetNItems(ndim, dim);
2359 * Make sure source array has enough entries. Note we ignore the shape of
2360 * the source array and just read entries serially.
2362 mda_get_range(ndim, span, lowerIndx, upperIndx);
2363 nsrcitems = ArrayGetNItems(ndim, span);
2364 if (nsrcitems > ArrayGetNItems(ARR_NDIM(srcArray), ARR_DIMS(srcArray)))
2366 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2367 errmsg("source array too small")));
2370 * Compute space occupied by new entries, space occupied by replaced
2371 * entries, and required space for new array.
2373 if (ARR_HASNULL(array) || ARR_HASNULL(srcArray))
2376 overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
2380 newhasnulls = false;
2381 overheadlen = ARR_OVERHEAD_NONULLS(ndim);
2383 newitemsize = array_nelems_size(ARR_DATA_PTR(srcArray), 0,
2384 ARR_NULLBITMAP(srcArray), nsrcitems,
2385 elmlen, elmbyval, elmalign);
2386 oldoverheadlen = ARR_DATA_OFFSET(array);
2387 olddatasize = ARR_SIZE(array) - oldoverheadlen;
2391 * here we do not need to cope with extension of the array; it would
2392 * be a lot more complicated if we had to do so...
2394 olditemsize = array_slice_size(ARR_DATA_PTR(array),
2395 ARR_NULLBITMAP(array),
2397 lowerIndx, upperIndx,
2398 elmlen, elmbyval, elmalign);
2399 lenbefore = lenafter = 0; /* keep compiler quiet */
2400 itemsbefore = itemsafter = nolditems = 0;
2405 * here we must allow for possibility of slice larger than orig array
2407 int oldlb = ARR_LBOUND(array)[0];
2408 int oldub = oldlb + ARR_DIMS(array)[0] - 1;
2409 int slicelb = Max(oldlb, lowerIndx[0]);
2410 int sliceub = Min(oldub, upperIndx[0]);
2411 char *oldarraydata = ARR_DATA_PTR(array);
2412 bits8 *oldarraybitmap = ARR_NULLBITMAP(array);
2414 itemsbefore = slicelb - oldlb;
2415 lenbefore = array_nelems_size(oldarraydata, 0, oldarraybitmap,
2417 elmlen, elmbyval, elmalign);
2418 if (slicelb > sliceub)
2425 nolditems = sliceub - slicelb + 1;
2426 olditemsize = array_nelems_size(oldarraydata + lenbefore,
2427 itemsbefore, oldarraybitmap,
2429 elmlen, elmbyval, elmalign);
2431 itemsafter = oldub - sliceub;
2432 lenafter = olddatasize - lenbefore - olditemsize;
2435 newsize = overheadlen + olddatasize - olditemsize + newitemsize;
2437 newarray = (ArrayType *) palloc(newsize);
2438 newarray->size = newsize;
2439 newarray->ndim = ndim;
2440 newarray->dataoffset = newhasnulls ? overheadlen : 0;
2441 newarray->elemtype = ARR_ELEMTYPE(array);
2442 memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
2443 memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
2448 * here we do not need to cope with extension of the array; it would
2449 * be a lot more complicated if we had to do so...
2451 array_insert_slice(newarray, array, srcArray,
2453 lowerIndx, upperIndx,
2454 elmlen, elmbyval, elmalign);
2459 memcpy((char *) newarray + overheadlen,
2460 (char *) array + oldoverheadlen,
2462 memcpy((char *) newarray + overheadlen + lenbefore,
2463 ARR_DATA_PTR(srcArray),
2465 memcpy((char *) newarray + overheadlen + lenbefore + newitemsize,
2466 (char *) array + oldoverheadlen + lenbefore + olditemsize,
2468 /* fill in nulls bitmap if needed */
2471 bits8 *newnullbitmap = ARR_NULLBITMAP(newarray);
2472 bits8 *oldnullbitmap = ARR_NULLBITMAP(array);
2474 array_bitmap_copy(newnullbitmap, 0,
2477 array_bitmap_copy(newnullbitmap, itemsbefore,
2478 ARR_NULLBITMAP(srcArray), 0,
2480 array_bitmap_copy(newnullbitmap, itemsbefore + nsrcitems,
2481 oldnullbitmap, itemsbefore + nolditems,
2492 * Map an array through an arbitrary function. Return a new array with
2493 * same dimensions and each source element transformed by fn(). Each
2494 * source element is passed as the first argument to fn(); additional
2495 * arguments to be passed to fn() can be specified by the caller.
2496 * The output array can have a different element type than the input.
2499 * * fcinfo: a function-call data structure pre-constructed by the caller
2500 * to be ready to call the desired function, with everything except the
2501 * first argument position filled in. In particular, flinfo identifies
2502 * the function fn(), and if nargs > 1 then argument positions after the
2503 * first must be preset to the additional values to be passed. The
2504 * first argument position initially holds the input array value.
2505 * * inpType: OID of element type of input array. This must be the same as,
2506 * or binary-compatible with, the first argument type of fn().
2507 * * retType: OID of element type of output array. This must be the same as,
2508 * or binary-compatible with, the result type of fn().
2509 * * amstate: workspace for array_map. Must be zeroed by caller before
2510 * first call, and not touched after that.
2512 * It is legitimate to pass a freshly-zeroed ArrayMapState on each call,
2513 * but better performance can be had if the state can be preserved across
2514 * a series of calls.
2516 * NB: caller must assure that input array is not NULL. NULL elements in
2517 * the array are OK however.
2520 array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType,
2521 ArrayMapState *amstate)
2544 ArrayMetaState *inp_extra;
2545 ArrayMetaState *ret_extra;
2547 /* Get input array */
2548 if (fcinfo->nargs < 1)
2549 elog(ERROR, "invalid nargs: %d", fcinfo->nargs);
2550 if (PG_ARGISNULL(0))
2551 elog(ERROR, "null input array");
2552 v = PG_GETARG_ARRAYTYPE_P(0);
2554 Assert(ARR_ELEMTYPE(v) == inpType);
2558 nitems = ArrayGetNItems(ndim, dim);
2560 /* Check for empty array */
2563 /* Return empty array */
2564 PG_RETURN_ARRAYTYPE_P(construct_empty_array(retType));
2568 * We arrange to look up info about input and return element types only
2569 * once per series of calls, assuming the element type doesn't change
2572 inp_extra = &amstate->inp_extra;
2573 ret_extra = &amstate->ret_extra;
2575 if (inp_extra->element_type != inpType)
2577 get_typlenbyvalalign(inpType,
2579 &inp_extra->typbyval,
2580 &inp_extra->typalign);
2581 inp_extra->element_type = inpType;
2583 inp_typlen = inp_extra->typlen;
2584 inp_typbyval = inp_extra->typbyval;
2585 inp_typalign = inp_extra->typalign;
2587 if (ret_extra->element_type != retType)
2589 get_typlenbyvalalign(retType,
2591 &ret_extra->typbyval,
2592 &ret_extra->typalign);
2593 ret_extra->element_type = retType;
2595 typlen = ret_extra->typlen;
2596 typbyval = ret_extra->typbyval;
2597 typalign = ret_extra->typalign;
2599 /* Allocate temporary arrays for new values */
2600 values = (Datum *) palloc(nitems * sizeof(Datum));
2601 nulls = (bool *) palloc(nitems * sizeof(bool));
2603 /* Loop over source data */
2604 s = ARR_DATA_PTR(v);
2605 bitmap = ARR_NULLBITMAP(v);
2609 for (i = 0; i < nitems; i++)
2613 /* Get source element, checking for NULL */
2614 if (bitmap && (*bitmap & bitmask) == 0)
2616 fcinfo->argnull[0] = true;
2620 elt = fetch_att(s, inp_typbyval, inp_typlen);
2621 s = att_addlength(s, inp_typlen, elt);
2622 s = (char *) att_align(s, inp_typalign);
2623 fcinfo->arg[0] = elt;
2624 fcinfo->argnull[0] = false;
2628 * Apply the given function to source elt and extra args.
2630 if (fcinfo->flinfo->fn_strict)
2634 for (j = 0; j < fcinfo->nargs; j++)
2636 if (fcinfo->argnull[j])
2646 fcinfo->isnull = false;
2647 values[i] = FunctionCallInvoke(fcinfo);
2650 fcinfo->isnull = true;
2652 nulls[i] = fcinfo->isnull;
2657 /* Ensure data is not toasted */
2659 values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
2660 /* Update total result size */
2661 nbytes = att_addlength(nbytes, typlen, values[i]);
2662 nbytes = att_align(nbytes, typalign);
2663 /* check for overflow of total request */
2664 if (!AllocSizeIsValid(nbytes))
2666 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2667 errmsg("array size exceeds the maximum allowed (%d)",
2668 (int) MaxAllocSize)));
2671 /* advance bitmap pointer if any */
2675 if (bitmask == 0x100)
2683 /* Allocate and initialize the result array */
2686 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
2687 nbytes += dataoffset;
2691 dataoffset = 0; /* marker for no null bitmap */
2692 nbytes += ARR_OVERHEAD_NONULLS(ndim);
2694 result = (ArrayType *) palloc(nbytes);
2695 result->size = nbytes;
2696 result->ndim = ndim;
2697 result->dataoffset = dataoffset;
2698 result->elemtype = retType;
2699 memcpy(ARR_DIMS(result), ARR_DIMS(v), 2 * ndim * sizeof(int));
2702 * Note: do not risk trying to pfree the results of the called function
2704 CopyArrayEls(result,
2705 values, nulls, nitems,
2706 typlen, typbyval, typalign,
2712 PG_RETURN_ARRAYTYPE_P(result);
2716 * construct_array --- simple method for constructing an array object
2718 * elems: array of Datum items to become the array contents
2719 * (NULL element values are not supported).
2720 * nelems: number of items
2721 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
2723 * A palloc'd 1-D array object is constructed and returned. Note that
2724 * elem values will be copied into the object even if pass-by-ref type.
2726 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
2727 * from the system catalogs, given the elmtype. However, the caller is
2728 * in a better position to cache this info across multiple uses, or even
2729 * to hard-wire values if the element type is hard-wired.
2732 construct_array(Datum *elems, int nelems,
2734 int elmlen, bool elmbyval, char elmalign)
2742 return construct_md_array(elems, NULL, 1, dims, lbs,
2743 elmtype, elmlen, elmbyval, elmalign);
2747 * construct_md_array --- simple method for constructing an array object
2748 * with arbitrary dimensions and possible NULLs
2750 * elems: array of Datum items to become the array contents
2751 * nulls: array of is-null flags (can be NULL if no nulls)
2752 * ndims: number of dimensions
2753 * dims: integer array with size of each dimension
2754 * lbs: integer array with lower bound of each dimension
2755 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
2757 * A palloc'd ndims-D array object is constructed and returned. Note that
2758 * elem values will be copied into the object even if pass-by-ref type.
2760 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
2761 * from the system catalogs, given the elmtype. However, the caller is
2762 * in a better position to cache this info across multiple uses, or even
2763 * to hard-wire values if the element type is hard-wired.
2766 construct_md_array(Datum *elems,
2771 Oid elmtype, int elmlen, bool elmbyval, char elmalign)
2780 if (ndims < 0) /* we do allow zero-dimension arrays */
2782 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2783 errmsg("invalid number of dimensions: %d", ndims)));
2786 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2787 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
2790 /* fast track for empty array */
2792 return construct_empty_array(elmtype);
2794 nelems = ArrayGetNItems(ndims, dims);
2796 /* compute required space */
2799 for (i = 0; i < nelems; i++)
2801 if (nulls && nulls[i])
2806 /* make sure data is not toasted */
2808 elems[i] = PointerGetDatum(PG_DETOAST_DATUM(elems[i]));
2809 nbytes = att_addlength(nbytes, elmlen, elems[i]);
2810 nbytes = att_align(nbytes, elmalign);
2811 /* check for overflow of total request */
2812 if (!AllocSizeIsValid(nbytes))
2814 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2815 errmsg("array size exceeds the maximum allowed (%d)",
2816 (int) MaxAllocSize)));
2819 /* Allocate and initialize result array */
2822 dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nelems);
2823 nbytes += dataoffset;
2827 dataoffset = 0; /* marker for no null bitmap */
2828 nbytes += ARR_OVERHEAD_NONULLS(ndims);
2830 result = (ArrayType *) palloc(nbytes);
2831 result->size = nbytes;
2832 result->ndim = ndims;
2833 result->dataoffset = dataoffset;
2834 result->elemtype = elmtype;
2835 memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
2836 memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
2838 CopyArrayEls(result,
2839 elems, nulls, nelems,
2840 elmlen, elmbyval, elmalign,
2847 * construct_empty_array --- make a zero-dimensional array of given type
2850 construct_empty_array(Oid elmtype)
2854 result = (ArrayType *) palloc(sizeof(ArrayType));
2855 result->size = sizeof(ArrayType);
2857 result->dataoffset = 0;
2858 result->elemtype = elmtype;
2863 * deconstruct_array --- simple method for extracting data from an array
2865 * array: array object to examine (must not be NULL)
2866 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
2867 * elemsp: return value, set to point to palloc'd array of Datum values
2868 * nullsp: return value, set to point to palloc'd array of isnull markers
2869 * nelemsp: return value, set to number of extracted values
2871 * The caller may pass nullsp == NULL if it does not support NULLs in the
2872 * array. Note that this produces a very uninformative error message,
2873 * so do it only in cases where a NULL is really not expected.
2875 * If array elements are pass-by-ref data type, the returned Datums will
2876 * be pointers into the array object.
2878 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
2879 * from the system catalogs, given the elmtype. However, in most current
2880 * uses the type is hard-wired into the caller and so we can save a lookup
2881 * cycle by hard-wiring the type info as well.
2884 deconstruct_array(ArrayType *array,
2886 int elmlen, bool elmbyval, char elmalign,
2887 Datum **elemsp, bool **nullsp, int *nelemsp)
2897 Assert(ARR_ELEMTYPE(array) == elmtype);
2899 nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
2900 *elemsp = elems = (Datum *) palloc(nelems * sizeof(Datum));
2902 *nullsp = nulls = (bool *) palloc(nelems * sizeof(bool));
2907 p = ARR_DATA_PTR(array);
2908 bitmap = ARR_NULLBITMAP(array);
2911 for (i = 0; i < nelems; i++)
2913 /* Get source element, checking for NULL */
2914 if (bitmap && (*bitmap & bitmask) == 0)
2916 elems[i] = (Datum) 0;
2921 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
2922 errmsg("NULL array element not allowed in this context")));
2926 elems[i] = fetch_att(p, elmbyval, elmlen);
2929 p = att_addlength(p, elmlen, PointerGetDatum(p));
2930 p = (char *) att_align(p, elmalign);
2933 /* advance bitmap pointer if any */
2937 if (bitmask == 0x100)
2949 * compares two arrays for equality
2951 * returns true if the arrays are equal, false otherwise.
2953 * Note: we do not use array_cmp here, since equality may be meaningful in
2954 * datatypes that don't have a total ordering (and hence no btree support).
2957 array_eq(PG_FUNCTION_ARGS)
2959 ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
2960 ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
2961 int ndims1 = ARR_NDIM(array1);
2962 int ndims2 = ARR_NDIM(array2);
2963 int *dims1 = ARR_DIMS(array1);
2964 int *dims2 = ARR_DIMS(array2);
2965 Oid element_type = ARR_ELEMTYPE(array1);
2968 TypeCacheEntry *typentry;
2978 FunctionCallInfoData locfcinfo;
2980 if (element_type != ARR_ELEMTYPE(array2))
2982 (errcode(ERRCODE_DATATYPE_MISMATCH),
2983 errmsg("cannot compare arrays of different element types")));
2985 /* fast path if the arrays do not have the same dimensionality */
2986 if (ndims1 != ndims2 ||
2987 memcmp(dims1, dims2, 2 * ndims1 * sizeof(int)) != 0)
2992 * We arrange to look up the equality function only once per series of
2993 * calls, assuming the element type doesn't change underneath us. The
2994 * typcache is used so that we have no memory leakage when being used
2995 * as an index support function.
2997 typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
2998 if (typentry == NULL ||
2999 typentry->type_id != element_type)
3001 typentry = lookup_type_cache(element_type,
3002 TYPECACHE_EQ_OPR_FINFO);
3003 if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
3005 (errcode(ERRCODE_UNDEFINED_FUNCTION),
3006 errmsg("could not identify an equality operator for type %s",
3007 format_type_be(element_type))));
3008 fcinfo->flinfo->fn_extra = (void *) typentry;
3010 typlen = typentry->typlen;
3011 typbyval = typentry->typbyval;
3012 typalign = typentry->typalign;
3015 * apply the operator to each pair of array elements.
3017 InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
3020 /* Loop over source data */
3021 nitems = ArrayGetNItems(ndims1, dims1);
3022 ptr1 = ARR_DATA_PTR(array1);
3023 ptr2 = ARR_DATA_PTR(array2);
3024 bitmap1 = ARR_NULLBITMAP(array1);
3025 bitmap2 = ARR_NULLBITMAP(array2);
3026 bitmask = 1; /* use same bitmask for both arrays */
3028 for (i = 0; i < nitems; i++)
3036 /* Get elements, checking for NULL */
3037 if (bitmap1 && (*bitmap1 & bitmask) == 0)
3045 elt1 = fetch_att(ptr1, typbyval, typlen);
3046 ptr1 = att_addlength(ptr1, typlen, PointerGetDatum(ptr1));
3047 ptr1 = (char *) att_align(ptr1, typalign);
3050 if (bitmap2 && (*bitmap2 & bitmask) == 0)
3058 elt2 = fetch_att(ptr2, typbyval, typlen);
3059 ptr2 = att_addlength(ptr2, typlen, PointerGetDatum(ptr2));
3060 ptr2 = (char *) att_align(ptr2, typalign);
3063 /* advance bitmap pointers if any */
3065 if (bitmask == 0x100)
3075 * We consider two NULLs equal; NULL and not-NULL are unequal.
3077 if (isnull1 && isnull2)
3079 if (isnull1 || isnull2)
3086 * Apply the operator to the element pair
3088 locfcinfo.arg[0] = elt1;
3089 locfcinfo.arg[1] = elt2;
3090 locfcinfo.argnull[0] = false;
3091 locfcinfo.argnull[1] = false;
3092 locfcinfo.isnull = false;
3093 oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
3102 /* Avoid leaking memory when handed toasted input. */
3103 PG_FREE_IF_COPY(array1, 0);
3104 PG_FREE_IF_COPY(array2, 1);
3106 PG_RETURN_BOOL(result);
3110 /*-----------------------------------------------------------------------------
3111 * array-array bool operators:
3112 * Given two arrays, iterate comparison operators
3113 * over the array. Uses logic similar to text comparison
3114 * functions, except element-by-element instead of
3115 * character-by-character.
3116 *----------------------------------------------------------------------------
3120 array_ne(PG_FUNCTION_ARGS)
3122 PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo)));
3126 array_lt(PG_FUNCTION_ARGS)
3128 PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
3132 array_gt(PG_FUNCTION_ARGS)
3134 PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
3138 array_le(PG_FUNCTION_ARGS)
3140 PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
3144 array_ge(PG_FUNCTION_ARGS)
3146 PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
3150 btarraycmp(PG_FUNCTION_ARGS)
3152 PG_RETURN_INT32(array_cmp(fcinfo));
3157 * Internal comparison function for arrays.
3159 * Returns -1, 0 or 1
3162 array_cmp(FunctionCallInfo fcinfo)
3164 ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
3165 ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
3166 int ndims1 = ARR_NDIM(array1);
3167 int ndims2 = ARR_NDIM(array2);
3168 int *dims1 = ARR_DIMS(array1);
3169 int *dims2 = ARR_DIMS(array2);
3170 int nitems1 = ArrayGetNItems(ndims1, dims1);
3171 int nitems2 = ArrayGetNItems(ndims2, dims2);
3172 Oid element_type = ARR_ELEMTYPE(array1);
3174 TypeCacheEntry *typentry;
3185 FunctionCallInfoData locfcinfo;
3187 if (element_type != ARR_ELEMTYPE(array2))
3189 (errcode(ERRCODE_DATATYPE_MISMATCH),
3190 errmsg("cannot compare arrays of different element types")));
3193 * We arrange to look up the comparison function only once per series of
3194 * calls, assuming the element type doesn't change underneath us. The
3195 * typcache is used so that we have no memory leakage when being used as
3196 * an index support function.
3198 typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3199 if (typentry == NULL ||
3200 typentry->type_id != element_type)
3202 typentry = lookup_type_cache(element_type,
3203 TYPECACHE_CMP_PROC_FINFO);
3204 if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
3206 (errcode(ERRCODE_UNDEFINED_FUNCTION),
3207 errmsg("could not identify a comparison function for type %s",
3208 format_type_be(element_type))));
3209 fcinfo->flinfo->fn_extra = (void *) typentry;
3211 typlen = typentry->typlen;
3212 typbyval = typentry->typbyval;
3213 typalign = typentry->typalign;
3216 * apply the operator to each pair of array elements.
3218 InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2,
3221 /* Loop over source data */
3222 min_nitems = Min(nitems1, nitems2);
3223 ptr1 = ARR_DATA_PTR(array1);
3224 ptr2 = ARR_DATA_PTR(array2);
3225 bitmap1 = ARR_NULLBITMAP(array1);
3226 bitmap2 = ARR_NULLBITMAP(array2);
3227 bitmask = 1; /* use same bitmask for both arrays */
3229 for (i = 0; i < min_nitems; i++)
3237 /* Get elements, checking for NULL */
3238 if (bitmap1 && (*bitmap1 & bitmask) == 0)
3246 elt1 = fetch_att(ptr1, typbyval, typlen);
3247 ptr1 = att_addlength(ptr1, typlen, PointerGetDatum(ptr1));
3248 ptr1 = (char *) att_align(ptr1, typalign);
3251 if (bitmap2 && (*bitmap2 & bitmask) == 0)
3259 elt2 = fetch_att(ptr2, typbyval, typlen);
3260 ptr2 = att_addlength(ptr2, typlen, PointerGetDatum(ptr2));
3261 ptr2 = (char *) att_align(ptr2, typalign);
3264 /* advance bitmap pointers if any */
3266 if (bitmask == 0x100)
3276 * We consider two NULLs equal; NULL > not-NULL.
3278 if (isnull1 && isnull2)
3282 /* arg1 is greater than arg2 */
3288 /* arg1 is less than arg2 */
3293 /* Compare the pair of elements */
3294 locfcinfo.arg[0] = elt1;
3295 locfcinfo.arg[1] = elt2;
3296 locfcinfo.argnull[0] = false;
3297 locfcinfo.argnull[1] = false;
3298 locfcinfo.isnull = false;
3299 cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
3302 continue; /* equal */
3306 /* arg1 is less than arg2 */
3312 /* arg1 is greater than arg2 */
3319 * If arrays contain same data (up to end of shorter one), apply
3320 * additional rules to sort by dimensionality. The relative significance
3321 * of the different bits of information is historical; mainly we just care
3322 * that we don't say "equal" for arrays of different dimensionality.
3326 if (nitems1 != nitems2)
3327 result = (nitems1 < nitems2) ? -1 : 1;
3328 else if (ndims1 != ndims2)
3329 result = (ndims1 < ndims2) ? -1 : 1;
3332 /* this relies on LB array immediately following DIMS array */
3333 for (i = 0; i < ndims1 * 2; i++)
3335 if (dims1[i] != dims2[i])
3337 result = (dims1[i] < dims2[i]) ? -1 : 1;
3344 /* Avoid leaking memory when handed toasted input. */
3345 PG_FREE_IF_COPY(array1, 0);
3346 PG_FREE_IF_COPY(array2, 1);
3352 /***************************************************************************/
3353 /******************| Support Routines |*****************/
3354 /***************************************************************************/
3357 * Check whether a specific array element is NULL
3359 * nullbitmap: pointer to array's null bitmap (NULL if none)
3360 * offset: 0-based linear element number of array element
3363 array_get_isnull(const bits8 *nullbitmap, int offset)
3365 if (nullbitmap == NULL)
3366 return false; /* assume not null */
3367 if (nullbitmap[offset / 8] & (1 << (offset % 8)))
3368 return false; /* not null */
3373 * Set a specific array element's null-bitmap entry
3375 * nullbitmap: pointer to array's null bitmap (mustn't be NULL)
3376 * offset: 0-based linear element number of array element
3377 * isNull: null status to set
3380 array_set_isnull(bits8 *nullbitmap, int offset, bool isNull)
3384 nullbitmap += offset / 8;
3385 bitmask = 1 << (offset % 8);
3387 *nullbitmap &= ~bitmask;
3389 *nullbitmap |= bitmask;
3393 * Fetch array element at pointer, converted correctly to a Datum
3395 * Caller must have handled case of NULL element
3398 ArrayCast(char *value, bool byval, int len)
3400 return fetch_att(value, byval, len);
3404 * Copy datum to *dest and return total space used (including align padding)
3406 * Caller must have handled case of NULL element
3409 ArrayCastAndSet(Datum src,
3420 store_att_byval(dest, src, typlen);
3422 memmove(dest, DatumGetPointer(src), typlen);
3423 inc = att_align(typlen, typalign);
3428 inc = att_addlength(0, typlen, src);
3429 memmove(dest, DatumGetPointer(src), inc);
3430 inc = att_align(inc, typalign);
3437 * Advance ptr over nitems array elements
3439 * ptr: starting location in array
3440 * offset: 0-based linear element number of first element (the one at *ptr)
3441 * nullbitmap: start of array's null bitmap, or NULL if none
3442 * nitems: number of array elements to advance over (>= 0)
3443 * typlen, typbyval, typalign: storage parameters of array element datatype
3445 * It is caller's responsibility to ensure that nitems is within range
3448 array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
3449 int typlen, bool typbyval, char typalign)
3454 /* easy if fixed-size elements and no NULLs */
3455 if (typlen > 0 && !nullbitmap)
3456 return ptr + nitems * ((Size) att_align(typlen, typalign));
3458 /* seems worth having separate loops for NULL and no-NULLs cases */
3461 nullbitmap += offset / 8;
3462 bitmask = 1 << (offset % 8);
3464 for (i = 0; i < nitems; i++)
3466 if (*nullbitmap & bitmask)
3468 ptr = att_addlength(ptr, typlen, PointerGetDatum(ptr));
3469 ptr = (char *) att_align(ptr, typalign);
3472 if (bitmask == 0x100)
3481 for (i = 0; i < nitems; i++)
3483 ptr = att_addlength(ptr, typlen, PointerGetDatum(ptr));
3484 ptr = (char *) att_align(ptr, typalign);
3491 * Compute total size of the nitems array elements starting at *ptr
3493 * Parameters same as for array_seek
3496 array_nelems_size(char *ptr, int offset, bits8 *nullbitmap, int nitems,
3497 int typlen, bool typbyval, char typalign)
3499 return array_seek(ptr, offset, nullbitmap, nitems,
3500 typlen, typbyval, typalign) - ptr;
3504 * Copy nitems array elements from srcptr to destptr
3506 * destptr: starting destination location (must be enough room!)
3507 * nitems: number of array elements to copy (>= 0)
3508 * srcptr: starting location in source array
3509 * offset: 0-based linear element number of first element (the one at *srcptr)
3510 * nullbitmap: start of source array's null bitmap, or NULL if none
3511 * typlen, typbyval, typalign: storage parameters of array element datatype
3513 * Returns number of bytes copied
3515 * NB: this does not take care of setting up the destination's null bitmap!
3518 array_copy(char *destptr, int nitems,
3519 char *srcptr, int offset, bits8 *nullbitmap,
3520 int typlen, bool typbyval, char typalign)
3524 numbytes = array_nelems_size(srcptr, offset, nullbitmap, nitems,
3525 typlen, typbyval, typalign);
3526 memcpy(destptr, srcptr, numbytes);
3531 * Copy nitems null-bitmap bits from source to destination
3533 * destbitmap: start of destination array's null bitmap (mustn't be NULL)
3534 * destoffset: 0-based linear element number of first dest element
3535 * srcbitmap: start of source array's null bitmap, or NULL if none
3536 * srcoffset: 0-based linear element number of first source element
3537 * nitems: number of bits to copy (>= 0)
3539 * If srcbitmap is NULL then we assume the source is all-non-NULL and
3540 * fill 1's into the destination bitmap. Note that only the specified
3541 * bits in the destination map are changed, not any before or after.
3543 * Note: this could certainly be optimized using standard bitblt methods.
3544 * However, it's not clear that the typical Postgres array has enough elements
3545 * to make it worth worrying too much. For the moment, KISS.
3548 array_bitmap_copy(bits8 *destbitmap, int destoffset,
3549 const bits8 *srcbitmap, int srcoffset,
3559 return; /* don't risk fetch off end of memory */
3560 destbitmap += destoffset / 8;
3561 destbitmask = 1 << (destoffset % 8);
3562 destbitval = *destbitmap;
3565 srcbitmap += srcoffset / 8;
3566 srcbitmask = 1 << (srcoffset % 8);
3567 srcbitval = *srcbitmap;
3568 while (nitems-- > 0)
3570 if (srcbitval & srcbitmask)
3571 destbitval |= destbitmask;
3573 destbitval &= ~destbitmask;
3575 if (destbitmask == 0x100)
3577 *destbitmap++ = destbitval;
3580 destbitval = *destbitmap;
3583 if (srcbitmask == 0x100)
3588 srcbitval = *srcbitmap;
3591 if (destbitmask != 1)
3592 *destbitmap = destbitval;
3596 while (nitems-- > 0)
3598 destbitval |= destbitmask;
3600 if (destbitmask == 0x100)
3602 *destbitmap++ = destbitval;
3605 destbitval = *destbitmap;
3608 if (destbitmask != 1)
3609 *destbitmap = destbitval;
3614 * Compute space needed for a slice of an array
3616 * We assume the caller has verified that the slice coordinates are valid.
3619 array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
3620 int ndim, int *dim, int *lb,
3622 int typlen, bool typbyval, char typalign)
3635 mda_get_range(ndim, span, st, endp);
3637 /* Pretty easy for fixed element length without nulls ... */
3638 if (typlen > 0 && !arraynullsptr)
3639 return ArrayGetNItems(ndim, span) * att_align(typlen, typalign);
3641 /* Else gotta do it the hard way */
3642 src_offset = ArrayGetOffset(ndim, dim, lb, st);
3643 ptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
3644 typlen, typbyval, typalign);
3645 mda_get_prod(ndim, dim, prod);
3646 mda_get_offset_values(ndim, dist, prod, span);
3647 for (i = 0; i < ndim; i++)
3654 ptr = array_seek(ptr, src_offset, arraynullsptr, dist[j],
3655 typlen, typbyval, typalign);
3656 src_offset += dist[j];
3658 if (!array_get_isnull(arraynullsptr, src_offset))
3660 inc = att_addlength(0, typlen, PointerGetDatum(ptr));
3661 inc = att_align(inc, typalign);
3666 } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
3671 * Extract a slice of an array into consecutive elements in the destination
3674 * We assume the caller has verified that the slice coordinates are valid,
3675 * allocated enough storage for the result, and initialized the header
3679 array_extract_slice(ArrayType *newarray,
3684 bits8 *arraynullsptr,
3691 char *destdataptr = ARR_DATA_PTR(newarray);
3692 bits8 *destnullsptr = ARR_NULLBITMAP(newarray);
3704 src_offset = ArrayGetOffset(ndim, dim, lb, st);
3705 srcdataptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
3706 typlen, typbyval, typalign);
3707 mda_get_prod(ndim, dim, prod);
3708 mda_get_range(ndim, span, st, endp);
3709 mda_get_offset_values(ndim, dist, prod, span);
3710 for (i = 0; i < ndim; i++)
3718 /* skip unwanted elements */
3719 srcdataptr = array_seek(srcdataptr, src_offset, arraynullsptr,
3721 typlen, typbyval, typalign);
3722 src_offset += dist[j];
3724 inc = array_copy(destdataptr, 1,
3725 srcdataptr, src_offset, arraynullsptr,
3726 typlen, typbyval, typalign);
3728 array_bitmap_copy(destnullsptr, dest_offset,
3729 arraynullsptr, src_offset,
3735 } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
3739 * Insert a slice into an array.
3741 * ndim/dim[]/lb[] are dimensions of the original array. A new array with
3742 * those same dimensions is to be constructed. destArray must already
3743 * have been allocated and its header initialized.
3745 * st[]/endp[] identify the slice to be replaced. Elements within the slice
3746 * volume are taken from consecutive elements of the srcArray; elements
3747 * outside it are copied from origArray.
3749 * We assume the caller has verified that the slice coordinates are valid.
3752 array_insert_slice(ArrayType *destArray,
3753 ArrayType *origArray,
3754 ArrayType *srcArray,
3764 char *destPtr = ARR_DATA_PTR(destArray);
3765 char *origPtr = ARR_DATA_PTR(origArray);
3766 char *srcPtr = ARR_DATA_PTR(srcArray);
3767 bits8 *destBitmap = ARR_NULLBITMAP(destArray);
3768 bits8 *origBitmap = ARR_NULLBITMAP(origArray);
3769 bits8 *srcBitmap = ARR_NULLBITMAP(srcArray);
3770 int orignitems = ArrayGetNItems(ARR_NDIM(origArray),
3771 ARR_DIMS(origArray));
3783 dest_offset = ArrayGetOffset(ndim, dim, lb, st);
3784 /* copy items before the slice start */
3785 inc = array_copy(destPtr, dest_offset,
3786 origPtr, 0, origBitmap,
3787 typlen, typbyval, typalign);
3791 array_bitmap_copy(destBitmap, 0, origBitmap, 0, dest_offset);
3792 orig_offset = dest_offset;
3793 mda_get_prod(ndim, dim, prod);
3794 mda_get_range(ndim, span, st, endp);
3795 mda_get_offset_values(ndim, dist, prod, span);
3796 for (i = 0; i < ndim; i++)
3802 /* Copy/advance over elements between here and next part of slice */
3805 inc = array_copy(destPtr, dist[j],
3806 origPtr, orig_offset, origBitmap,
3807 typlen, typbyval, typalign);
3811 array_bitmap_copy(destBitmap, dest_offset,
3812 origBitmap, orig_offset,
3814 dest_offset += dist[j];
3815 orig_offset += dist[j];
3817 /* Copy new element at this slice position */
3818 inc = array_copy(destPtr, 1,
3819 srcPtr, src_offset, srcBitmap,
3820 typlen, typbyval, typalign);
3822 array_bitmap_copy(destBitmap, dest_offset,
3823 srcBitmap, src_offset,
3829 /* Advance over old element at this slice position */
3830 origPtr = array_seek(origPtr, orig_offset, origBitmap, 1,
3831 typlen, typbyval, typalign);
3833 } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
3835 /* don't miss any data at the end */
3836 array_copy(destPtr, orignitems - orig_offset,
3837 origPtr, orig_offset, origBitmap,
3838 typlen, typbyval, typalign);
3840 array_bitmap_copy(destBitmap, dest_offset,
3841 origBitmap, orig_offset,
3842 orignitems - orig_offset);
3846 * array_type_coerce -- allow explicit or assignment coercion from
3847 * one array type to another.
3849 * array_type_length_coerce -- the same, for cases where both type and length
3850 * coercion are done by a single function on the element type.
3852 * Caller should have already verified that the source element type can be
3853 * coerced into the target element type.
3856 array_type_coerce(PG_FUNCTION_ARGS)
3858 ArrayType *src = PG_GETARG_ARRAYTYPE_P(0);
3859 FmgrInfo *fmgr_info = fcinfo->flinfo;
3861 return array_type_length_coerce_internal(src, -1, false, fmgr_info);
3865 array_type_length_coerce(PG_FUNCTION_ARGS)
3867 ArrayType *src = PG_GETARG_ARRAYTYPE_P(0);
3868 int32 desttypmod = PG_GETARG_INT32(1);
3869 bool isExplicit = PG_GETARG_BOOL(2);
3870 FmgrInfo *fmgr_info = fcinfo->flinfo;
3872 return array_type_length_coerce_internal(src, desttypmod,
3873 isExplicit, fmgr_info);
3877 array_type_length_coerce_internal(ArrayType *src,
3880 FmgrInfo *fmgr_info)
3882 Oid src_elem_type = ARR_ELEMTYPE(src);
3887 FmgrInfo coerce_finfo;
3888 ArrayMapState amstate;
3890 atc_extra *my_extra;
3891 FunctionCallInfoData locfcinfo;
3894 * We arrange to look up the coercion function only once per series of
3895 * calls, assuming the input data type doesn't change underneath us.
3896 * (Output type can't change.)
3898 my_extra = (atc_extra *) fmgr_info->fn_extra;
3899 if (my_extra == NULL)
3901 fmgr_info->fn_extra = MemoryContextAllocZero(fmgr_info->fn_mcxt,
3903 my_extra = (atc_extra *) fmgr_info->fn_extra;
3906 if (my_extra->srctype != src_elem_type)
3908 Oid tgt_type = get_fn_expr_rettype(fmgr_info);
3912 if (tgt_type == InvalidOid)
3914 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3915 errmsg("could not determine target array type")));
3917 tgt_elem_type = get_element_type(tgt_type);
3918 if (tgt_elem_type == InvalidOid)
3920 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3921 errmsg("target type is not an array")));
3924 * We don't deal with domain constraints yet, so bail out. This isn't
3925 * currently a problem, because we also don't support arrays of domain
3926 * type elements either. But in the future we might. At that point
3927 * consideration should be given to removing the check below and
3928 * adding a domain constraints check to the coercion.
3930 if (getBaseType(tgt_elem_type) != tgt_elem_type)
3932 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3933 errmsg("array coercion to domain type elements not "
3934 "currently supported")));
3936 if (!find_coercion_pathway(tgt_elem_type, src_elem_type,
3937 COERCION_EXPLICIT, &funcId))
3939 /* should never happen, but check anyway */
3940 elog(ERROR, "no conversion function from %s to %s",
3941 format_type_be(src_elem_type),
3942 format_type_be(tgt_elem_type));
3944 if (OidIsValid(funcId))
3945 fmgr_info_cxt(funcId, &my_extra->coerce_finfo, fmgr_info->fn_mcxt);
3947 my_extra->coerce_finfo.fn_oid = InvalidOid;
3948 my_extra->srctype = src_elem_type;
3949 my_extra->desttype = tgt_elem_type;
3953 * If it's binary-compatible, modify the element type in the array header,
3954 * but otherwise leave the array as we received it.
3956 if (my_extra->coerce_finfo.fn_oid == InvalidOid)
3960 result = (ArrayType *) DatumGetPointer(datumCopy(PointerGetDatum(src),
3962 ARR_ELEMTYPE(result) = my_extra->desttype;
3963 PG_RETURN_ARRAYTYPE_P(result);
3967 * Use array_map to apply the function to each array element.
3969 * We pass on the desttypmod and isExplicit flags whether or not the
3970 * function wants them.
3972 InitFunctionCallInfoData(locfcinfo, &my_extra->coerce_finfo, 3,
3974 locfcinfo.arg[0] = PointerGetDatum(src);
3975 locfcinfo.arg[1] = Int32GetDatum(desttypmod);
3976 locfcinfo.arg[2] = BoolGetDatum(isExplicit);
3977 locfcinfo.argnull[0] = false;
3978 locfcinfo.argnull[1] = false;
3979 locfcinfo.argnull[2] = false;
3981 return array_map(&locfcinfo, my_extra->srctype, my_extra->desttype,
3982 &my_extra->amstate);
3986 * array_length_coerce -- apply the element type's length-coercion routine
3987 * to each element of the given array.
3990 array_length_coerce(PG_FUNCTION_ARGS)
3992 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
3993 int32 desttypmod = PG_GETARG_INT32(1);
3994 bool isExplicit = PG_GETARG_BOOL(2);
3995 FmgrInfo *fmgr_info = fcinfo->flinfo;
3999 FmgrInfo coerce_finfo;
4000 ArrayMapState amstate;
4002 alc_extra *my_extra;
4003 FunctionCallInfoData locfcinfo;
4005 /* If no typmod is provided, shortcircuit the whole thing */
4007 PG_RETURN_ARRAYTYPE_P(v);
4010 * We arrange to look up the element type's coercion function only once
4011 * per series of calls, assuming the element type doesn't change
4014 my_extra = (alc_extra *) fmgr_info->fn_extra;
4015 if (my_extra == NULL)
4017 fmgr_info->fn_extra = MemoryContextAllocZero(fmgr_info->fn_mcxt,
4019 my_extra = (alc_extra *) fmgr_info->fn_extra;
4022 if (my_extra->elemtype != ARR_ELEMTYPE(v))
4026 funcId = find_typmod_coercion_function(ARR_ELEMTYPE(v));
4028 if (OidIsValid(funcId))
4029 fmgr_info_cxt(funcId, &my_extra->coerce_finfo, fmgr_info->fn_mcxt);
4031 my_extra->coerce_finfo.fn_oid = InvalidOid;
4032 my_extra->elemtype = ARR_ELEMTYPE(v);
4036 * If we didn't find a coercion function, return the array unmodified
4037 * (this should not happen in the normal course of things, but might
4038 * happen if this function is called manually).
4040 if (my_extra->coerce_finfo.fn_oid == InvalidOid)
4041 PG_RETURN_ARRAYTYPE_P(v);
4044 * Use array_map to apply the function to each array element.
4046 * Note: we pass isExplicit whether or not the function wants it ...
4048 InitFunctionCallInfoData(locfcinfo, &my_extra->coerce_finfo, 3,
4050 locfcinfo.arg[0] = PointerGetDatum(v);
4051 locfcinfo.arg[1] = Int32GetDatum(desttypmod);
4052 locfcinfo.arg[2] = BoolGetDatum(isExplicit);
4053 locfcinfo.argnull[0] = false;
4054 locfcinfo.argnull[1] = false;
4055 locfcinfo.argnull[2] = false;
4057 return array_map(&locfcinfo, ARR_ELEMTYPE(v), ARR_ELEMTYPE(v),
4058 &my_extra->amstate);
4062 * accumArrayResult - accumulate one (more) Datum for an array result
4064 * astate is working state (NULL on first call)
4065 * rcontext is where to keep working state
4068 accumArrayResult(ArrayBuildState *astate,
4069 Datum dvalue, bool disnull,
4071 MemoryContext rcontext)
4073 MemoryContext arr_context,
4078 /* First time through --- initialize */
4080 /* Make a temporary context to hold all the junk */
4081 arr_context = AllocSetContextCreate(rcontext,
4083 ALLOCSET_DEFAULT_MINSIZE,
4084 ALLOCSET_DEFAULT_INITSIZE,
4085 ALLOCSET_DEFAULT_MAXSIZE);
4086 oldcontext = MemoryContextSwitchTo(arr_context);
4087 astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
4088 astate->mcontext = arr_context;
4089 astate->dvalues = (Datum *)
4090 palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
4091 astate->dnulls = (bool *)
4092 palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(bool));
4094 astate->element_type = element_type;
4095 get_typlenbyvalalign(element_type,
4102 oldcontext = MemoryContextSwitchTo(astate->mcontext);
4103 Assert(astate->element_type == element_type);
4104 /* enlarge dvalues[]/dnulls[] if needed */
4105 if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
4107 astate->dvalues = (Datum *)
4108 repalloc(astate->dvalues,
4109 (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
4110 astate->dnulls = (bool *)
4111 repalloc(astate->dnulls,
4112 (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(bool));
4116 /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
4117 if (!disnull && !astate->typbyval)
4118 dvalue = datumCopy(dvalue, astate->typbyval, astate->typlen);
4120 astate->dvalues[astate->nelems] = dvalue;
4121 astate->dnulls[astate->nelems] = disnull;
4124 MemoryContextSwitchTo(oldcontext);
4130 * makeArrayResult - produce 1-D final result of accumArrayResult
4132 * astate is working state (not NULL)
4133 * rcontext is where to construct result
4136 makeArrayResult(ArrayBuildState *astate,
4137 MemoryContext rcontext)
4142 dims[0] = astate->nelems;
4145 return makeMdArrayResult(astate, 1, dims, lbs, rcontext);
4149 * makeMdArrayResult - produce multi-D final result of accumArrayResult
4151 * beware: no check that specified dimensions match the number of values
4154 * astate is working state (not NULL)
4155 * rcontext is where to construct result
4158 makeMdArrayResult(ArrayBuildState *astate,
4162 MemoryContext rcontext)
4165 MemoryContext oldcontext;
4167 /* Build the final array result in rcontext */
4168 oldcontext = MemoryContextSwitchTo(rcontext);
4170 result = construct_md_array(astate->dvalues,
4175 astate->element_type,
4180 MemoryContextSwitchTo(oldcontext);
4182 /* Clean up all the junk */
4183 MemoryContextDelete(astate->mcontext);
4185 return PointerGetDatum(result);
4189 array_larger(PG_FUNCTION_ARGS)
4195 v1 = PG_GETARG_ARRAYTYPE_P(0);
4196 v2 = PG_GETARG_ARRAYTYPE_P(1);
4198 result = ((array_cmp(fcinfo) > 0) ? v1 : v2);
4200 PG_RETURN_ARRAYTYPE_P(result);
4204 array_smaller(PG_FUNCTION_ARGS)
4210 v1 = PG_GETARG_ARRAYTYPE_P(0);
4211 v2 = PG_GETARG_ARRAYTYPE_P(1);
4213 result = ((array_cmp(fcinfo) < 0) ? v1 : v2);
4215 PG_RETURN_ARRAYTYPE_P(result);