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.133 2006/10/04 00:29:58 momjian Exp $
13 *-------------------------------------------------------------------------
19 #include "access/tupmacs.h"
20 #include "libpq/pqformat.h"
21 #include "parser/parse_coerce.h"
22 #include "utils/array.h"
23 #include "utils/builtins.h"
24 #include "utils/datum.h"
25 #include "utils/lsyscache.h"
26 #include "utils/memutils.h"
27 #include "utils/typcache.h"
33 bool Array_nulls = true;
46 ARRAY_QUOTED_ELEM_STARTED,
47 ARRAY_QUOTED_ELEM_COMPLETED,
49 ARRAY_LEVEL_COMPLETED,
53 static int ArrayCount(const char *str, int *dim, char typdelim);
54 static void ReadArrayStr(char *arrayStr, const char *origStr,
55 int nitems, int ndim, int *dim,
56 FmgrInfo *inputproc, Oid typioparam, int32 typmod,
58 int typlen, bool typbyval, char typalign,
59 Datum *values, bool *nulls,
60 bool *hasnulls, int32 *nbytes);
61 static void ReadArrayBinary(StringInfo buf, int nitems,
62 FmgrInfo *receiveproc, Oid typioparam, int32 typmod,
63 int typlen, bool typbyval, char typalign,
64 Datum *values, bool *nulls,
65 bool *hasnulls, int32 *nbytes);
66 static void CopyArrayEls(ArrayType *array,
67 Datum *values, bool *nulls, int nitems,
68 int typlen, bool typbyval, char typalign,
70 static bool array_get_isnull(const bits8 *nullbitmap, int offset);
71 static void array_set_isnull(bits8 *nullbitmap, int offset, bool isNull);
72 static Datum ArrayCast(char *value, bool byval, int len);
73 static int ArrayCastAndSet(Datum src,
74 int typlen, bool typbyval, char typalign,
76 static char *array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
77 int typlen, bool typbyval, char typalign);
78 static int array_nelems_size(char *ptr, int offset, bits8 *nullbitmap,
79 int nitems, int typlen, bool typbyval, char typalign);
80 static int array_copy(char *destptr, int nitems,
81 char *srcptr, int offset, bits8 *nullbitmap,
82 int typlen, bool typbyval, char typalign);
83 static int array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
84 int ndim, int *dim, int *lb,
86 int typlen, bool typbyval, char typalign);
87 static void array_extract_slice(ArrayType *newarray,
88 int ndim, int *dim, int *lb,
89 char *arraydataptr, bits8 *arraynullsptr,
91 int typlen, bool typbyval, char typalign);
92 static void array_insert_slice(ArrayType *destArray, ArrayType *origArray,
94 int ndim, int *dim, int *lb,
96 int typlen, bool typbyval, char typalign);
97 static int array_cmp(FunctionCallInfo fcinfo);
98 static Datum array_type_length_coerce_internal(ArrayType *src,
101 FmgrInfo *fmgr_info);
106 * converts an array from the external format in "string" to
107 * its internal format.
110 * the internal representation of the input array
113 array_in(PG_FUNCTION_ARGS)
115 char *string = PG_GETARG_CSTRING(0); /* external form */
116 Oid element_type = PG_GETARG_OID(1); /* type of an array
118 int32 typmod = PG_GETARG_INT32(2); /* typmod for array elements */
137 ArrayMetaState *my_extra;
140 * We arrange to look up info about element type, including its input
141 * conversion proc, only once per series of calls, assuming the element
142 * type doesn't change underneath us.
144 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
145 if (my_extra == NULL)
147 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
148 sizeof(ArrayMetaState));
149 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
150 my_extra->element_type = ~element_type;
153 if (my_extra->element_type != element_type)
156 * Get info about element type, including its input conversion proc
158 get_type_io_data(element_type, IOFunc_input,
159 &my_extra->typlen, &my_extra->typbyval,
160 &my_extra->typalign, &my_extra->typdelim,
161 &my_extra->typioparam, &my_extra->typiofunc);
162 fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
163 fcinfo->flinfo->fn_mcxt);
164 my_extra->element_type = element_type;
166 typlen = my_extra->typlen;
167 typbyval = my_extra->typbyval;
168 typalign = my_extra->typalign;
169 typdelim = my_extra->typdelim;
170 typioparam = my_extra->typioparam;
172 /* Make a modifiable copy of the input */
173 string_save = pstrdup(string);
176 * If the input string starts with dimension info, read and use that.
177 * Otherwise, we require the input to be in curly-brace style, and we
178 * prescan the input to determine dimensions.
180 * Dimension info takes the form of one or more [n] or [m:n] items. The
181 * outer loop iterates once per dimension item.
191 * Note: we currently allow whitespace between, but not within,
194 while (isspace((unsigned char) *p))
197 break; /* no more dimension items */
201 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
202 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
205 for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++);
206 if (q == p) /* no digits? */
208 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
209 errmsg("missing dimension value")));
215 lBound[ndim] = atoi(p);
217 for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++);
218 if (q == p) /* no digits? */
220 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
221 errmsg("missing dimension value")));
230 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
231 errmsg("missing \"]\" in array dimensions")));
236 if (ub < lBound[ndim])
238 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
239 errmsg("upper bound cannot be less than lower bound")));
241 dim[ndim] = ub - lBound[ndim] + 1;
247 /* No array dimensions, so intuit dimensions from brace structure */
250 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
251 errmsg("array value must start with \"{\" or dimension information")));
252 ndim = ArrayCount(p, dim, typdelim);
253 for (i = 0; i < ndim; i++)
261 /* If array dimensions are given, expect '=' operator */
262 if (strncmp(p, ASSGN, strlen(ASSGN)) != 0)
264 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
265 errmsg("missing assignment operator")));
267 while (isspace((unsigned char) *p))
271 * intuit dimensions from brace structure -- it better match what we
276 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
277 errmsg("array value must start with \"{\" or dimension information")));
278 ndim_braces = ArrayCount(p, dim_braces, typdelim);
279 if (ndim_braces != ndim)
281 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
282 errmsg("array dimensions incompatible with array literal")));
283 for (i = 0; i < ndim; ++i)
285 if (dim[i] != dim_braces[i])
287 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
288 errmsg("array dimensions incompatible with array literal")));
293 printf("array_in- ndim %d (", ndim);
294 for (i = 0; i < ndim; i++)
296 printf(" %d", dim[i]);
298 printf(") for %s\n", string);
301 /* This checks for overflow of the array dimensions */
302 nitems = ArrayGetNItems(ndim, dim);
305 PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
307 dataPtr = (Datum *) palloc(nitems * sizeof(Datum));
308 nullsPtr = (bool *) palloc(nitems * sizeof(bool));
309 ReadArrayStr(p, string,
311 &my_extra->proc, typioparam, typmod,
313 typlen, typbyval, typalign,
318 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
319 nbytes += dataoffset;
323 dataoffset = 0; /* marker for no null bitmap */
324 nbytes += ARR_OVERHEAD_NONULLS(ndim);
326 retval = (ArrayType *) palloc(nbytes);
327 retval->size = nbytes;
329 retval->dataoffset = dataoffset;
330 retval->elemtype = element_type;
331 memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
332 memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
335 dataPtr, nullsPtr, nitems,
336 typlen, typbyval, typalign,
343 PG_RETURN_ARRAYTYPE_P(retval);
348 * Determines the dimensions for an array string.
350 * Returns number of dimensions as function result. The axis lengths are
351 * returned in dim[], which must be of size MAXDIM.
354 ArrayCount(const char *str, int *dim, char typdelim)
362 bool in_quotes = false;
363 bool eoArray = false;
364 bool empty_array = true;
366 ArrayParseState parse_state = ARRAY_NO_LEVEL;
368 for (i = 0; i < MAXDIM; ++i)
370 temp[i] = dim[i] = 0;
371 nelems_last[i] = nelems[i] = 1;
377 bool itemdone = false;
381 if (parse_state == ARRAY_ELEM_STARTED ||
382 parse_state == ARRAY_QUOTED_ELEM_STARTED)
388 /* Signal a premature end of the string */
390 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
391 errmsg("malformed array literal: \"%s\"", str)));
396 * An escape must be after a level start, after an element
397 * start, or after an element delimiter. In any case we
398 * now must be past an element start.
400 if (parse_state != ARRAY_LEVEL_STARTED &&
401 parse_state != ARRAY_ELEM_STARTED &&
402 parse_state != ARRAY_QUOTED_ELEM_STARTED &&
403 parse_state != ARRAY_ELEM_DELIMITED)
405 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
406 errmsg("malformed array literal: \"%s\"", str)));
407 if (parse_state != ARRAY_QUOTED_ELEM_STARTED)
408 parse_state = ARRAY_ELEM_STARTED;
409 /* skip the escaped character */
414 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
415 errmsg("malformed array literal: \"%s\"", str)));
420 * A quote must be after a level start, after a quoted
421 * element start, or after an element delimiter. In any
422 * case we now must be past an element start.
424 if (parse_state != ARRAY_LEVEL_STARTED &&
425 parse_state != ARRAY_QUOTED_ELEM_STARTED &&
426 parse_state != ARRAY_ELEM_DELIMITED)
428 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
429 errmsg("malformed array literal: \"%s\"", str)));
430 in_quotes = !in_quotes;
432 parse_state = ARRAY_QUOTED_ELEM_STARTED;
434 parse_state = ARRAY_QUOTED_ELEM_COMPLETED;
440 * A left brace can occur if no nesting has occurred
441 * yet, after a level start, or after a level
444 if (parse_state != ARRAY_NO_LEVEL &&
445 parse_state != ARRAY_LEVEL_STARTED &&
446 parse_state != ARRAY_LEVEL_DELIMITED)
448 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
449 errmsg("malformed array literal: \"%s\"", str)));
450 parse_state = ARRAY_LEVEL_STARTED;
451 if (nest_level >= MAXDIM)
453 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
454 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
455 nest_level, MAXDIM)));
456 temp[nest_level] = 0;
458 if (ndim < nest_level)
466 * A right brace can occur after an element start, an
467 * element completion, a quoted element completion, or
468 * a level completion.
470 if (parse_state != ARRAY_ELEM_STARTED &&
471 parse_state != ARRAY_ELEM_COMPLETED &&
472 parse_state != ARRAY_QUOTED_ELEM_COMPLETED &&
473 parse_state != ARRAY_LEVEL_COMPLETED &&
474 !(nest_level == 1 && parse_state == ARRAY_LEVEL_STARTED))
476 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
477 errmsg("malformed array literal: \"%s\"", str)));
478 parse_state = ARRAY_LEVEL_COMPLETED;
481 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
482 errmsg("malformed array literal: \"%s\"", str)));
485 if ((nelems_last[nest_level] != 1) &&
486 (nelems[nest_level] != nelems_last[nest_level]))
488 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
489 errmsg("multidimensional arrays must have "
490 "array expressions with matching "
492 nelems_last[nest_level] = nelems[nest_level];
493 nelems[nest_level] = 1;
495 eoArray = itemdone = true;
499 * We don't set itemdone here; see comments in
502 temp[nest_level - 1]++;
509 if (*ptr == typdelim)
512 * Delimiters can occur after an element start, an
513 * element completion, a quoted element
514 * completion, or a level completion.
516 if (parse_state != ARRAY_ELEM_STARTED &&
517 parse_state != ARRAY_ELEM_COMPLETED &&
518 parse_state != ARRAY_QUOTED_ELEM_COMPLETED &&
519 parse_state != ARRAY_LEVEL_COMPLETED)
521 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
522 errmsg("malformed array literal: \"%s\"", str)));
523 if (parse_state == ARRAY_LEVEL_COMPLETED)
524 parse_state = ARRAY_LEVEL_DELIMITED;
526 parse_state = ARRAY_ELEM_DELIMITED;
528 nelems[nest_level - 1]++;
530 else if (!isspace((unsigned char) *ptr))
533 * Other non-space characters must be after a
534 * level start, after an element start, or after
535 * an element delimiter. In any case we now must
536 * be past an element start.
538 if (parse_state != ARRAY_LEVEL_STARTED &&
539 parse_state != ARRAY_ELEM_STARTED &&
540 parse_state != ARRAY_ELEM_DELIMITED)
542 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
543 errmsg("malformed array literal: \"%s\"", str)));
544 parse_state = ARRAY_ELEM_STARTED;
556 /* only whitespace is allowed after the closing brace */
559 if (!isspace((unsigned char) *ptr++))
561 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
562 errmsg("malformed array literal: \"%s\"", str)));
565 /* special case for an empty array */
569 for (i = 0; i < ndim; ++i)
577 * parses the array string pointed to by "arrayStr" and converts the values
578 * to internal format. Unspecified elements are initialized to nulls.
579 * The array dimensions must already have been determined.
582 * arrayStr: the string to parse.
583 * CAUTION: the contents of "arrayStr" will be modified!
584 * origStr: the unmodified input string, used only in error messages.
585 * nitems: total number of array elements, as already determined.
586 * ndim: number of array dimensions
587 * dim[]: array axis lengths
588 * inputproc: type-specific input procedure for element datatype.
589 * typioparam, typmod: auxiliary values to pass to inputproc.
590 * typdelim: the value delimiter (type-specific).
591 * typlen, typbyval, typalign: storage parameters of element datatype.
594 * values[]: filled with converted data values.
595 * nulls[]: filled with is-null markers.
596 * *hasnulls: set TRUE iff there are any null elements.
597 * *nbytes: set to total size of data area needed (including alignment
598 * padding but not including array header overhead).
600 * Note that values[] and nulls[] are allocated by the caller, and must have
604 ReadArrayStr(char *arrayStr,
624 bool in_quotes = false;
625 bool eoArray = false;
631 mda_get_prod(ndim, dim, prod);
632 MemSet(indx, 0, sizeof(indx));
634 /* Initialize is-null markers to true */
635 memset(nulls, true, nitems * sizeof(bool));
638 * We have to remove " and \ characters to create a clean item value to
639 * pass to the datatype input routine. We overwrite each item value
640 * in-place within arrayStr to do this. srcptr is the current scan point,
641 * and dstptr is where we are copying to.
643 * We also want to suppress leading and trailing unquoted whitespace. We
644 * use the leadingspace flag to suppress leading space. Trailing space is
645 * tracked by using dstendptr to point to the last significant output
648 * The error checking in this routine is mostly pro-forma, since we expect
649 * that ArrayCount() already validated the string.
654 bool itemdone = false;
655 bool leadingspace = true;
656 bool hasquoting = false;
662 itemstart = dstptr = dstendptr = srcptr;
669 /* Signal a premature end of the string */
671 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
672 errmsg("malformed array literal: \"%s\"",
676 /* Skip backslash, copy next character as-is. */
680 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
681 errmsg("malformed array literal: \"%s\"",
683 *dstptr++ = *srcptr++;
684 /* Treat the escaped character as non-whitespace */
685 leadingspace = false;
687 hasquoting = true; /* can't be a NULL marker */
690 in_quotes = !in_quotes;
692 leadingspace = false;
696 * Advance dstendptr when we exit in_quotes; this
697 * saves having to do it in all the other in_quotes
702 hasquoting = true; /* can't be a NULL marker */
708 if (nest_level >= ndim)
710 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
711 errmsg("malformed array literal: \"%s\"",
714 indx[nest_level - 1] = 0;
718 *dstptr++ = *srcptr++;
725 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
726 errmsg("malformed array literal: \"%s\"",
729 i = ArrayGetOffset0(ndim, indx, prod);
730 indx[nest_level - 1] = 0;
733 eoArray = itemdone = true;
735 indx[nest_level - 1]++;
739 *dstptr++ = *srcptr++;
743 *dstptr++ = *srcptr++;
744 else if (*srcptr == typdelim)
747 i = ArrayGetOffset0(ndim, indx, prod);
752 else if (isspace((unsigned char) *srcptr))
755 * If leading space, drop it immediately. Else, copy
756 * but don't advance dstendptr.
761 *dstptr++ = *srcptr++;
765 *dstptr++ = *srcptr++;
766 leadingspace = false;
773 Assert(dstptr < srcptr);
776 if (i < 0 || i >= nitems)
778 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
779 errmsg("malformed array literal: \"%s\"",
782 if (Array_nulls && !hasquoting &&
783 pg_strcasecmp(itemstart, "NULL") == 0)
785 /* it's a NULL item */
786 values[i] = InputFunctionCall(inputproc, NULL,
792 values[i] = InputFunctionCall(inputproc, itemstart,
799 * Check for nulls, compute total data space needed
803 for (i = 0; i < nitems; i++)
809 /* let's just make sure data is not toasted */
811 values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
812 totbytes = att_addlength(totbytes, typlen, values[i]);
813 totbytes = att_align(totbytes, typalign);
814 /* check for overflow of total request */
815 if (!AllocSizeIsValid(totbytes))
817 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
818 errmsg("array size exceeds the maximum allowed (%d)",
819 (int) MaxAllocSize)));
828 * Copy data into an array object from a temporary array of Datums.
830 * array: array object (with header fields already filled in)
831 * values: array of Datums to be copied
832 * nulls: array of is-null flags (can be NULL if no nulls)
833 * nitems: number of Datums to be copied
834 * typbyval, typlen, typalign: info about element datatype
835 * freedata: if TRUE and element type is pass-by-ref, pfree data values
836 * referenced by Datums after copying them.
838 * If the input data is of varlena type, the caller must have ensured that
839 * the values are not toasted. (Doing it here doesn't work since the
840 * caller has already allocated space for the array...)
843 CopyArrayEls(ArrayType *array,
852 char *p = ARR_DATA_PTR(array);
853 bits8 *bitmap = ARR_NULLBITMAP(array);
861 for (i = 0; i < nitems; i++)
863 if (nulls && nulls[i])
865 if (!bitmap) /* shouldn't happen */
866 elog(ERROR, "null array element where not supported");
867 /* bitmap bit stays 0 */
872 p += ArrayCastAndSet(values[i], typlen, typbyval, typalign, p);
874 pfree(DatumGetPointer(values[i]));
879 if (bitmask == 0x100)
888 if (bitmap && bitmask != 1)
894 * takes the internal representation of an array and returns a string
895 * containing the array in its external format.
898 array_out(PG_FUNCTION_ARGS)
900 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
901 Oid element_type = ARR_ELEMTYPE(v);
910 dims_str[(MAXDIM * 33) + 2];
913 * 33 per dim since we assume 15 digits per number + ':' +'[]'
915 * +2 allows for assignment operator + trailing null
930 ArrayMetaState *my_extra;
933 * We arrange to look up info about element type, including its output
934 * conversion proc, only once per series of calls, assuming the element
935 * type doesn't change underneath us.
937 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
938 if (my_extra == NULL)
940 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
941 sizeof(ArrayMetaState));
942 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
943 my_extra->element_type = ~element_type;
946 if (my_extra->element_type != element_type)
949 * Get info about element type, including its output conversion proc
951 get_type_io_data(element_type, IOFunc_output,
952 &my_extra->typlen, &my_extra->typbyval,
953 &my_extra->typalign, &my_extra->typdelim,
954 &my_extra->typioparam, &my_extra->typiofunc);
955 fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
956 fcinfo->flinfo->fn_mcxt);
957 my_extra->element_type = element_type;
959 typlen = my_extra->typlen;
960 typbyval = my_extra->typbyval;
961 typalign = my_extra->typalign;
962 typdelim = my_extra->typdelim;
967 nitems = ArrayGetNItems(ndim, dims);
971 retval = pstrdup("{}");
972 PG_RETURN_CSTRING(retval);
976 * we will need to add explicit dimensions if any dimension has a lower
977 * bound other than one
979 for (i = 0; i < ndim; i++)
989 * Convert all values to string form, count total space needed (including
990 * any overhead such as escaping backslashes), and detect whether each
991 * item needs double quotes.
993 values = (char **) palloc(nitems * sizeof(char *));
994 needquotes = (bool *) palloc(nitems * sizeof(bool));
995 overall_length = 1; /* don't forget to count \0 at end. */
998 bitmap = ARR_NULLBITMAP(v);
1001 for (i = 0; i < nitems; i++)
1005 /* Get source element, checking for NULL */
1006 if (bitmap && (*bitmap & bitmask) == 0)
1008 values[i] = pstrdup("NULL");
1009 overall_length += 4;
1016 itemvalue = fetch_att(p, typbyval, typlen);
1017 values[i] = OutputFunctionCall(&my_extra->proc, itemvalue);
1018 p = att_addlength(p, typlen, PointerGetDatum(p));
1019 p = (char *) att_align(p, typalign);
1021 /* count data plus backslashes; detect chars needing quotes */
1022 if (values[i][0] == '\0')
1023 needquote = true; /* force quotes for empty string */
1024 else if (pg_strcasecmp(values[i], "NULL") == 0)
1025 needquote = true; /* force quotes for literal NULL */
1029 for (tmp = values[i]; *tmp != '\0'; tmp++)
1033 overall_length += 1;
1034 if (ch == '"' || ch == '\\')
1038 overall_length += 1;
1041 else if (ch == '{' || ch == '}' || ch == typdelim ||
1042 isspace((unsigned char) ch))
1047 needquotes[i] = needquote;
1049 /* Count the pair of double quotes, if needed */
1051 overall_length += 2;
1053 overall_length += 1;
1055 /* advance bitmap pointer if any */
1059 if (bitmask == 0x100)
1068 * count total number of curly braces in output string
1070 for (i = j = 0, k = 1; i < ndim; i++)
1071 k *= dims[i], j += k;
1075 /* add explicit dimensions if required */
1078 char *ptr = dims_str;
1080 for (i = 0; i < ndim; i++)
1082 sprintf(ptr, "[%d:%d]", lb[i], lb[i] + dims[i] - 1);
1089 retval = (char *) palloc(strlen(dims_str) + overall_length + 2 * j);
1092 #define APPENDSTR(str) (strcpy(p, (str)), p += strlen(p))
1093 #define APPENDCHAR(ch) (*p++ = (ch), *p = '\0')
1096 APPENDSTR(dims_str);
1098 for (i = 0; i < ndim; i++)
1104 for (i = j; i < ndim - 1; i++)
1111 for (tmp = values[k]; *tmp; tmp++)
1115 if (ch == '"' || ch == '\\')
1121 APPENDSTR(values[k]);
1126 APPENDSTR(values[k]);
1129 for (i = ndim - 1; i >= 0; i--)
1131 indx[i] = (indx[i] + 1) % dims[i];
1134 APPENDCHAR(typdelim);
1149 PG_RETURN_CSTRING(retval);
1154 * converts an array from the external binary format to
1155 * its internal format.
1158 * the internal representation of the input array
1161 array_recv(PG_FUNCTION_ARGS)
1163 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
1164 Oid spec_element_type = PG_GETARG_OID(1); /* type of an array
1166 int32 typmod = PG_GETARG_INT32(2); /* typmod for array elements */
1184 ArrayMetaState *my_extra;
1186 /* Get the array header information */
1187 ndim = pq_getmsgint(buf, 4);
1188 if (ndim < 0) /* we do allow zero-dimension arrays */
1190 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1191 errmsg("invalid number of dimensions: %d", ndim)));
1194 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1195 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
1198 flags = pq_getmsgint(buf, 4);
1199 if (flags != 0 && flags != 1)
1201 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1202 errmsg("invalid array flags")));
1204 element_type = pq_getmsgint(buf, sizeof(Oid));
1205 if (element_type != spec_element_type)
1207 /* XXX Can we allow taking the input element type in any cases? */
1209 (errcode(ERRCODE_DATATYPE_MISMATCH),
1210 errmsg("wrong element type")));
1213 for (i = 0; i < ndim; i++)
1215 dim[i] = pq_getmsgint(buf, 4);
1216 lBound[i] = pq_getmsgint(buf, 4);
1219 /* This checks for overflow of array dimensions */
1220 nitems = ArrayGetNItems(ndim, dim);
1223 * We arrange to look up info about element type, including its receive
1224 * conversion proc, only once per series of calls, assuming the element
1225 * type doesn't change underneath us.
1227 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1228 if (my_extra == NULL)
1230 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1231 sizeof(ArrayMetaState));
1232 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1233 my_extra->element_type = ~element_type;
1236 if (my_extra->element_type != element_type)
1238 /* Get info about element type, including its receive proc */
1239 get_type_io_data(element_type, IOFunc_receive,
1240 &my_extra->typlen, &my_extra->typbyval,
1241 &my_extra->typalign, &my_extra->typdelim,
1242 &my_extra->typioparam, &my_extra->typiofunc);
1243 if (!OidIsValid(my_extra->typiofunc))
1245 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1246 errmsg("no binary input function available for type %s",
1247 format_type_be(element_type))));
1248 fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1249 fcinfo->flinfo->fn_mcxt);
1250 my_extra->element_type = element_type;
1255 /* Return empty array ... but not till we've validated element_type */
1256 PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
1259 typlen = my_extra->typlen;
1260 typbyval = my_extra->typbyval;
1261 typalign = my_extra->typalign;
1262 typioparam = my_extra->typioparam;
1264 dataPtr = (Datum *) palloc(nitems * sizeof(Datum));
1265 nullsPtr = (bool *) palloc(nitems * sizeof(bool));
1266 ReadArrayBinary(buf, nitems,
1267 &my_extra->proc, typioparam, typmod,
1268 typlen, typbyval, typalign,
1270 &hasnulls, &nbytes);
1273 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
1274 nbytes += dataoffset;
1278 dataoffset = 0; /* marker for no null bitmap */
1279 nbytes += ARR_OVERHEAD_NONULLS(ndim);
1281 retval = (ArrayType *) palloc(nbytes);
1282 retval->size = nbytes;
1283 retval->ndim = ndim;
1284 retval->dataoffset = dataoffset;
1285 retval->elemtype = element_type;
1286 memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
1287 memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
1289 CopyArrayEls(retval,
1290 dataPtr, nullsPtr, nitems,
1291 typlen, typbyval, typalign,
1297 PG_RETURN_ARRAYTYPE_P(retval);
1302 * collect the data elements of an array being read in binary style.
1305 * buf: the data buffer to read from.
1306 * nitems: total number of array elements (already read).
1307 * receiveproc: type-specific receive procedure for element datatype.
1308 * typioparam, typmod: auxiliary values to pass to receiveproc.
1309 * typlen, typbyval, typalign: storage parameters of element datatype.
1312 * values[]: filled with converted data values.
1313 * nulls[]: filled with is-null markers.
1314 * *hasnulls: set TRUE iff there are any null elements.
1315 * *nbytes: set to total size of data area needed (including alignment
1316 * padding but not including array header overhead).
1318 * Note that values[] and nulls[] are allocated by the caller, and must have
1322 ReadArrayBinary(StringInfo buf,
1324 FmgrInfo *receiveproc,
1339 for (i = 0; i < nitems; i++)
1342 StringInfoData elem_buf;
1345 /* Get and check the item length */
1346 itemlen = pq_getmsgint(buf, 4);
1347 if (itemlen < -1 || itemlen > (buf->len - buf->cursor))
1349 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1350 errmsg("insufficient data left in message")));
1354 /* -1 length means NULL */
1355 values[i] = ReceiveFunctionCall(receiveproc, NULL,
1356 typioparam, typmod);
1362 * Rather than copying data around, we just set up a phony StringInfo
1363 * pointing to the correct portion of the input buffer. We assume we
1364 * can scribble on the input buffer so as to maintain the convention
1365 * that StringInfos have a trailing null.
1367 elem_buf.data = &buf->data[buf->cursor];
1368 elem_buf.maxlen = itemlen + 1;
1369 elem_buf.len = itemlen;
1370 elem_buf.cursor = 0;
1372 buf->cursor += itemlen;
1374 csave = buf->data[buf->cursor];
1375 buf->data[buf->cursor] = '\0';
1377 /* Now call the element's receiveproc */
1378 values[i] = ReceiveFunctionCall(receiveproc, &elem_buf,
1379 typioparam, typmod);
1382 /* Trouble if it didn't eat the whole buffer */
1383 if (elem_buf.cursor != itemlen)
1385 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1386 errmsg("improper binary format in array element %d",
1389 buf->data[buf->cursor] = csave;
1393 * Check for nulls, compute total data space needed
1397 for (i = 0; i < nitems; i++)
1403 /* let's just make sure data is not toasted */
1405 values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
1406 totbytes = att_addlength(totbytes, typlen, values[i]);
1407 totbytes = att_align(totbytes, typalign);
1408 /* check for overflow of total request */
1409 if (!AllocSizeIsValid(totbytes))
1411 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1412 errmsg("array size exceeds the maximum allowed (%d)",
1413 (int) MaxAllocSize)));
1416 *hasnulls = hasnull;
1423 * takes the internal representation of an array and returns a bytea
1424 * containing the array in its external binary format.
1427 array_send(PG_FUNCTION_ARGS)
1429 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1430 Oid element_type = ARR_ELEMTYPE(v);
1442 ArrayMetaState *my_extra;
1445 * We arrange to look up info about element type, including its send
1446 * conversion proc, only once per series of calls, assuming the element
1447 * type doesn't change underneath us.
1449 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1450 if (my_extra == NULL)
1452 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1453 sizeof(ArrayMetaState));
1454 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1455 my_extra->element_type = ~element_type;
1458 if (my_extra->element_type != element_type)
1460 /* Get info about element type, including its send proc */
1461 get_type_io_data(element_type, IOFunc_send,
1462 &my_extra->typlen, &my_extra->typbyval,
1463 &my_extra->typalign, &my_extra->typdelim,
1464 &my_extra->typioparam, &my_extra->typiofunc);
1465 if (!OidIsValid(my_extra->typiofunc))
1467 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1468 errmsg("no binary output function available for type %s",
1469 format_type_be(element_type))));
1470 fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1471 fcinfo->flinfo->fn_mcxt);
1472 my_extra->element_type = element_type;
1474 typlen = my_extra->typlen;
1475 typbyval = my_extra->typbyval;
1476 typalign = my_extra->typalign;
1480 nitems = ArrayGetNItems(ndim, dim);
1482 pq_begintypsend(&buf);
1484 /* Send the array header information */
1485 pq_sendint(&buf, ndim, 4);
1486 pq_sendint(&buf, ARR_HASNULL(v) ? 1 : 0, 4);
1487 pq_sendint(&buf, element_type, sizeof(Oid));
1488 for (i = 0; i < ndim; i++)
1490 pq_sendint(&buf, ARR_DIMS(v)[i], 4);
1491 pq_sendint(&buf, ARR_LBOUND(v)[i], 4);
1494 /* Send the array elements using the element's own sendproc */
1495 p = ARR_DATA_PTR(v);
1496 bitmap = ARR_NULLBITMAP(v);
1499 for (i = 0; i < nitems; i++)
1501 /* Get source element, checking for NULL */
1502 if (bitmap && (*bitmap & bitmask) == 0)
1504 /* -1 length means a NULL */
1505 pq_sendint(&buf, -1, 4);
1512 itemvalue = fetch_att(p, typbyval, typlen);
1513 outputbytes = SendFunctionCall(&my_extra->proc, itemvalue);
1514 pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
1515 pq_sendbytes(&buf, VARDATA(outputbytes),
1516 VARSIZE(outputbytes) - VARHDRSZ);
1519 p = att_addlength(p, typlen, PointerGetDatum(p));
1520 p = (char *) att_align(p, typalign);
1523 /* advance bitmap pointer if any */
1527 if (bitmask == 0x100)
1535 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
1540 * returns the dimensions of the array pointed to by "v", as a "text"
1543 array_dims(PG_FUNCTION_ARGS)
1545 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1553 /* Sanity check: does it look like an array at all? */
1554 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1557 nbytes = ARR_NDIM(v) * 33 + 1;
1560 * 33 since we assume 15 digits per number + ':' +'[]'
1562 * +1 allows for temp trailing null
1565 result = (text *) palloc(nbytes + VARHDRSZ);
1566 p = VARDATA(result);
1571 for (i = 0; i < ARR_NDIM(v); i++)
1573 sprintf(p, "[%d:%d]", lb[i], dimv[i] + lb[i] - 1);
1576 VARATT_SIZEP(result) = strlen(VARDATA(result)) + VARHDRSZ;
1578 PG_RETURN_TEXT_P(result);
1583 * returns the lower dimension, of the DIM requested, for
1584 * the array pointed to by "v", as an int4
1587 array_lower(PG_FUNCTION_ARGS)
1589 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1590 int reqdim = PG_GETARG_INT32(1);
1594 /* Sanity check: does it look like an array at all? */
1595 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1598 /* Sanity check: was the requested dim valid */
1599 if (reqdim <= 0 || reqdim > ARR_NDIM(v))
1603 result = lb[reqdim - 1];
1605 PG_RETURN_INT32(result);
1610 * returns the upper dimension, of the DIM requested, for
1611 * the array pointed to by "v", as an int4
1614 array_upper(PG_FUNCTION_ARGS)
1616 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1617 int reqdim = PG_GETARG_INT32(1);
1622 /* Sanity check: does it look like an array at all? */
1623 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1626 /* Sanity check: was the requested dim valid */
1627 if (reqdim <= 0 || reqdim > ARR_NDIM(v))
1633 result = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
1635 PG_RETURN_INT32(result);
1640 * This routine takes an array pointer and a subscript array and returns
1641 * the referenced item as a Datum. Note that for a pass-by-reference
1642 * datatype, the returned Datum is a pointer into the array object.
1644 * This handles both ordinary varlena arrays and fixed-length arrays.
1647 * array: the array object (mustn't be NULL)
1648 * nSubscripts: number of subscripts supplied
1649 * indx[]: the subscript values
1650 * arraytyplen: pg_type.typlen for the array type
1651 * elmlen: pg_type.typlen for the array's element type
1652 * elmbyval: pg_type.typbyval for the array's element type
1653 * elmalign: pg_type.typalign for the array's element type
1656 * The return value is the element Datum.
1657 * *isNull is set to indicate whether the element is NULL.
1660 array_ref(ArrayType *array,
1678 bits8 *arraynullsptr;
1680 if (arraytyplen > 0)
1683 * fixed-length arrays -- these are assumed to be 1-d, 0-based
1686 fixedDim[0] = arraytyplen / elmlen;
1690 arraydataptr = (char *) array;
1691 arraynullsptr = NULL;
1695 /* detoast input array if necessary */
1696 array = DatumGetArrayTypeP(PointerGetDatum(array));
1698 ndim = ARR_NDIM(array);
1699 dim = ARR_DIMS(array);
1700 lb = ARR_LBOUND(array);
1701 arraydataptr = ARR_DATA_PTR(array);
1702 arraynullsptr = ARR_NULLBITMAP(array);
1706 * Return NULL for invalid subscript
1708 if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
1713 for (i = 0; i < ndim; i++)
1715 if (indx[i] < lb[i] || indx[i] >= (dim[i] + lb[i]))
1723 * Calculate the element number
1725 offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
1728 * Check for NULL array element
1730 if (array_get_isnull(arraynullsptr, offset))
1737 * OK, get the element
1740 retptr = array_seek(arraydataptr, 0, arraynullsptr, offset,
1741 elmlen, elmbyval, elmalign);
1742 return ArrayCast(retptr, elmbyval, elmlen);
1747 * This routine takes an array and a range of indices (upperIndex and
1748 * lowerIndx), creates a new array structure for the referred elements
1749 * and returns a pointer to it.
1751 * This handles both ordinary varlena arrays and fixed-length arrays.
1754 * array: the array object (mustn't be NULL)
1755 * nSubscripts: number of subscripts supplied (must be same for upper/lower)
1756 * upperIndx[]: the upper subscript values
1757 * lowerIndx[]: the lower subscript values
1758 * arraytyplen: pg_type.typlen for the array type
1759 * elmlen: pg_type.typlen for the array's element type
1760 * elmbyval: pg_type.typbyval for the array's element type
1761 * elmalign: pg_type.typalign for the array's element type
1764 * The return value is the new array Datum (it's never NULL)
1766 * NOTE: we assume it is OK to scribble on the provided subscript arrays
1767 * lowerIndx[] and upperIndx[]. These are generally just temporaries.
1770 array_get_slice(ArrayType *array,
1779 ArrayType *newarray;
1789 bits8 *arraynullsptr;
1794 if (arraytyplen > 0)
1797 * fixed-length arrays -- currently, cannot slice these because parser
1798 * labels output as being of the fixed-length array type! Code below
1799 * shows how we could support it if the parser were changed to label
1800 * output as a suitable varlena array type.
1803 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1804 errmsg("slices of fixed-length arrays not implemented")));
1807 * fixed-length arrays -- these are assumed to be 1-d, 0-based
1809 * XXX where would we get the correct ELEMTYPE from?
1812 fixedDim[0] = arraytyplen / elmlen;
1816 elemtype = InvalidOid; /* XXX */
1817 arraydataptr = (char *) array;
1818 arraynullsptr = NULL;
1822 /* detoast input array if necessary */
1823 array = DatumGetArrayTypeP(PointerGetDatum(array));
1825 ndim = ARR_NDIM(array);
1826 dim = ARR_DIMS(array);
1827 lb = ARR_LBOUND(array);
1828 elemtype = ARR_ELEMTYPE(array);
1829 arraydataptr = ARR_DATA_PTR(array);
1830 arraynullsptr = ARR_NULLBITMAP(array);
1834 * Check provided subscripts. A slice exceeding the current array limits
1835 * is silently truncated to the array limits. If we end up with an empty
1836 * slice, return an empty array.
1838 if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
1839 return construct_empty_array(elemtype);
1841 for (i = 0; i < nSubscripts; i++)
1843 if (lowerIndx[i] < lb[i])
1844 lowerIndx[i] = lb[i];
1845 if (upperIndx[i] >= (dim[i] + lb[i]))
1846 upperIndx[i] = dim[i] + lb[i] - 1;
1847 if (lowerIndx[i] > upperIndx[i])
1848 return construct_empty_array(elemtype);
1850 /* fill any missing subscript positions with full array range */
1851 for (; i < ndim; i++)
1853 lowerIndx[i] = lb[i];
1854 upperIndx[i] = dim[i] + lb[i] - 1;
1855 if (lowerIndx[i] > upperIndx[i])
1856 return construct_empty_array(elemtype);
1859 mda_get_range(ndim, span, lowerIndx, upperIndx);
1861 bytes = array_slice_size(arraydataptr, arraynullsptr,
1863 lowerIndx, upperIndx,
1864 elmlen, elmbyval, elmalign);
1867 * Currently, we put a null bitmap in the result if the source has one;
1868 * could be smarter ...
1872 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, ArrayGetNItems(ndim, span));
1873 bytes += dataoffset;
1877 dataoffset = 0; /* marker for no null bitmap */
1878 bytes += ARR_OVERHEAD_NONULLS(ndim);
1881 newarray = (ArrayType *) palloc(bytes);
1882 newarray->size = bytes;
1883 newarray->ndim = ndim;
1884 newarray->dataoffset = dataoffset;
1885 newarray->elemtype = elemtype;
1886 memcpy(ARR_DIMS(newarray), span, ndim * sizeof(int));
1889 * Lower bounds of the new array are set to 1. Formerly (before 7.3) we
1890 * copied the given lowerIndx values ... but that seems confusing.
1892 newlb = ARR_LBOUND(newarray);
1893 for (i = 0; i < ndim; i++)
1896 array_extract_slice(newarray,
1898 arraydataptr, arraynullsptr,
1899 lowerIndx, upperIndx,
1900 elmlen, elmbyval, elmalign);
1907 * This routine sets the value of an array element (specified by
1908 * a subscript array) to a new value specified by "dataValue".
1910 * This handles both ordinary varlena arrays and fixed-length arrays.
1913 * array: the initial array object (mustn't be NULL)
1914 * nSubscripts: number of subscripts supplied
1915 * indx[]: the subscript values
1916 * dataValue: the datum to be inserted at the given position
1917 * isNull: whether dataValue is NULL
1918 * arraytyplen: pg_type.typlen for the array type
1919 * elmlen: pg_type.typlen for the array's element type
1920 * elmbyval: pg_type.typbyval for the array's element type
1921 * elmalign: pg_type.typalign for the array's element type
1924 * A new array is returned, just like the old except for the one
1925 * modified entry. The original array object is not changed.
1927 * For one-dimensional arrays only, we allow the array to be extended
1928 * by assigning to a position outside the existing subscript range; any
1929 * positions between the existing elements and the new one are set to NULLs.
1930 * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
1932 * NOTE: For assignments, we throw an error for invalid subscripts etc,
1933 * rather than returning a NULL as the fetch operations do.
1936 array_set(ArrayType *array,
1946 ArrayType *newarray;
1954 bits8 *oldnullbitmap;
1968 if (arraytyplen > 0)
1971 * fixed-length arrays -- these are assumed to be 1-d, 0-based. We
1972 * cannot extend them, either.
1974 if (nSubscripts != 1)
1976 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1977 errmsg("wrong number of array subscripts")));
1979 if (indx[0] < 0 || indx[0] * elmlen >= arraytyplen)
1981 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1982 errmsg("array subscript out of range")));
1986 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
1987 errmsg("cannot assign NULL to an element of a fixed-length array")));
1989 newarray = (ArrayType *) palloc(arraytyplen);
1990 memcpy(newarray, array, arraytyplen);
1991 elt_ptr = (char *) newarray + indx[0] * elmlen;
1992 ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign, elt_ptr);
1996 if (nSubscripts <= 0 || nSubscripts > MAXDIM)
1998 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1999 errmsg("wrong number of array subscripts")));
2001 /* make sure item to be inserted is not toasted */
2002 if (elmlen == -1 && !isNull)
2003 dataValue = PointerGetDatum(PG_DETOAST_DATUM(dataValue));
2005 /* detoast input array if necessary */
2006 array = DatumGetArrayTypeP(PointerGetDatum(array));
2008 ndim = ARR_NDIM(array);
2011 * if number of dims is zero, i.e. an empty array, create an array with
2012 * nSubscripts dimensions, and set the lower bounds to the supplied
2017 Oid elmtype = ARR_ELEMTYPE(array);
2019 for (i = 0; i < nSubscripts; i++)
2025 return construct_md_array(&dataValue, &isNull, nSubscripts,
2027 elmlen, elmbyval, elmalign);
2030 if (ndim != nSubscripts)
2032 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2033 errmsg("wrong number of array subscripts")));
2035 /* copy dim/lb since we may modify them */
2036 memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
2037 memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
2039 newhasnulls = (ARR_HASNULL(array) || isNull);
2040 addedbefore = addedafter = 0;
2047 if (indx[0] < lb[0])
2049 addedbefore = lb[0] - indx[0];
2050 dim[0] += addedbefore;
2052 if (addedbefore > 1)
2053 newhasnulls = true; /* will insert nulls */
2055 if (indx[0] >= (dim[0] + lb[0]))
2057 addedafter = indx[0] - (dim[0] + lb[0]) + 1;
2058 dim[0] += addedafter;
2060 newhasnulls = true; /* will insert nulls */
2066 * XXX currently we do not support extending multi-dimensional arrays
2069 for (i = 0; i < ndim; i++)
2071 if (indx[i] < lb[i] ||
2072 indx[i] >= (dim[i] + lb[i]))
2074 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2075 errmsg("array subscript out of range")));
2080 * Compute sizes of items and areas to copy
2082 newnitems = ArrayGetNItems(ndim, dim);
2084 overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, newnitems);
2086 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 (addedafter)
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 /* Zero the bitmap to take care of marking inserted positions null */
2166 MemSet(newnullbitmap, 0, (newnitems + 7) / 8);
2167 /* Fix the inserted value */
2169 array_set_isnull(newnullbitmap, newnitems - 1, isNull);
2171 array_set_isnull(newnullbitmap, offset, isNull);
2172 /* Fix the copied range(s) */
2174 array_bitmap_copy(newnullbitmap, addedbefore,
2179 array_bitmap_copy(newnullbitmap, 0,
2182 if (addedafter == 0)
2183 array_bitmap_copy(newnullbitmap, offset + 1,
2184 oldnullbitmap, offset + 1,
2185 oldnitems - offset - 1);
2194 * This routine sets the value of a range of array locations (specified
2195 * by upper and lower subscript values) to new values passed as
2198 * This handles both ordinary varlena arrays and fixed-length arrays.
2201 * array: the initial array object (mustn't be NULL)
2202 * nSubscripts: number of subscripts supplied (must be same for upper/lower)
2203 * upperIndx[]: the upper subscript values
2204 * lowerIndx[]: the lower subscript values
2205 * srcArray: the source for the inserted values
2206 * isNull: indicates whether srcArray is NULL
2207 * arraytyplen: pg_type.typlen for the array type
2208 * elmlen: pg_type.typlen for the array's element type
2209 * elmbyval: pg_type.typbyval for the array's element type
2210 * elmalign: pg_type.typalign for the array's element type
2213 * A new array is returned, just like the old except for the
2214 * modified range. The original array object is not changed.
2216 * For one-dimensional arrays only, we allow the array to be extended
2217 * by assigning to positions outside the existing subscript range; any
2218 * positions between the existing elements and the new ones are set to NULLs.
2219 * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
2221 * NOTE: we assume it is OK to scribble on the provided index arrays
2222 * lowerIndx[] and upperIndx[]. These are generally just temporaries.
2224 * NOTE: For assignments, we throw an error for silly subscripts etc,
2225 * rather than returning a NULL or empty array as the fetch operations do.
2228 array_set_slice(ArrayType *array,
2232 ArrayType *srcArray,
2239 ArrayType *newarray;
2262 /* Currently, assignment from a NULL source array is a no-op */
2266 if (arraytyplen > 0)
2269 * fixed-length arrays -- not got round to doing this...
2272 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2273 errmsg("updates on slices of fixed-length arrays not implemented")));
2276 /* detoast arrays if necessary */
2277 array = DatumGetArrayTypeP(PointerGetDatum(array));
2278 srcArray = DatumGetArrayTypeP(PointerGetDatum(srcArray));
2280 /* note: we assume srcArray contains no toasted elements */
2282 ndim = ARR_NDIM(array);
2285 * if number of dims is zero, i.e. an empty array, create an array with
2286 * nSubscripts dimensions, and set the upper and lower bounds to the
2287 * supplied subscripts
2294 Oid elmtype = ARR_ELEMTYPE(array);
2296 deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
2297 &dvalues, &dnulls, &nelems);
2299 for (i = 0; i < nSubscripts; i++)
2301 dim[i] = 1 + upperIndx[i] - lowerIndx[i];
2302 lb[i] = lowerIndx[i];
2305 /* complain if too few source items; we ignore extras, however */
2306 if (nelems < ArrayGetNItems(nSubscripts, dim))
2308 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2309 errmsg("source array too small")));
2311 return construct_md_array(dvalues, dnulls, nSubscripts,
2313 elmlen, elmbyval, elmalign);
2316 if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
2318 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2319 errmsg("wrong number of array subscripts")));
2321 /* copy dim/lb since we may modify them */
2322 memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
2323 memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
2325 newhasnulls = (ARR_HASNULL(array) || ARR_HASNULL(srcArray));
2326 addedbefore = addedafter = 0;
2333 Assert(nSubscripts == 1);
2334 if (lowerIndx[0] > upperIndx[0])
2336 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2337 errmsg("upper bound cannot be less than lower bound")));
2338 if (lowerIndx[0] < lb[0])
2340 if (upperIndx[0] < lb[0] - 1)
2341 newhasnulls = true; /* will insert nulls */
2342 addedbefore = lb[0] - lowerIndx[0];
2343 dim[0] += addedbefore;
2344 lb[0] = lowerIndx[0];
2346 if (upperIndx[0] >= (dim[0] + lb[0]))
2348 if (lowerIndx[0] > (dim[0] + lb[0]))
2349 newhasnulls = true; /* will insert nulls */
2350 addedafter = upperIndx[0] - (dim[0] + lb[0]) + 1;
2351 dim[0] += addedafter;
2357 * XXX currently we do not support extending multi-dimensional arrays
2360 for (i = 0; i < nSubscripts; i++)
2362 if (lowerIndx[i] > upperIndx[i])
2364 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2365 errmsg("upper bound cannot be less than lower bound")));
2366 if (lowerIndx[i] < lb[i] ||
2367 upperIndx[i] >= (dim[i] + lb[i]))
2369 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2370 errmsg("array subscript out of range")));
2372 /* fill any missing subscript positions with full array range */
2373 for (; i < ndim; i++)
2375 lowerIndx[i] = lb[i];
2376 upperIndx[i] = dim[i] + lb[i] - 1;
2377 if (lowerIndx[i] > upperIndx[i])
2379 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2380 errmsg("upper bound cannot be less than lower bound")));
2384 /* Do this mainly to check for overflow */
2385 nitems = ArrayGetNItems(ndim, dim);
2388 * Make sure source array has enough entries. Note we ignore the shape of
2389 * the source array and just read entries serially.
2391 mda_get_range(ndim, span, lowerIndx, upperIndx);
2392 nsrcitems = ArrayGetNItems(ndim, span);
2393 if (nsrcitems > ArrayGetNItems(ARR_NDIM(srcArray), ARR_DIMS(srcArray)))
2395 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2396 errmsg("source array too small")));
2399 * Compute space occupied by new entries, space occupied by replaced
2400 * entries, and required space for new array.
2403 overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
2405 overheadlen = ARR_OVERHEAD_NONULLS(ndim);
2406 newitemsize = array_nelems_size(ARR_DATA_PTR(srcArray), 0,
2407 ARR_NULLBITMAP(srcArray), nsrcitems,
2408 elmlen, elmbyval, elmalign);
2409 oldoverheadlen = ARR_DATA_OFFSET(array);
2410 olddatasize = ARR_SIZE(array) - oldoverheadlen;
2414 * here we do not need to cope with extension of the array; it would
2415 * be a lot more complicated if we had to do so...
2417 olditemsize = array_slice_size(ARR_DATA_PTR(array),
2418 ARR_NULLBITMAP(array),
2420 lowerIndx, upperIndx,
2421 elmlen, elmbyval, elmalign);
2422 lenbefore = lenafter = 0; /* keep compiler quiet */
2423 itemsbefore = itemsafter = nolditems = 0;
2428 * here we must allow for possibility of slice larger than orig array
2430 int oldlb = ARR_LBOUND(array)[0];
2431 int oldub = oldlb + ARR_DIMS(array)[0] - 1;
2432 int slicelb = Max(oldlb, lowerIndx[0]);
2433 int sliceub = Min(oldub, upperIndx[0]);
2434 char *oldarraydata = ARR_DATA_PTR(array);
2435 bits8 *oldarraybitmap = ARR_NULLBITMAP(array);
2437 itemsbefore = Min(slicelb, oldub + 1) - oldlb;
2438 lenbefore = array_nelems_size(oldarraydata, 0, oldarraybitmap,
2440 elmlen, elmbyval, elmalign);
2441 if (slicelb > sliceub)
2448 nolditems = sliceub - slicelb + 1;
2449 olditemsize = array_nelems_size(oldarraydata + lenbefore,
2450 itemsbefore, oldarraybitmap,
2452 elmlen, elmbyval, elmalign);
2454 itemsafter = oldub - sliceub;
2455 lenafter = olddatasize - lenbefore - olditemsize;
2458 newsize = overheadlen + olddatasize - olditemsize + newitemsize;
2460 newarray = (ArrayType *) palloc(newsize);
2461 newarray->size = newsize;
2462 newarray->ndim = ndim;
2463 newarray->dataoffset = newhasnulls ? overheadlen : 0;
2464 newarray->elemtype = ARR_ELEMTYPE(array);
2465 memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
2466 memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
2471 * here we do not need to cope with extension of the array; it would
2472 * be a lot more complicated if we had to do so...
2474 array_insert_slice(newarray, array, srcArray,
2476 lowerIndx, upperIndx,
2477 elmlen, elmbyval, elmalign);
2482 memcpy((char *) newarray + overheadlen,
2483 (char *) array + oldoverheadlen,
2485 memcpy((char *) newarray + overheadlen + lenbefore,
2486 ARR_DATA_PTR(srcArray),
2488 memcpy((char *) newarray + overheadlen + lenbefore + newitemsize,
2489 (char *) array + oldoverheadlen + lenbefore + olditemsize,
2491 /* fill in nulls bitmap if needed */
2494 bits8 *newnullbitmap = ARR_NULLBITMAP(newarray);
2495 bits8 *oldnullbitmap = ARR_NULLBITMAP(array);
2497 /* Zero the bitmap to handle marking inserted positions null */
2498 MemSet(newnullbitmap, 0, (nitems + 7) / 8);
2499 array_bitmap_copy(newnullbitmap, addedbefore,
2502 array_bitmap_copy(newnullbitmap, lowerIndx[0] - lb[0],
2503 ARR_NULLBITMAP(srcArray), 0,
2505 array_bitmap_copy(newnullbitmap, addedbefore + itemsbefore + nolditems,
2506 oldnullbitmap, itemsbefore + nolditems,
2517 * Map an array through an arbitrary function. Return a new array with
2518 * same dimensions and each source element transformed by fn(). Each
2519 * source element is passed as the first argument to fn(); additional
2520 * arguments to be passed to fn() can be specified by the caller.
2521 * The output array can have a different element type than the input.
2524 * * fcinfo: a function-call data structure pre-constructed by the caller
2525 * to be ready to call the desired function, with everything except the
2526 * first argument position filled in. In particular, flinfo identifies
2527 * the function fn(), and if nargs > 1 then argument positions after the
2528 * first must be preset to the additional values to be passed. The
2529 * first argument position initially holds the input array value.
2530 * * inpType: OID of element type of input array. This must be the same as,
2531 * or binary-compatible with, the first argument type of fn().
2532 * * retType: OID of element type of output array. This must be the same as,
2533 * or binary-compatible with, the result type of fn().
2534 * * amstate: workspace for array_map. Must be zeroed by caller before
2535 * first call, and not touched after that.
2537 * It is legitimate to pass a freshly-zeroed ArrayMapState on each call,
2538 * but better performance can be had if the state can be preserved across
2539 * a series of calls.
2541 * NB: caller must assure that input array is not NULL. NULL elements in
2542 * the array are OK however.
2545 array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType,
2546 ArrayMapState *amstate)
2569 ArrayMetaState *inp_extra;
2570 ArrayMetaState *ret_extra;
2572 /* Get input array */
2573 if (fcinfo->nargs < 1)
2574 elog(ERROR, "invalid nargs: %d", fcinfo->nargs);
2575 if (PG_ARGISNULL(0))
2576 elog(ERROR, "null input array");
2577 v = PG_GETARG_ARRAYTYPE_P(0);
2579 Assert(ARR_ELEMTYPE(v) == inpType);
2583 nitems = ArrayGetNItems(ndim, dim);
2585 /* Check for empty array */
2588 /* Return empty array */
2589 PG_RETURN_ARRAYTYPE_P(construct_empty_array(retType));
2593 * We arrange to look up info about input and return element types only
2594 * once per series of calls, assuming the element type doesn't change
2597 inp_extra = &amstate->inp_extra;
2598 ret_extra = &amstate->ret_extra;
2600 if (inp_extra->element_type != inpType)
2602 get_typlenbyvalalign(inpType,
2604 &inp_extra->typbyval,
2605 &inp_extra->typalign);
2606 inp_extra->element_type = inpType;
2608 inp_typlen = inp_extra->typlen;
2609 inp_typbyval = inp_extra->typbyval;
2610 inp_typalign = inp_extra->typalign;
2612 if (ret_extra->element_type != retType)
2614 get_typlenbyvalalign(retType,
2616 &ret_extra->typbyval,
2617 &ret_extra->typalign);
2618 ret_extra->element_type = retType;
2620 typlen = ret_extra->typlen;
2621 typbyval = ret_extra->typbyval;
2622 typalign = ret_extra->typalign;
2624 /* Allocate temporary arrays for new values */
2625 values = (Datum *) palloc(nitems * sizeof(Datum));
2626 nulls = (bool *) palloc(nitems * sizeof(bool));
2628 /* Loop over source data */
2629 s = ARR_DATA_PTR(v);
2630 bitmap = ARR_NULLBITMAP(v);
2634 for (i = 0; i < nitems; i++)
2638 /* Get source element, checking for NULL */
2639 if (bitmap && (*bitmap & bitmask) == 0)
2641 fcinfo->argnull[0] = true;
2645 elt = fetch_att(s, inp_typbyval, inp_typlen);
2646 s = att_addlength(s, inp_typlen, elt);
2647 s = (char *) att_align(s, inp_typalign);
2648 fcinfo->arg[0] = elt;
2649 fcinfo->argnull[0] = false;
2653 * Apply the given function to source elt and extra args.
2655 if (fcinfo->flinfo->fn_strict)
2659 for (j = 0; j < fcinfo->nargs; j++)
2661 if (fcinfo->argnull[j])
2671 fcinfo->isnull = false;
2672 values[i] = FunctionCallInvoke(fcinfo);
2675 fcinfo->isnull = true;
2677 nulls[i] = fcinfo->isnull;
2682 /* Ensure data is not toasted */
2684 values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
2685 /* Update total result size */
2686 nbytes = att_addlength(nbytes, typlen, values[i]);
2687 nbytes = att_align(nbytes, typalign);
2688 /* check for overflow of total request */
2689 if (!AllocSizeIsValid(nbytes))
2691 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2692 errmsg("array size exceeds the maximum allowed (%d)",
2693 (int) MaxAllocSize)));
2696 /* advance bitmap pointer if any */
2700 if (bitmask == 0x100)
2708 /* Allocate and initialize the result array */
2711 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
2712 nbytes += dataoffset;
2716 dataoffset = 0; /* marker for no null bitmap */
2717 nbytes += ARR_OVERHEAD_NONULLS(ndim);
2719 result = (ArrayType *) palloc(nbytes);
2720 result->size = nbytes;
2721 result->ndim = ndim;
2722 result->dataoffset = dataoffset;
2723 result->elemtype = retType;
2724 memcpy(ARR_DIMS(result), ARR_DIMS(v), 2 * ndim * sizeof(int));
2727 * Note: do not risk trying to pfree the results of the called function
2729 CopyArrayEls(result,
2730 values, nulls, nitems,
2731 typlen, typbyval, typalign,
2737 PG_RETURN_ARRAYTYPE_P(result);
2741 * construct_array --- simple method for constructing an array object
2743 * elems: array of Datum items to become the array contents
2744 * (NULL element values are not supported).
2745 * nelems: number of items
2746 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
2748 * A palloc'd 1-D array object is constructed and returned. Note that
2749 * elem values will be copied into the object even if pass-by-ref type.
2751 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
2752 * from the system catalogs, given the elmtype. However, the caller is
2753 * in a better position to cache this info across multiple uses, or even
2754 * to hard-wire values if the element type is hard-wired.
2757 construct_array(Datum *elems, int nelems,
2759 int elmlen, bool elmbyval, char elmalign)
2767 return construct_md_array(elems, NULL, 1, dims, lbs,
2768 elmtype, elmlen, elmbyval, elmalign);
2772 * construct_md_array --- simple method for constructing an array object
2773 * with arbitrary dimensions and possible NULLs
2775 * elems: array of Datum items to become the array contents
2776 * nulls: array of is-null flags (can be NULL if no nulls)
2777 * ndims: number of dimensions
2778 * dims: integer array with size of each dimension
2779 * lbs: integer array with lower bound of each dimension
2780 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
2782 * A palloc'd ndims-D array object is constructed and returned. Note that
2783 * elem values will be copied into the object even if pass-by-ref type.
2785 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
2786 * from the system catalogs, given the elmtype. However, the caller is
2787 * in a better position to cache this info across multiple uses, or even
2788 * to hard-wire values if the element type is hard-wired.
2791 construct_md_array(Datum *elems,
2796 Oid elmtype, int elmlen, bool elmbyval, char elmalign)
2805 if (ndims < 0) /* we do allow zero-dimension arrays */
2807 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2808 errmsg("invalid number of dimensions: %d", ndims)));
2811 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2812 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
2815 /* fast track for empty array */
2817 return construct_empty_array(elmtype);
2819 nelems = ArrayGetNItems(ndims, dims);
2821 /* compute required space */
2824 for (i = 0; i < nelems; i++)
2826 if (nulls && nulls[i])
2831 /* make sure data is not toasted */
2833 elems[i] = PointerGetDatum(PG_DETOAST_DATUM(elems[i]));
2834 nbytes = att_addlength(nbytes, elmlen, elems[i]);
2835 nbytes = att_align(nbytes, elmalign);
2836 /* check for overflow of total request */
2837 if (!AllocSizeIsValid(nbytes))
2839 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2840 errmsg("array size exceeds the maximum allowed (%d)",
2841 (int) MaxAllocSize)));
2844 /* Allocate and initialize result array */
2847 dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nelems);
2848 nbytes += dataoffset;
2852 dataoffset = 0; /* marker for no null bitmap */
2853 nbytes += ARR_OVERHEAD_NONULLS(ndims);
2855 result = (ArrayType *) palloc(nbytes);
2856 result->size = nbytes;
2857 result->ndim = ndims;
2858 result->dataoffset = dataoffset;
2859 result->elemtype = elmtype;
2860 memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
2861 memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
2863 CopyArrayEls(result,
2864 elems, nulls, nelems,
2865 elmlen, elmbyval, elmalign,
2872 * construct_empty_array --- make a zero-dimensional array of given type
2875 construct_empty_array(Oid elmtype)
2879 result = (ArrayType *) palloc(sizeof(ArrayType));
2880 result->size = sizeof(ArrayType);
2882 result->dataoffset = 0;
2883 result->elemtype = elmtype;
2888 * deconstruct_array --- simple method for extracting data from an array
2890 * array: array object to examine (must not be NULL)
2891 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
2892 * elemsp: return value, set to point to palloc'd array of Datum values
2893 * nullsp: return value, set to point to palloc'd array of isnull markers
2894 * nelemsp: return value, set to number of extracted values
2896 * The caller may pass nullsp == NULL if it does not support NULLs in the
2897 * array. Note that this produces a very uninformative error message,
2898 * so do it only in cases where a NULL is really not expected.
2900 * If array elements are pass-by-ref data type, the returned Datums will
2901 * be pointers into the array object.
2903 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
2904 * from the system catalogs, given the elmtype. However, in most current
2905 * uses the type is hard-wired into the caller and so we can save a lookup
2906 * cycle by hard-wiring the type info as well.
2909 deconstruct_array(ArrayType *array,
2911 int elmlen, bool elmbyval, char elmalign,
2912 Datum **elemsp, bool **nullsp, int *nelemsp)
2922 Assert(ARR_ELEMTYPE(array) == elmtype);
2924 nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
2925 *elemsp = elems = (Datum *) palloc(nelems * sizeof(Datum));
2927 *nullsp = nulls = (bool *) palloc(nelems * sizeof(bool));
2932 p = ARR_DATA_PTR(array);
2933 bitmap = ARR_NULLBITMAP(array);
2936 for (i = 0; i < nelems; i++)
2938 /* Get source element, checking for NULL */
2939 if (bitmap && (*bitmap & bitmask) == 0)
2941 elems[i] = (Datum) 0;
2946 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
2947 errmsg("NULL array element not allowed in this context")));
2951 elems[i] = fetch_att(p, elmbyval, elmlen);
2954 p = att_addlength(p, elmlen, PointerGetDatum(p));
2955 p = (char *) att_align(p, elmalign);
2958 /* advance bitmap pointer if any */
2962 if (bitmask == 0x100)
2974 * compares two arrays for equality
2976 * returns true if the arrays are equal, false otherwise.
2978 * Note: we do not use array_cmp here, since equality may be meaningful in
2979 * datatypes that don't have a total ordering (and hence no btree support).
2982 array_eq(PG_FUNCTION_ARGS)
2984 ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
2985 ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
2986 int ndims1 = ARR_NDIM(array1);
2987 int ndims2 = ARR_NDIM(array2);
2988 int *dims1 = ARR_DIMS(array1);
2989 int *dims2 = ARR_DIMS(array2);
2990 Oid element_type = ARR_ELEMTYPE(array1);
2993 TypeCacheEntry *typentry;
3003 FunctionCallInfoData locfcinfo;
3005 if (element_type != ARR_ELEMTYPE(array2))
3007 (errcode(ERRCODE_DATATYPE_MISMATCH),
3008 errmsg("cannot compare arrays of different element types")));
3010 /* fast path if the arrays do not have the same dimensionality */
3011 if (ndims1 != ndims2 ||
3012 memcmp(dims1, dims2, 2 * ndims1 * sizeof(int)) != 0)
3017 * We arrange to look up the equality function only once per series of
3018 * calls, assuming the element type doesn't change underneath us. The
3019 * typcache is used so that we have no memory leakage when being used
3020 * as an index support function.
3022 typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3023 if (typentry == NULL ||
3024 typentry->type_id != element_type)
3026 typentry = lookup_type_cache(element_type,
3027 TYPECACHE_EQ_OPR_FINFO);
3028 if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
3030 (errcode(ERRCODE_UNDEFINED_FUNCTION),
3031 errmsg("could not identify an equality operator for type %s",
3032 format_type_be(element_type))));
3033 fcinfo->flinfo->fn_extra = (void *) typentry;
3035 typlen = typentry->typlen;
3036 typbyval = typentry->typbyval;
3037 typalign = typentry->typalign;
3040 * apply the operator to each pair of array elements.
3042 InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
3045 /* Loop over source data */
3046 nitems = ArrayGetNItems(ndims1, dims1);
3047 ptr1 = ARR_DATA_PTR(array1);
3048 ptr2 = ARR_DATA_PTR(array2);
3049 bitmap1 = ARR_NULLBITMAP(array1);
3050 bitmap2 = ARR_NULLBITMAP(array2);
3051 bitmask = 1; /* use same bitmask for both arrays */
3053 for (i = 0; i < nitems; i++)
3061 /* Get elements, checking for NULL */
3062 if (bitmap1 && (*bitmap1 & bitmask) == 0)
3070 elt1 = fetch_att(ptr1, typbyval, typlen);
3071 ptr1 = att_addlength(ptr1, typlen, PointerGetDatum(ptr1));
3072 ptr1 = (char *) att_align(ptr1, typalign);
3075 if (bitmap2 && (*bitmap2 & bitmask) == 0)
3083 elt2 = fetch_att(ptr2, typbyval, typlen);
3084 ptr2 = att_addlength(ptr2, typlen, PointerGetDatum(ptr2));
3085 ptr2 = (char *) att_align(ptr2, typalign);
3088 /* advance bitmap pointers if any */
3090 if (bitmask == 0x100)
3100 * We consider two NULLs equal; NULL and not-NULL are unequal.
3102 if (isnull1 && isnull2)
3104 if (isnull1 || isnull2)
3111 * Apply the operator to the element pair
3113 locfcinfo.arg[0] = elt1;
3114 locfcinfo.arg[1] = elt2;
3115 locfcinfo.argnull[0] = false;
3116 locfcinfo.argnull[1] = false;
3117 locfcinfo.isnull = false;
3118 oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
3127 /* Avoid leaking memory when handed toasted input. */
3128 PG_FREE_IF_COPY(array1, 0);
3129 PG_FREE_IF_COPY(array2, 1);
3131 PG_RETURN_BOOL(result);
3135 /*-----------------------------------------------------------------------------
3136 * array-array bool operators:
3137 * Given two arrays, iterate comparison operators
3138 * over the array. Uses logic similar to text comparison
3139 * functions, except element-by-element instead of
3140 * character-by-character.
3141 *----------------------------------------------------------------------------
3145 array_ne(PG_FUNCTION_ARGS)
3147 PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo)));
3151 array_lt(PG_FUNCTION_ARGS)
3153 PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
3157 array_gt(PG_FUNCTION_ARGS)
3159 PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
3163 array_le(PG_FUNCTION_ARGS)
3165 PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
3169 array_ge(PG_FUNCTION_ARGS)
3171 PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
3175 btarraycmp(PG_FUNCTION_ARGS)
3177 PG_RETURN_INT32(array_cmp(fcinfo));
3182 * Internal comparison function for arrays.
3184 * Returns -1, 0 or 1
3187 array_cmp(FunctionCallInfo fcinfo)
3189 ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
3190 ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
3191 int ndims1 = ARR_NDIM(array1);
3192 int ndims2 = ARR_NDIM(array2);
3193 int *dims1 = ARR_DIMS(array1);
3194 int *dims2 = ARR_DIMS(array2);
3195 int nitems1 = ArrayGetNItems(ndims1, dims1);
3196 int nitems2 = ArrayGetNItems(ndims2, dims2);
3197 Oid element_type = ARR_ELEMTYPE(array1);
3199 TypeCacheEntry *typentry;
3210 FunctionCallInfoData locfcinfo;
3212 if (element_type != ARR_ELEMTYPE(array2))
3214 (errcode(ERRCODE_DATATYPE_MISMATCH),
3215 errmsg("cannot compare arrays of different element types")));
3218 * We arrange to look up the comparison function only once per series of
3219 * calls, assuming the element type doesn't change underneath us. The
3220 * typcache is used so that we have no memory leakage when being used as
3221 * an index support function.
3223 typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3224 if (typentry == NULL ||
3225 typentry->type_id != element_type)
3227 typentry = lookup_type_cache(element_type,
3228 TYPECACHE_CMP_PROC_FINFO);
3229 if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
3231 (errcode(ERRCODE_UNDEFINED_FUNCTION),
3232 errmsg("could not identify a comparison function for type %s",
3233 format_type_be(element_type))));
3234 fcinfo->flinfo->fn_extra = (void *) typentry;
3236 typlen = typentry->typlen;
3237 typbyval = typentry->typbyval;
3238 typalign = typentry->typalign;
3241 * apply the operator to each pair of array elements.
3243 InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2,
3246 /* Loop over source data */
3247 min_nitems = Min(nitems1, nitems2);
3248 ptr1 = ARR_DATA_PTR(array1);
3249 ptr2 = ARR_DATA_PTR(array2);
3250 bitmap1 = ARR_NULLBITMAP(array1);
3251 bitmap2 = ARR_NULLBITMAP(array2);
3252 bitmask = 1; /* use same bitmask for both arrays */
3254 for (i = 0; i < min_nitems; i++)
3262 /* Get elements, checking for NULL */
3263 if (bitmap1 && (*bitmap1 & bitmask) == 0)
3271 elt1 = fetch_att(ptr1, typbyval, typlen);
3272 ptr1 = att_addlength(ptr1, typlen, PointerGetDatum(ptr1));
3273 ptr1 = (char *) att_align(ptr1, typalign);
3276 if (bitmap2 && (*bitmap2 & bitmask) == 0)
3284 elt2 = fetch_att(ptr2, typbyval, typlen);
3285 ptr2 = att_addlength(ptr2, typlen, PointerGetDatum(ptr2));
3286 ptr2 = (char *) att_align(ptr2, typalign);
3289 /* advance bitmap pointers if any */
3291 if (bitmask == 0x100)
3301 * We consider two NULLs equal; NULL > not-NULL.
3303 if (isnull1 && isnull2)
3307 /* arg1 is greater than arg2 */
3313 /* arg1 is less than arg2 */
3318 /* Compare the pair of elements */
3319 locfcinfo.arg[0] = elt1;
3320 locfcinfo.arg[1] = elt2;
3321 locfcinfo.argnull[0] = false;
3322 locfcinfo.argnull[1] = false;
3323 locfcinfo.isnull = false;
3324 cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
3327 continue; /* equal */
3331 /* arg1 is less than arg2 */
3337 /* arg1 is greater than arg2 */
3344 * If arrays contain same data (up to end of shorter one), apply
3345 * additional rules to sort by dimensionality. The relative significance
3346 * of the different bits of information is historical; mainly we just care
3347 * that we don't say "equal" for arrays of different dimensionality.
3351 if (nitems1 != nitems2)
3352 result = (nitems1 < nitems2) ? -1 : 1;
3353 else if (ndims1 != ndims2)
3354 result = (ndims1 < ndims2) ? -1 : 1;
3357 /* this relies on LB array immediately following DIMS array */
3358 for (i = 0; i < ndims1 * 2; i++)
3360 if (dims1[i] != dims2[i])
3362 result = (dims1[i] < dims2[i]) ? -1 : 1;
3369 /* Avoid leaking memory when handed toasted input. */
3370 PG_FREE_IF_COPY(array1, 0);
3371 PG_FREE_IF_COPY(array2, 1);
3377 /*-----------------------------------------------------------------------------
3378 * array overlap/containment comparisons
3379 * These use the same methods of comparing array elements as array_eq.
3380 * We consider only the elements of the arrays, ignoring dimensionality.
3381 *----------------------------------------------------------------------------
3385 * array_contain_compare :
3386 * compares two arrays for overlap/containment
3388 * When matchall is true, return true if all members of array1 are in array2.
3389 * When matchall is false, return true if any members of array1 are in array2.
3392 array_contain_compare(ArrayType *array1, ArrayType *array2, bool matchall,
3395 bool result = matchall;
3396 Oid element_type = ARR_ELEMTYPE(array1);
3397 TypeCacheEntry *typentry;
3410 FunctionCallInfoData locfcinfo;
3412 if (element_type != ARR_ELEMTYPE(array2))
3414 (errcode(ERRCODE_DATATYPE_MISMATCH),
3415 errmsg("cannot compare arrays of different element types")));
3418 * We arrange to look up the equality function only once per series of
3419 * calls, assuming the element type doesn't change underneath us. The
3420 * typcache is used so that we have no memory leakage when being used as
3421 * an index support function.
3423 typentry = (TypeCacheEntry *) *fn_extra;
3424 if (typentry == NULL ||
3425 typentry->type_id != element_type)
3427 typentry = lookup_type_cache(element_type,
3428 TYPECACHE_EQ_OPR_FINFO);
3429 if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
3431 (errcode(ERRCODE_UNDEFINED_FUNCTION),
3432 errmsg("could not identify an equality operator for type %s",
3433 format_type_be(element_type))));
3434 *fn_extra = (void *) typentry;
3436 typlen = typentry->typlen;
3437 typbyval = typentry->typbyval;
3438 typalign = typentry->typalign;
3441 * Since we probably will need to scan array2 multiple times, it's
3442 * worthwhile to use deconstruct_array on it. We scan array1 the hard way
3443 * however, since we very likely won't need to look at all of it.
3445 deconstruct_array(array2, element_type, typlen, typbyval, typalign,
3446 &values2, &nulls2, &nelems2);
3449 * Apply the comparison operator to each pair of array elements.
3451 InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
3454 /* Loop over source data */
3455 nelems1 = ArrayGetNItems(ARR_NDIM(array1), ARR_DIMS(array1));
3456 ptr1 = ARR_DATA_PTR(array1);
3457 bitmap1 = ARR_NULLBITMAP(array1);
3460 for (i = 0; i < nelems1; i++)
3465 /* Get element, checking for NULL */
3466 if (bitmap1 && (*bitmap1 & bitmask) == 0)
3474 elt1 = fetch_att(ptr1, typbyval, typlen);
3475 ptr1 = att_addlength(ptr1, typlen, PointerGetDatum(ptr1));
3476 ptr1 = (char *) att_align(ptr1, typalign);
3479 /* advance bitmap pointer if any */
3481 if (bitmask == 0x100)
3489 * We assume that the comparison operator is strict, so a NULL can't
3490 * match anything. XXX this diverges from the "NULL=NULL" behavior of
3491 * array_eq, should we act like that?
3503 for (j = 0; j < nelems2; j++)
3505 Datum elt2 = values2[j];
3506 bool isnull2 = nulls2[j];
3510 continue; /* can't match */
3513 * Apply the operator to the element pair
3515 locfcinfo.arg[0] = elt1;
3516 locfcinfo.arg[1] = elt2;
3517 locfcinfo.argnull[0] = false;
3518 locfcinfo.argnull[1] = false;
3519 locfcinfo.isnull = false;
3520 oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
3527 /* found a match for elt1 */
3536 /* no match for elt1 */
3552 arrayoverlap(PG_FUNCTION_ARGS)
3554 ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
3555 ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
3558 result = array_contain_compare(array1, array2, false,
3559 &fcinfo->flinfo->fn_extra);
3561 /* Avoid leaking memory when handed toasted input. */
3562 PG_FREE_IF_COPY(array1, 0);
3563 PG_FREE_IF_COPY(array2, 1);
3565 PG_RETURN_BOOL(result);
3569 arraycontains(PG_FUNCTION_ARGS)
3571 ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
3572 ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
3575 result = array_contain_compare(array2, array1, true,
3576 &fcinfo->flinfo->fn_extra);
3578 /* Avoid leaking memory when handed toasted input. */
3579 PG_FREE_IF_COPY(array1, 0);
3580 PG_FREE_IF_COPY(array2, 1);
3582 PG_RETURN_BOOL(result);
3586 arraycontained(PG_FUNCTION_ARGS)
3588 ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
3589 ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
3592 result = array_contain_compare(array1, array2, true,
3593 &fcinfo->flinfo->fn_extra);
3595 /* Avoid leaking memory when handed toasted input. */
3596 PG_FREE_IF_COPY(array1, 0);
3597 PG_FREE_IF_COPY(array2, 1);
3599 PG_RETURN_BOOL(result);
3603 /***************************************************************************/
3604 /******************| Support Routines |*****************/
3605 /***************************************************************************/
3608 * Check whether a specific array element is NULL
3610 * nullbitmap: pointer to array's null bitmap (NULL if none)
3611 * offset: 0-based linear element number of array element
3614 array_get_isnull(const bits8 *nullbitmap, int offset)
3616 if (nullbitmap == NULL)
3617 return false; /* assume not null */
3618 if (nullbitmap[offset / 8] & (1 << (offset % 8)))
3619 return false; /* not null */
3624 * Set a specific array element's null-bitmap entry
3626 * nullbitmap: pointer to array's null bitmap (mustn't be NULL)
3627 * offset: 0-based linear element number of array element
3628 * isNull: null status to set
3631 array_set_isnull(bits8 *nullbitmap, int offset, bool isNull)
3635 nullbitmap += offset / 8;
3636 bitmask = 1 << (offset % 8);
3638 *nullbitmap &= ~bitmask;
3640 *nullbitmap |= bitmask;
3644 * Fetch array element at pointer, converted correctly to a Datum
3646 * Caller must have handled case of NULL element
3649 ArrayCast(char *value, bool byval, int len)
3651 return fetch_att(value, byval, len);
3655 * Copy datum to *dest and return total space used (including align padding)
3657 * Caller must have handled case of NULL element
3660 ArrayCastAndSet(Datum src,
3671 store_att_byval(dest, src, typlen);
3673 memmove(dest, DatumGetPointer(src), typlen);
3674 inc = att_align(typlen, typalign);
3679 inc = att_addlength(0, typlen, src);
3680 memmove(dest, DatumGetPointer(src), inc);
3681 inc = att_align(inc, typalign);
3688 * Advance ptr over nitems array elements
3690 * ptr: starting location in array
3691 * offset: 0-based linear element number of first element (the one at *ptr)
3692 * nullbitmap: start of array's null bitmap, or NULL if none
3693 * nitems: number of array elements to advance over (>= 0)
3694 * typlen, typbyval, typalign: storage parameters of array element datatype
3696 * It is caller's responsibility to ensure that nitems is within range
3699 array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
3700 int typlen, bool typbyval, char typalign)
3705 /* easy if fixed-size elements and no NULLs */
3706 if (typlen > 0 && !nullbitmap)
3707 return ptr + nitems * ((Size) att_align(typlen, typalign));
3709 /* seems worth having separate loops for NULL and no-NULLs cases */
3712 nullbitmap += offset / 8;
3713 bitmask = 1 << (offset % 8);
3715 for (i = 0; i < nitems; i++)
3717 if (*nullbitmap & bitmask)
3719 ptr = att_addlength(ptr, typlen, PointerGetDatum(ptr));
3720 ptr = (char *) att_align(ptr, typalign);
3723 if (bitmask == 0x100)
3732 for (i = 0; i < nitems; i++)
3734 ptr = att_addlength(ptr, typlen, PointerGetDatum(ptr));
3735 ptr = (char *) att_align(ptr, typalign);
3742 * Compute total size of the nitems array elements starting at *ptr
3744 * Parameters same as for array_seek
3747 array_nelems_size(char *ptr, int offset, bits8 *nullbitmap, int nitems,
3748 int typlen, bool typbyval, char typalign)
3750 return array_seek(ptr, offset, nullbitmap, nitems,
3751 typlen, typbyval, typalign) - ptr;
3755 * Copy nitems array elements from srcptr to destptr
3757 * destptr: starting destination location (must be enough room!)
3758 * nitems: number of array elements to copy (>= 0)
3759 * srcptr: starting location in source array
3760 * offset: 0-based linear element number of first element (the one at *srcptr)
3761 * nullbitmap: start of source array's null bitmap, or NULL if none
3762 * typlen, typbyval, typalign: storage parameters of array element datatype
3764 * Returns number of bytes copied
3766 * NB: this does not take care of setting up the destination's null bitmap!
3769 array_copy(char *destptr, int nitems,
3770 char *srcptr, int offset, bits8 *nullbitmap,
3771 int typlen, bool typbyval, char typalign)
3775 numbytes = array_nelems_size(srcptr, offset, nullbitmap, nitems,
3776 typlen, typbyval, typalign);
3777 memcpy(destptr, srcptr, numbytes);
3782 * Copy nitems null-bitmap bits from source to destination
3784 * destbitmap: start of destination array's null bitmap (mustn't be NULL)
3785 * destoffset: 0-based linear element number of first dest element
3786 * srcbitmap: start of source array's null bitmap, or NULL if none
3787 * srcoffset: 0-based linear element number of first source element
3788 * nitems: number of bits to copy (>= 0)
3790 * If srcbitmap is NULL then we assume the source is all-non-NULL and
3791 * fill 1's into the destination bitmap. Note that only the specified
3792 * bits in the destination map are changed, not any before or after.
3794 * Note: this could certainly be optimized using standard bitblt methods.
3795 * However, it's not clear that the typical Postgres array has enough elements
3796 * to make it worth worrying too much. For the moment, KISS.
3799 array_bitmap_copy(bits8 *destbitmap, int destoffset,
3800 const bits8 *srcbitmap, int srcoffset,
3810 return; /* don't risk fetch off end of memory */
3811 destbitmap += destoffset / 8;
3812 destbitmask = 1 << (destoffset % 8);
3813 destbitval = *destbitmap;
3816 srcbitmap += srcoffset / 8;
3817 srcbitmask = 1 << (srcoffset % 8);
3818 srcbitval = *srcbitmap;
3819 while (nitems-- > 0)
3821 if (srcbitval & srcbitmask)
3822 destbitval |= destbitmask;
3824 destbitval &= ~destbitmask;
3826 if (destbitmask == 0x100)
3828 *destbitmap++ = destbitval;
3831 destbitval = *destbitmap;
3834 if (srcbitmask == 0x100)
3839 srcbitval = *srcbitmap;
3842 if (destbitmask != 1)
3843 *destbitmap = destbitval;
3847 while (nitems-- > 0)
3849 destbitval |= destbitmask;
3851 if (destbitmask == 0x100)
3853 *destbitmap++ = destbitval;
3856 destbitval = *destbitmap;
3859 if (destbitmask != 1)
3860 *destbitmap = destbitval;
3865 * Compute space needed for a slice of an array
3867 * We assume the caller has verified that the slice coordinates are valid.
3870 array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
3871 int ndim, int *dim, int *lb,
3873 int typlen, bool typbyval, char typalign)
3886 mda_get_range(ndim, span, st, endp);
3888 /* Pretty easy for fixed element length without nulls ... */
3889 if (typlen > 0 && !arraynullsptr)
3890 return ArrayGetNItems(ndim, span) * att_align(typlen, typalign);
3892 /* Else gotta do it the hard way */
3893 src_offset = ArrayGetOffset(ndim, dim, lb, st);
3894 ptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
3895 typlen, typbyval, typalign);
3896 mda_get_prod(ndim, dim, prod);
3897 mda_get_offset_values(ndim, dist, prod, span);
3898 for (i = 0; i < ndim; i++)
3905 ptr = array_seek(ptr, src_offset, arraynullsptr, dist[j],
3906 typlen, typbyval, typalign);
3907 src_offset += dist[j];
3909 if (!array_get_isnull(arraynullsptr, src_offset))
3911 inc = att_addlength(0, typlen, PointerGetDatum(ptr));
3912 inc = att_align(inc, typalign);
3917 } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
3922 * Extract a slice of an array into consecutive elements in the destination
3925 * We assume the caller has verified that the slice coordinates are valid,
3926 * allocated enough storage for the result, and initialized the header
3930 array_extract_slice(ArrayType *newarray,
3935 bits8 *arraynullsptr,
3942 char *destdataptr = ARR_DATA_PTR(newarray);
3943 bits8 *destnullsptr = ARR_NULLBITMAP(newarray);
3955 src_offset = ArrayGetOffset(ndim, dim, lb, st);
3956 srcdataptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
3957 typlen, typbyval, typalign);
3958 mda_get_prod(ndim, dim, prod);
3959 mda_get_range(ndim, span, st, endp);
3960 mda_get_offset_values(ndim, dist, prod, span);
3961 for (i = 0; i < ndim; i++)
3969 /* skip unwanted elements */
3970 srcdataptr = array_seek(srcdataptr, src_offset, arraynullsptr,
3972 typlen, typbyval, typalign);
3973 src_offset += dist[j];
3975 inc = array_copy(destdataptr, 1,
3976 srcdataptr, src_offset, arraynullsptr,
3977 typlen, typbyval, typalign);
3979 array_bitmap_copy(destnullsptr, dest_offset,
3980 arraynullsptr, src_offset,
3986 } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
3990 * Insert a slice into an array.
3992 * ndim/dim[]/lb[] are dimensions of the original array. A new array with
3993 * those same dimensions is to be constructed. destArray must already
3994 * have been allocated and its header initialized.
3996 * st[]/endp[] identify the slice to be replaced. Elements within the slice
3997 * volume are taken from consecutive elements of the srcArray; elements
3998 * outside it are copied from origArray.
4000 * We assume the caller has verified that the slice coordinates are valid.
4003 array_insert_slice(ArrayType *destArray,
4004 ArrayType *origArray,
4005 ArrayType *srcArray,
4015 char *destPtr = ARR_DATA_PTR(destArray);
4016 char *origPtr = ARR_DATA_PTR(origArray);
4017 char *srcPtr = ARR_DATA_PTR(srcArray);
4018 bits8 *destBitmap = ARR_NULLBITMAP(destArray);
4019 bits8 *origBitmap = ARR_NULLBITMAP(origArray);
4020 bits8 *srcBitmap = ARR_NULLBITMAP(srcArray);
4021 int orignitems = ArrayGetNItems(ARR_NDIM(origArray),
4022 ARR_DIMS(origArray));
4034 dest_offset = ArrayGetOffset(ndim, dim, lb, st);
4035 /* copy items before the slice start */
4036 inc = array_copy(destPtr, dest_offset,
4037 origPtr, 0, origBitmap,
4038 typlen, typbyval, typalign);
4042 array_bitmap_copy(destBitmap, 0, origBitmap, 0, dest_offset);
4043 orig_offset = dest_offset;
4044 mda_get_prod(ndim, dim, prod);
4045 mda_get_range(ndim, span, st, endp);
4046 mda_get_offset_values(ndim, dist, prod, span);
4047 for (i = 0; i < ndim; i++)
4053 /* Copy/advance over elements between here and next part of slice */
4056 inc = array_copy(destPtr, dist[j],
4057 origPtr, orig_offset, origBitmap,
4058 typlen, typbyval, typalign);
4062 array_bitmap_copy(destBitmap, dest_offset,
4063 origBitmap, orig_offset,
4065 dest_offset += dist[j];
4066 orig_offset += dist[j];
4068 /* Copy new element at this slice position */
4069 inc = array_copy(destPtr, 1,
4070 srcPtr, src_offset, srcBitmap,
4071 typlen, typbyval, typalign);
4073 array_bitmap_copy(destBitmap, dest_offset,
4074 srcBitmap, src_offset,
4080 /* Advance over old element at this slice position */
4081 origPtr = array_seek(origPtr, orig_offset, origBitmap, 1,
4082 typlen, typbyval, typalign);
4084 } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
4086 /* don't miss any data at the end */
4087 array_copy(destPtr, orignitems - orig_offset,
4088 origPtr, orig_offset, origBitmap,
4089 typlen, typbyval, typalign);
4091 array_bitmap_copy(destBitmap, dest_offset,
4092 origBitmap, orig_offset,
4093 orignitems - orig_offset);
4097 * array_type_coerce -- allow explicit or assignment coercion from
4098 * one array type to another.
4100 * array_type_length_coerce -- the same, for cases where both type and length
4101 * coercion are done by a single function on the element type.
4103 * Caller should have already verified that the source element type can be
4104 * coerced into the target element type.
4107 array_type_coerce(PG_FUNCTION_ARGS)
4109 ArrayType *src = PG_GETARG_ARRAYTYPE_P(0);
4110 FmgrInfo *fmgr_info = fcinfo->flinfo;
4112 return array_type_length_coerce_internal(src, -1, false, fmgr_info);
4116 array_type_length_coerce(PG_FUNCTION_ARGS)
4118 ArrayType *src = PG_GETARG_ARRAYTYPE_P(0);
4119 int32 desttypmod = PG_GETARG_INT32(1);
4120 bool isExplicit = PG_GETARG_BOOL(2);
4121 FmgrInfo *fmgr_info = fcinfo->flinfo;
4123 return array_type_length_coerce_internal(src, desttypmod,
4124 isExplicit, fmgr_info);
4128 array_type_length_coerce_internal(ArrayType *src,
4131 FmgrInfo *fmgr_info)
4133 Oid src_elem_type = ARR_ELEMTYPE(src);
4138 FmgrInfo coerce_finfo;
4139 ArrayMapState amstate;
4141 atc_extra *my_extra;
4142 FunctionCallInfoData locfcinfo;
4145 * We arrange to look up the coercion function only once per series of
4146 * calls, assuming the input data type doesn't change underneath us.
4147 * (Output type can't change.)
4149 my_extra = (atc_extra *) fmgr_info->fn_extra;
4150 if (my_extra == NULL)
4152 fmgr_info->fn_extra = MemoryContextAllocZero(fmgr_info->fn_mcxt,
4154 my_extra = (atc_extra *) fmgr_info->fn_extra;
4157 if (my_extra->srctype != src_elem_type)
4159 Oid tgt_type = get_fn_expr_rettype(fmgr_info);
4163 if (tgt_type == InvalidOid)
4165 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4166 errmsg("could not determine target array type")));
4168 tgt_elem_type = get_element_type(tgt_type);
4169 if (tgt_elem_type == InvalidOid)
4171 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4172 errmsg("target type is not an array")));
4175 * We don't deal with domain constraints yet, so bail out. This isn't
4176 * currently a problem, because we also don't support arrays of domain
4177 * type elements either. But in the future we might. At that point
4178 * consideration should be given to removing the check below and
4179 * adding a domain constraints check to the coercion.
4181 if (getBaseType(tgt_elem_type) != tgt_elem_type)
4183 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4184 errmsg("array coercion to domain type elements not "
4185 "currently supported")));
4187 if (!find_coercion_pathway(tgt_elem_type, src_elem_type,
4188 COERCION_EXPLICIT, &funcId))
4190 /* should never happen, but check anyway */
4191 elog(ERROR, "no conversion function from %s to %s",
4192 format_type_be(src_elem_type),
4193 format_type_be(tgt_elem_type));
4195 if (OidIsValid(funcId))
4196 fmgr_info_cxt(funcId, &my_extra->coerce_finfo, fmgr_info->fn_mcxt);
4198 my_extra->coerce_finfo.fn_oid = InvalidOid;
4199 my_extra->srctype = src_elem_type;
4200 my_extra->desttype = tgt_elem_type;
4204 * If it's binary-compatible, modify the element type in the array header,
4205 * but otherwise leave the array as we received it.
4207 if (my_extra->coerce_finfo.fn_oid == InvalidOid)
4211 result = (ArrayType *) DatumGetPointer(datumCopy(PointerGetDatum(src),
4213 ARR_ELEMTYPE(result) = my_extra->desttype;
4214 PG_RETURN_ARRAYTYPE_P(result);
4218 * Use array_map to apply the function to each array element.
4220 * We pass on the desttypmod and isExplicit flags whether or not the
4221 * function wants them.
4223 InitFunctionCallInfoData(locfcinfo, &my_extra->coerce_finfo, 3,
4225 locfcinfo.arg[0] = PointerGetDatum(src);
4226 locfcinfo.arg[1] = Int32GetDatum(desttypmod);
4227 locfcinfo.arg[2] = BoolGetDatum(isExplicit);
4228 locfcinfo.argnull[0] = false;
4229 locfcinfo.argnull[1] = false;
4230 locfcinfo.argnull[2] = false;
4232 return array_map(&locfcinfo, my_extra->srctype, my_extra->desttype,
4233 &my_extra->amstate);
4237 * array_length_coerce -- apply the element type's length-coercion routine
4238 * to each element of the given array.
4241 array_length_coerce(PG_FUNCTION_ARGS)
4243 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
4244 int32 desttypmod = PG_GETARG_INT32(1);
4245 bool isExplicit = PG_GETARG_BOOL(2);
4246 FmgrInfo *fmgr_info = fcinfo->flinfo;
4250 FmgrInfo coerce_finfo;
4251 ArrayMapState amstate;
4253 alc_extra *my_extra;
4254 FunctionCallInfoData locfcinfo;
4256 /* If no typmod is provided, shortcircuit the whole thing */
4258 PG_RETURN_ARRAYTYPE_P(v);
4261 * We arrange to look up the element type's coercion function only once
4262 * per series of calls, assuming the element type doesn't change
4265 my_extra = (alc_extra *) fmgr_info->fn_extra;
4266 if (my_extra == NULL)
4268 fmgr_info->fn_extra = MemoryContextAllocZero(fmgr_info->fn_mcxt,
4270 my_extra = (alc_extra *) fmgr_info->fn_extra;
4273 if (my_extra->elemtype != ARR_ELEMTYPE(v))
4277 funcId = find_typmod_coercion_function(ARR_ELEMTYPE(v));
4279 if (OidIsValid(funcId))
4280 fmgr_info_cxt(funcId, &my_extra->coerce_finfo, fmgr_info->fn_mcxt);
4282 my_extra->coerce_finfo.fn_oid = InvalidOid;
4283 my_extra->elemtype = ARR_ELEMTYPE(v);
4287 * If we didn't find a coercion function, return the array unmodified
4288 * (this should not happen in the normal course of things, but might
4289 * happen if this function is called manually).
4291 if (my_extra->coerce_finfo.fn_oid == InvalidOid)
4292 PG_RETURN_ARRAYTYPE_P(v);
4295 * Use array_map to apply the function to each array element.
4297 * Note: we pass isExplicit whether or not the function wants it ...
4299 InitFunctionCallInfoData(locfcinfo, &my_extra->coerce_finfo, 3,
4301 locfcinfo.arg[0] = PointerGetDatum(v);
4302 locfcinfo.arg[1] = Int32GetDatum(desttypmod);
4303 locfcinfo.arg[2] = BoolGetDatum(isExplicit);
4304 locfcinfo.argnull[0] = false;
4305 locfcinfo.argnull[1] = false;
4306 locfcinfo.argnull[2] = false;
4308 return array_map(&locfcinfo, ARR_ELEMTYPE(v), ARR_ELEMTYPE(v),
4309 &my_extra->amstate);
4313 * accumArrayResult - accumulate one (more) Datum for an array result
4315 * astate is working state (NULL on first call)
4316 * rcontext is where to keep working state
4319 accumArrayResult(ArrayBuildState *astate,
4320 Datum dvalue, bool disnull,
4322 MemoryContext rcontext)
4324 MemoryContext arr_context,
4329 /* First time through --- initialize */
4331 /* Make a temporary context to hold all the junk */
4332 arr_context = AllocSetContextCreate(rcontext,
4334 ALLOCSET_DEFAULT_MINSIZE,
4335 ALLOCSET_DEFAULT_INITSIZE,
4336 ALLOCSET_DEFAULT_MAXSIZE);
4337 oldcontext = MemoryContextSwitchTo(arr_context);
4338 astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
4339 astate->mcontext = arr_context;
4340 astate->dvalues = (Datum *)
4341 palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
4342 astate->dnulls = (bool *)
4343 palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(bool));
4345 astate->element_type = element_type;
4346 get_typlenbyvalalign(element_type,
4353 oldcontext = MemoryContextSwitchTo(astate->mcontext);
4354 Assert(astate->element_type == element_type);
4355 /* enlarge dvalues[]/dnulls[] if needed */
4356 if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
4358 astate->dvalues = (Datum *)
4359 repalloc(astate->dvalues,
4360 (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
4361 astate->dnulls = (bool *)
4362 repalloc(astate->dnulls,
4363 (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(bool));
4367 /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
4368 if (!disnull && !astate->typbyval)
4369 dvalue = datumCopy(dvalue, astate->typbyval, astate->typlen);
4371 astate->dvalues[astate->nelems] = dvalue;
4372 astate->dnulls[astate->nelems] = disnull;
4375 MemoryContextSwitchTo(oldcontext);
4381 * makeArrayResult - produce 1-D final result of accumArrayResult
4383 * astate is working state (not NULL)
4384 * rcontext is where to construct result
4387 makeArrayResult(ArrayBuildState *astate,
4388 MemoryContext rcontext)
4393 dims[0] = astate->nelems;
4396 return makeMdArrayResult(astate, 1, dims, lbs, rcontext);
4400 * makeMdArrayResult - produce multi-D final result of accumArrayResult
4402 * beware: no check that specified dimensions match the number of values
4405 * astate is working state (not NULL)
4406 * rcontext is where to construct result
4409 makeMdArrayResult(ArrayBuildState *astate,
4413 MemoryContext rcontext)
4416 MemoryContext oldcontext;
4418 /* Build the final array result in rcontext */
4419 oldcontext = MemoryContextSwitchTo(rcontext);
4421 result = construct_md_array(astate->dvalues,
4426 astate->element_type,
4431 MemoryContextSwitchTo(oldcontext);
4433 /* Clean up all the junk */
4434 MemoryContextDelete(astate->mcontext);
4436 return PointerGetDatum(result);
4440 array_larger(PG_FUNCTION_ARGS)
4446 v1 = PG_GETARG_ARRAYTYPE_P(0);
4447 v2 = PG_GETARG_ARRAYTYPE_P(1);
4449 result = ((array_cmp(fcinfo) > 0) ? v1 : v2);
4451 PG_RETURN_ARRAYTYPE_P(result);
4455 array_smaller(PG_FUNCTION_ARGS)
4461 v1 = PG_GETARG_ARRAYTYPE_P(0);
4462 v2 = PG_GETARG_ARRAYTYPE_P(1);
4464 result = ((array_cmp(fcinfo) < 0) ? v1 : v2);
4466 PG_RETURN_ARRAYTYPE_P(result);