1 /*-------------------------------------------------------------------------
4 * Support functions for arrays.
6 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
13 *-------------------------------------------------------------------------
20 #include "libpq/pqformat.h"
21 #include "parser/parse_coerce.h"
22 #include "utils/array.h"
23 #include "utils/builtins.h"
24 #include "utils/datum.h"
25 #include "utils/lsyscache.h"
26 #include "utils/memutils.h"
27 #include "utils/typcache.h"
33 bool Array_nulls
= true;
46 ARRAY_QUOTED_ELEM_STARTED
,
47 ARRAY_QUOTED_ELEM_COMPLETED
,
49 ARRAY_LEVEL_COMPLETED
,
53 static 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 ArrayType
*create_array_envelope(int ndims
, int *dimv
, int *lbv
, int nbytes
,
99 Oid elmtype
, int dataoffset
);
100 static ArrayType
*array_fill_internal(ArrayType
*dims
, ArrayType
*lbs
,
101 Datum value
, bool isnull
, Oid elmtype
,
102 FunctionCallInfo fcinfo
);
107 * converts an array from the external format in "string" to
108 * its internal format.
111 * the internal representation of the input array
114 array_in(PG_FUNCTION_ARGS
)
116 char *string
= PG_GETARG_CSTRING(0); /* external form */
117 Oid element_type
= PG_GETARG_OID(1); /* type of an array
119 int32 typmod
= PG_GETARG_INT32(2); /* typmod for array elements */
138 ArrayMetaState
*my_extra
;
141 * We arrange to look up info about element type, including its input
142 * conversion proc, only once per series of calls, assuming the element
143 * type doesn't change underneath us.
145 my_extra
= (ArrayMetaState
*) fcinfo
->flinfo
->fn_extra
;
146 if (my_extra
== NULL
)
148 fcinfo
->flinfo
->fn_extra
= MemoryContextAlloc(fcinfo
->flinfo
->fn_mcxt
,
149 sizeof(ArrayMetaState
));
150 my_extra
= (ArrayMetaState
*) fcinfo
->flinfo
->fn_extra
;
151 my_extra
->element_type
= ~element_type
;
154 if (my_extra
->element_type
!= element_type
)
157 * Get info about element type, including its input conversion proc
159 get_type_io_data(element_type
, IOFunc_input
,
160 &my_extra
->typlen
, &my_extra
->typbyval
,
161 &my_extra
->typalign
, &my_extra
->typdelim
,
162 &my_extra
->typioparam
, &my_extra
->typiofunc
);
163 fmgr_info_cxt(my_extra
->typiofunc
, &my_extra
->proc
,
164 fcinfo
->flinfo
->fn_mcxt
);
165 my_extra
->element_type
= element_type
;
167 typlen
= my_extra
->typlen
;
168 typbyval
= my_extra
->typbyval
;
169 typalign
= my_extra
->typalign
;
170 typdelim
= my_extra
->typdelim
;
171 typioparam
= my_extra
->typioparam
;
173 /* Make a modifiable copy of the input */
174 string_save
= pstrdup(string
);
177 * If the input string starts with dimension info, read and use that.
178 * Otherwise, we require the input to be in curly-brace style, and we
179 * prescan the input to determine dimensions.
181 * Dimension info takes the form of one or more [n] or [m:n] items. The
182 * outer loop iterates once per dimension item.
192 * Note: we currently allow whitespace between, but not within,
195 while (isspace((unsigned char) *p
))
198 break; /* no more dimension items */
202 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
203 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
206 for (q
= p
; isdigit((unsigned char) *q
) || (*q
== '-') || (*q
== '+'); q
++);
207 if (q
== p
) /* no digits? */
209 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION
),
210 errmsg("missing dimension value")));
216 lBound
[ndim
] = atoi(p
);
218 for (q
= p
; isdigit((unsigned char) *q
) || (*q
== '-') || (*q
== '+'); q
++);
219 if (q
== p
) /* no digits? */
221 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION
),
222 errmsg("missing dimension value")));
231 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION
),
232 errmsg("missing \"]\" in array dimensions")));
237 if (ub
< lBound
[ndim
])
239 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
240 errmsg("upper bound cannot be less than lower bound")));
242 dim
[ndim
] = ub
- lBound
[ndim
] + 1;
248 /* No array dimensions, so intuit dimensions from brace structure */
251 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION
),
252 errmsg("array value must start with \"{\" or dimension information")));
253 ndim
= ArrayCount(p
, dim
, typdelim
);
254 for (i
= 0; i
< ndim
; i
++)
262 /* If array dimensions are given, expect '=' operator */
263 if (strncmp(p
, ASSGN
, strlen(ASSGN
)) != 0)
265 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION
),
266 errmsg("missing assignment operator")));
268 while (isspace((unsigned char) *p
))
272 * intuit dimensions from brace structure -- it better match what we
277 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION
),
278 errmsg("array value must start with \"{\" or dimension information")));
279 ndim_braces
= ArrayCount(p
, dim_braces
, typdelim
);
280 if (ndim_braces
!= ndim
)
282 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION
),
283 errmsg("array dimensions incompatible with array literal")));
284 for (i
= 0; i
< ndim
; ++i
)
286 if (dim
[i
] != dim_braces
[i
])
288 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION
),
289 errmsg("array dimensions incompatible with array literal")));
294 printf("array_in- ndim %d (", ndim
);
295 for (i
= 0; i
< ndim
; i
++)
297 printf(" %d", dim
[i
]);
299 printf(") for %s\n", string
);
302 /* This checks for overflow of the array dimensions */
303 nitems
= ArrayGetNItems(ndim
, dim
);
306 PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type
));
308 dataPtr
= (Datum
*) palloc(nitems
* sizeof(Datum
));
309 nullsPtr
= (bool *) palloc(nitems
* sizeof(bool));
310 ReadArrayStr(p
, string
,
312 &my_extra
->proc
, typioparam
, typmod
,
314 typlen
, typbyval
, typalign
,
319 dataoffset
= ARR_OVERHEAD_WITHNULLS(ndim
, nitems
);
320 nbytes
+= dataoffset
;
324 dataoffset
= 0; /* marker for no null bitmap */
325 nbytes
+= ARR_OVERHEAD_NONULLS(ndim
);
327 retval
= (ArrayType
*) palloc0(nbytes
);
328 SET_VARSIZE(retval
, nbytes
);
330 retval
->dataoffset
= dataoffset
;
331 retval
->elemtype
= element_type
;
332 memcpy(ARR_DIMS(retval
), dim
, ndim
* sizeof(int));
333 memcpy(ARR_LBOUND(retval
), lBound
, ndim
* sizeof(int));
336 dataPtr
, nullsPtr
, nitems
,
337 typlen
, typbyval
, typalign
,
344 PG_RETURN_ARRAYTYPE_P(retval
);
349 * Determines the dimensions for an array string.
351 * Returns number of dimensions as function result. The axis lengths are
352 * returned in dim[], which must be of size MAXDIM.
355 ArrayCount(const char *str
, int *dim
, char typdelim
)
363 bool in_quotes
= false;
364 bool eoArray
= false;
365 bool empty_array
= true;
367 ArrayParseState parse_state
= ARRAY_NO_LEVEL
;
369 for (i
= 0; i
< MAXDIM
; ++i
)
371 temp
[i
] = dim
[i
] = 0;
372 nelems_last
[i
] = nelems
[i
] = 1;
378 bool itemdone
= false;
382 if (parse_state
== ARRAY_ELEM_STARTED
||
383 parse_state
== ARRAY_QUOTED_ELEM_STARTED
)
389 /* Signal a premature end of the string */
391 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION
),
392 errmsg("malformed array literal: \"%s\"", str
)));
397 * An escape must be after a level start, after an element
398 * start, or after an element delimiter. In any case we
399 * now must be past an element start.
401 if (parse_state
!= ARRAY_LEVEL_STARTED
&&
402 parse_state
!= ARRAY_ELEM_STARTED
&&
403 parse_state
!= ARRAY_QUOTED_ELEM_STARTED
&&
404 parse_state
!= ARRAY_ELEM_DELIMITED
)
406 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION
),
407 errmsg("malformed array literal: \"%s\"", str
)));
408 if (parse_state
!= ARRAY_QUOTED_ELEM_STARTED
)
409 parse_state
= ARRAY_ELEM_STARTED
;
410 /* skip the escaped character */
415 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION
),
416 errmsg("malformed array literal: \"%s\"", str
)));
421 * A quote must be after a level start, after a quoted
422 * element start, or after an element delimiter. In any
423 * case we now must be past an element start.
425 if (parse_state
!= ARRAY_LEVEL_STARTED
&&
426 parse_state
!= ARRAY_QUOTED_ELEM_STARTED
&&
427 parse_state
!= ARRAY_ELEM_DELIMITED
)
429 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION
),
430 errmsg("malformed array literal: \"%s\"", str
)));
431 in_quotes
= !in_quotes
;
433 parse_state
= ARRAY_QUOTED_ELEM_STARTED
;
435 parse_state
= ARRAY_QUOTED_ELEM_COMPLETED
;
441 * A left brace can occur if no nesting has occurred
442 * yet, after a level start, or after a level
445 if (parse_state
!= ARRAY_NO_LEVEL
&&
446 parse_state
!= ARRAY_LEVEL_STARTED
&&
447 parse_state
!= ARRAY_LEVEL_DELIMITED
)
449 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION
),
450 errmsg("malformed array literal: \"%s\"", str
)));
451 parse_state
= ARRAY_LEVEL_STARTED
;
452 if (nest_level
>= MAXDIM
)
454 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
455 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
456 nest_level
, MAXDIM
)));
457 temp
[nest_level
] = 0;
459 if (ndim
< nest_level
)
467 * A right brace can occur after an element start, an
468 * element completion, a quoted element completion, or
469 * a level completion.
471 if (parse_state
!= ARRAY_ELEM_STARTED
&&
472 parse_state
!= ARRAY_ELEM_COMPLETED
&&
473 parse_state
!= ARRAY_QUOTED_ELEM_COMPLETED
&&
474 parse_state
!= ARRAY_LEVEL_COMPLETED
&&
475 !(nest_level
== 1 && parse_state
== ARRAY_LEVEL_STARTED
))
477 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION
),
478 errmsg("malformed array literal: \"%s\"", str
)));
479 parse_state
= ARRAY_LEVEL_COMPLETED
;
482 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION
),
483 errmsg("malformed array literal: \"%s\"", str
)));
486 if ((nelems_last
[nest_level
] != 1) &&
487 (nelems
[nest_level
] != nelems_last
[nest_level
]))
489 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION
),
490 errmsg("multidimensional arrays must have "
491 "array expressions with matching "
493 nelems_last
[nest_level
] = nelems
[nest_level
];
494 nelems
[nest_level
] = 1;
496 eoArray
= itemdone
= true;
500 * We don't set itemdone here; see comments in
503 temp
[nest_level
- 1]++;
510 if (*ptr
== typdelim
)
513 * Delimiters can occur after an element start, an
514 * element completion, a quoted element
515 * completion, or a level completion.
517 if (parse_state
!= ARRAY_ELEM_STARTED
&&
518 parse_state
!= ARRAY_ELEM_COMPLETED
&&
519 parse_state
!= ARRAY_QUOTED_ELEM_COMPLETED
&&
520 parse_state
!= ARRAY_LEVEL_COMPLETED
)
522 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION
),
523 errmsg("malformed array literal: \"%s\"", str
)));
524 if (parse_state
== ARRAY_LEVEL_COMPLETED
)
525 parse_state
= ARRAY_LEVEL_DELIMITED
;
527 parse_state
= ARRAY_ELEM_DELIMITED
;
529 nelems
[nest_level
- 1]++;
531 else if (!isspace((unsigned char) *ptr
))
534 * Other non-space characters must be after a
535 * level start, after an element start, or after
536 * an element delimiter. In any case we now must
537 * be past an element start.
539 if (parse_state
!= ARRAY_LEVEL_STARTED
&&
540 parse_state
!= ARRAY_ELEM_STARTED
&&
541 parse_state
!= ARRAY_ELEM_DELIMITED
)
543 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION
),
544 errmsg("malformed array literal: \"%s\"", str
)));
545 parse_state
= ARRAY_ELEM_STARTED
;
557 /* only whitespace is allowed after the closing brace */
560 if (!isspace((unsigned char) *ptr
++))
562 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION
),
563 errmsg("malformed array literal: \"%s\"", str
)));
566 /* special case for an empty array */
570 for (i
= 0; i
< ndim
; ++i
)
578 * parses the array string pointed to by "arrayStr" and converts the values
579 * to internal format. Unspecified elements are initialized to nulls.
580 * The array dimensions must already have been determined.
583 * arrayStr: the string to parse.
584 * CAUTION: the contents of "arrayStr" will be modified!
585 * origStr: the unmodified input string, used only in error messages.
586 * nitems: total number of array elements, as already determined.
587 * ndim: number of array dimensions
588 * dim[]: array axis lengths
589 * inputproc: type-specific input procedure for element datatype.
590 * typioparam, typmod: auxiliary values to pass to inputproc.
591 * typdelim: the value delimiter (type-specific).
592 * typlen, typbyval, typalign: storage parameters of element datatype.
595 * values[]: filled with converted data values.
596 * nulls[]: filled with is-null markers.
597 * *hasnulls: set TRUE iff there are any null elements.
598 * *nbytes: set to total size of data area needed (including alignment
599 * padding but not including array header overhead).
601 * Note that values[] and nulls[] are allocated by the caller, and must have
605 ReadArrayStr(char *arrayStr
,
625 bool in_quotes
= false;
626 bool eoArray
= false;
632 mda_get_prod(ndim
, dim
, prod
);
633 MemSet(indx
, 0, sizeof(indx
));
635 /* Initialize is-null markers to true */
636 memset(nulls
, true, nitems
* sizeof(bool));
639 * We have to remove " and \ characters to create a clean item value to
640 * pass to the datatype input routine. We overwrite each item value
641 * in-place within arrayStr to do this. srcptr is the current scan point,
642 * and dstptr is where we are copying to.
644 * We also want to suppress leading and trailing unquoted whitespace. We
645 * use the leadingspace flag to suppress leading space. Trailing space is
646 * tracked by using dstendptr to point to the last significant output
649 * The error checking in this routine is mostly pro-forma, since we expect
650 * that ArrayCount() already validated the string.
655 bool itemdone
= false;
656 bool leadingspace
= true;
657 bool hasquoting
= false;
663 itemstart
= dstptr
= dstendptr
= srcptr
;
670 /* Signal a premature end of the string */
672 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION
),
673 errmsg("malformed array literal: \"%s\"",
677 /* Skip backslash, copy next character as-is. */
681 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION
),
682 errmsg("malformed array literal: \"%s\"",
684 *dstptr
++ = *srcptr
++;
685 /* Treat the escaped character as non-whitespace */
686 leadingspace
= false;
688 hasquoting
= true; /* can't be a NULL marker */
691 in_quotes
= !in_quotes
;
693 leadingspace
= false;
697 * Advance dstendptr when we exit in_quotes; this
698 * saves having to do it in all the other in_quotes
703 hasquoting
= true; /* can't be a NULL marker */
709 if (nest_level
>= ndim
)
711 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION
),
712 errmsg("malformed array literal: \"%s\"",
715 indx
[nest_level
- 1] = 0;
719 *dstptr
++ = *srcptr
++;
726 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION
),
727 errmsg("malformed array literal: \"%s\"",
730 i
= ArrayGetOffset0(ndim
, indx
, prod
);
731 indx
[nest_level
- 1] = 0;
734 eoArray
= itemdone
= true;
736 indx
[nest_level
- 1]++;
740 *dstptr
++ = *srcptr
++;
744 *dstptr
++ = *srcptr
++;
745 else if (*srcptr
== typdelim
)
748 i
= ArrayGetOffset0(ndim
, indx
, prod
);
753 else if (isspace((unsigned char) *srcptr
))
756 * If leading space, drop it immediately. Else, copy
757 * but don't advance dstendptr.
762 *dstptr
++ = *srcptr
++;
766 *dstptr
++ = *srcptr
++;
767 leadingspace
= false;
774 Assert(dstptr
< srcptr
);
777 if (i
< 0 || i
>= nitems
)
779 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION
),
780 errmsg("malformed array literal: \"%s\"",
783 if (Array_nulls
&& !hasquoting
&&
784 pg_strcasecmp(itemstart
, "NULL") == 0)
786 /* it's a NULL item */
787 values
[i
] = InputFunctionCall(inputproc
, NULL
,
793 values
[i
] = InputFunctionCall(inputproc
, itemstart
,
800 * Check for nulls, compute total data space needed
804 for (i
= 0; i
< nitems
; i
++)
810 /* let's just make sure data is not toasted */
812 values
[i
] = PointerGetDatum(PG_DETOAST_DATUM(values
[i
]));
813 totbytes
= att_addlength_datum(totbytes
, typlen
, values
[i
]);
814 totbytes
= att_align_nominal(totbytes
, typalign
);
815 /* check for overflow of total request */
816 if (!AllocSizeIsValid(totbytes
))
818 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
819 errmsg("array size exceeds the maximum allowed (%d)",
820 (int) MaxAllocSize
)));
829 * Copy data into an array object from a temporary array of Datums.
831 * array: array object (with header fields already filled in)
832 * values: array of Datums to be copied
833 * nulls: array of is-null flags (can be NULL if no nulls)
834 * nitems: number of Datums to be copied
835 * typbyval, typlen, typalign: info about element datatype
836 * freedata: if TRUE and element type is pass-by-ref, pfree data values
837 * referenced by Datums after copying them.
839 * If the input data is of varlena type, the caller must have ensured that
840 * the values are not toasted. (Doing it here doesn't work since the
841 * caller has already allocated space for the array...)
844 CopyArrayEls(ArrayType
*array
,
853 char *p
= ARR_DATA_PTR(array
);
854 bits8
*bitmap
= ARR_NULLBITMAP(array
);
862 for (i
= 0; i
< nitems
; i
++)
864 if (nulls
&& nulls
[i
])
866 if (!bitmap
) /* shouldn't happen */
867 elog(ERROR
, "null array element where not supported");
868 /* bitmap bit stays 0 */
873 p
+= ArrayCastAndSet(values
[i
], typlen
, typbyval
, typalign
, p
);
875 pfree(DatumGetPointer(values
[i
]));
880 if (bitmask
== 0x100)
889 if (bitmap
&& bitmask
!= 1)
895 * takes the internal representation of an array and returns a string
896 * containing the array in its external format.
899 array_out(PG_FUNCTION_ARGS
)
901 ArrayType
*v
= PG_GETARG_ARRAYTYPE_P(0);
902 Oid element_type
= ARR_ELEMTYPE(v
);
911 dims_str
[(MAXDIM
* 33) + 2];
914 * 33 per dim since we assume 15 digits per number + ':' +'[]'
916 * +2 allows for assignment operator + trailing null
931 ArrayMetaState
*my_extra
;
934 * We arrange to look up info about element type, including its output
935 * conversion proc, only once per series of calls, assuming the element
936 * type doesn't change underneath us.
938 my_extra
= (ArrayMetaState
*) fcinfo
->flinfo
->fn_extra
;
939 if (my_extra
== NULL
)
941 fcinfo
->flinfo
->fn_extra
= MemoryContextAlloc(fcinfo
->flinfo
->fn_mcxt
,
942 sizeof(ArrayMetaState
));
943 my_extra
= (ArrayMetaState
*) fcinfo
->flinfo
->fn_extra
;
944 my_extra
->element_type
= ~element_type
;
947 if (my_extra
->element_type
!= element_type
)
950 * Get info about element type, including its output conversion proc
952 get_type_io_data(element_type
, IOFunc_output
,
953 &my_extra
->typlen
, &my_extra
->typbyval
,
954 &my_extra
->typalign
, &my_extra
->typdelim
,
955 &my_extra
->typioparam
, &my_extra
->typiofunc
);
956 fmgr_info_cxt(my_extra
->typiofunc
, &my_extra
->proc
,
957 fcinfo
->flinfo
->fn_mcxt
);
958 my_extra
->element_type
= element_type
;
960 typlen
= my_extra
->typlen
;
961 typbyval
= my_extra
->typbyval
;
962 typalign
= my_extra
->typalign
;
963 typdelim
= my_extra
->typdelim
;
968 nitems
= ArrayGetNItems(ndim
, dims
);
972 retval
= pstrdup("{}");
973 PG_RETURN_CSTRING(retval
);
977 * we will need to add explicit dimensions if any dimension has a lower
978 * bound other than one
980 for (i
= 0; i
< ndim
; i
++)
990 * Convert all values to string form, count total space needed (including
991 * any overhead such as escaping backslashes), and detect whether each
992 * item needs double quotes.
994 values
= (char **) palloc(nitems
* sizeof(char *));
995 needquotes
= (bool *) palloc(nitems
* sizeof(bool));
996 overall_length
= 1; /* don't forget to count \0 at end. */
999 bitmap
= ARR_NULLBITMAP(v
);
1002 for (i
= 0; i
< nitems
; i
++)
1006 /* Get source element, checking for NULL */
1007 if (bitmap
&& (*bitmap
& bitmask
) == 0)
1009 values
[i
] = pstrdup("NULL");
1010 overall_length
+= 4;
1017 itemvalue
= fetch_att(p
, typbyval
, typlen
);
1018 values
[i
] = OutputFunctionCall(&my_extra
->proc
, itemvalue
);
1019 p
= att_addlength_pointer(p
, typlen
, p
);
1020 p
= (char *) att_align_nominal(p
, typalign
);
1022 /* count data plus backslashes; detect chars needing quotes */
1023 if (values
[i
][0] == '\0')
1024 needquote
= true; /* force quotes for empty string */
1025 else if (pg_strcasecmp(values
[i
], "NULL") == 0)
1026 needquote
= true; /* force quotes for literal NULL */
1030 for (tmp
= values
[i
]; *tmp
!= '\0'; tmp
++)
1034 overall_length
+= 1;
1035 if (ch
== '"' || ch
== '\\')
1038 overall_length
+= 1;
1040 else if (ch
== '{' || ch
== '}' || ch
== typdelim
||
1041 isspace((unsigned char) ch
))
1046 needquotes
[i
] = needquote
;
1048 /* Count the pair of double quotes, if needed */
1050 overall_length
+= 2;
1052 overall_length
+= 1;
1054 /* advance bitmap pointer if any */
1058 if (bitmask
== 0x100)
1067 * count total number of curly braces in output string
1069 for (i
= j
= 0, k
= 1; i
< ndim
; i
++)
1070 k
*= dims
[i
], j
+= k
;
1074 /* add explicit dimensions if required */
1077 char *ptr
= dims_str
;
1079 for (i
= 0; i
< ndim
; i
++)
1081 sprintf(ptr
, "[%d:%d]", lb
[i
], lb
[i
] + dims
[i
] - 1);
1088 retval
= (char *) palloc(strlen(dims_str
) + overall_length
+ 2 * j
);
1091 #define APPENDSTR(str) (strcpy(p, (str)), p += strlen(p))
1092 #define APPENDCHAR(ch) (*p++ = (ch), *p = '\0')
1095 APPENDSTR(dims_str
);
1097 for (i
= 0; i
< ndim
; i
++)
1103 for (i
= j
; i
< ndim
- 1; i
++)
1109 for (tmp
= values
[k
]; *tmp
; tmp
++)
1113 if (ch
== '"' || ch
== '\\')
1121 APPENDSTR(values
[k
]);
1124 for (i
= ndim
- 1; i
>= 0; i
--)
1126 indx
[i
] = (indx
[i
] + 1) % dims
[i
];
1129 APPENDCHAR(typdelim
);
1144 PG_RETURN_CSTRING(retval
);
1149 * converts an array from the external binary format to
1150 * its internal format.
1153 * the internal representation of the input array
1156 array_recv(PG_FUNCTION_ARGS
)
1158 StringInfo buf
= (StringInfo
) PG_GETARG_POINTER(0);
1159 Oid spec_element_type
= PG_GETARG_OID(1); /* type of an array
1161 int32 typmod
= PG_GETARG_INT32(2); /* typmod for array elements */
1179 ArrayMetaState
*my_extra
;
1181 /* Get the array header information */
1182 ndim
= pq_getmsgint(buf
, 4);
1183 if (ndim
< 0) /* we do allow zero-dimension arrays */
1185 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION
),
1186 errmsg("invalid number of dimensions: %d", ndim
)));
1189 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
1190 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
1193 flags
= pq_getmsgint(buf
, 4);
1194 if (flags
!= 0 && flags
!= 1)
1196 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION
),
1197 errmsg("invalid array flags")));
1199 element_type
= pq_getmsgint(buf
, sizeof(Oid
));
1200 if (element_type
!= spec_element_type
)
1202 /* XXX Can we allow taking the input element type in any cases? */
1204 (errcode(ERRCODE_DATATYPE_MISMATCH
),
1205 errmsg("wrong element type")));
1208 for (i
= 0; i
< ndim
; i
++)
1210 dim
[i
] = pq_getmsgint(buf
, 4);
1211 lBound
[i
] = pq_getmsgint(buf
, 4);
1214 /* This checks for overflow of array dimensions */
1215 nitems
= ArrayGetNItems(ndim
, dim
);
1218 * We arrange to look up info about element type, including its receive
1219 * conversion proc, only once per series of calls, assuming the element
1220 * type doesn't change underneath us.
1222 my_extra
= (ArrayMetaState
*) fcinfo
->flinfo
->fn_extra
;
1223 if (my_extra
== NULL
)
1225 fcinfo
->flinfo
->fn_extra
= MemoryContextAlloc(fcinfo
->flinfo
->fn_mcxt
,
1226 sizeof(ArrayMetaState
));
1227 my_extra
= (ArrayMetaState
*) fcinfo
->flinfo
->fn_extra
;
1228 my_extra
->element_type
= ~element_type
;
1231 if (my_extra
->element_type
!= element_type
)
1233 /* Get info about element type, including its receive proc */
1234 get_type_io_data(element_type
, IOFunc_receive
,
1235 &my_extra
->typlen
, &my_extra
->typbyval
,
1236 &my_extra
->typalign
, &my_extra
->typdelim
,
1237 &my_extra
->typioparam
, &my_extra
->typiofunc
);
1238 if (!OidIsValid(my_extra
->typiofunc
))
1240 (errcode(ERRCODE_UNDEFINED_FUNCTION
),
1241 errmsg("no binary input function available for type %s",
1242 format_type_be(element_type
))));
1243 fmgr_info_cxt(my_extra
->typiofunc
, &my_extra
->proc
,
1244 fcinfo
->flinfo
->fn_mcxt
);
1245 my_extra
->element_type
= element_type
;
1250 /* Return empty array ... but not till we've validated element_type */
1251 PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type
));
1254 typlen
= my_extra
->typlen
;
1255 typbyval
= my_extra
->typbyval
;
1256 typalign
= my_extra
->typalign
;
1257 typioparam
= my_extra
->typioparam
;
1259 dataPtr
= (Datum
*) palloc(nitems
* sizeof(Datum
));
1260 nullsPtr
= (bool *) palloc(nitems
* sizeof(bool));
1261 ReadArrayBinary(buf
, nitems
,
1262 &my_extra
->proc
, typioparam
, typmod
,
1263 typlen
, typbyval
, typalign
,
1265 &hasnulls
, &nbytes
);
1268 dataoffset
= ARR_OVERHEAD_WITHNULLS(ndim
, nitems
);
1269 nbytes
+= dataoffset
;
1273 dataoffset
= 0; /* marker for no null bitmap */
1274 nbytes
+= ARR_OVERHEAD_NONULLS(ndim
);
1276 retval
= (ArrayType
*) palloc(nbytes
);
1277 SET_VARSIZE(retval
, nbytes
);
1278 retval
->ndim
= ndim
;
1279 retval
->dataoffset
= dataoffset
;
1280 retval
->elemtype
= element_type
;
1281 memcpy(ARR_DIMS(retval
), dim
, ndim
* sizeof(int));
1282 memcpy(ARR_LBOUND(retval
), lBound
, ndim
* sizeof(int));
1284 CopyArrayEls(retval
,
1285 dataPtr
, nullsPtr
, nitems
,
1286 typlen
, typbyval
, typalign
,
1292 PG_RETURN_ARRAYTYPE_P(retval
);
1297 * collect the data elements of an array being read in binary style.
1300 * buf: the data buffer to read from.
1301 * nitems: total number of array elements (already read).
1302 * receiveproc: type-specific receive procedure for element datatype.
1303 * typioparam, typmod: auxiliary values to pass to receiveproc.
1304 * typlen, typbyval, typalign: storage parameters of element datatype.
1307 * values[]: filled with converted data values.
1308 * nulls[]: filled with is-null markers.
1309 * *hasnulls: set TRUE iff there are any null elements.
1310 * *nbytes: set to total size of data area needed (including alignment
1311 * padding but not including array header overhead).
1313 * Note that values[] and nulls[] are allocated by the caller, and must have
1317 ReadArrayBinary(StringInfo buf
,
1319 FmgrInfo
*receiveproc
,
1334 for (i
= 0; i
< nitems
; i
++)
1337 StringInfoData elem_buf
;
1340 /* Get and check the item length */
1341 itemlen
= pq_getmsgint(buf
, 4);
1342 if (itemlen
< -1 || itemlen
> (buf
->len
- buf
->cursor
))
1344 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION
),
1345 errmsg("insufficient data left in message")));
1349 /* -1 length means NULL */
1350 values
[i
] = ReceiveFunctionCall(receiveproc
, NULL
,
1351 typioparam
, typmod
);
1357 * Rather than copying data around, we just set up a phony StringInfo
1358 * pointing to the correct portion of the input buffer. We assume we
1359 * can scribble on the input buffer so as to maintain the convention
1360 * that StringInfos have a trailing null.
1362 elem_buf
.data
= &buf
->data
[buf
->cursor
];
1363 elem_buf
.maxlen
= itemlen
+ 1;
1364 elem_buf
.len
= itemlen
;
1365 elem_buf
.cursor
= 0;
1367 buf
->cursor
+= itemlen
;
1369 csave
= buf
->data
[buf
->cursor
];
1370 buf
->data
[buf
->cursor
] = '\0';
1372 /* Now call the element's receiveproc */
1373 values
[i
] = ReceiveFunctionCall(receiveproc
, &elem_buf
,
1374 typioparam
, typmod
);
1377 /* Trouble if it didn't eat the whole buffer */
1378 if (elem_buf
.cursor
!= itemlen
)
1380 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION
),
1381 errmsg("improper binary format in array element %d",
1384 buf
->data
[buf
->cursor
] = csave
;
1388 * Check for nulls, compute total data space needed
1392 for (i
= 0; i
< nitems
; i
++)
1398 /* let's just make sure data is not toasted */
1400 values
[i
] = PointerGetDatum(PG_DETOAST_DATUM(values
[i
]));
1401 totbytes
= att_addlength_datum(totbytes
, typlen
, values
[i
]);
1402 totbytes
= att_align_nominal(totbytes
, typalign
);
1403 /* check for overflow of total request */
1404 if (!AllocSizeIsValid(totbytes
))
1406 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
1407 errmsg("array size exceeds the maximum allowed (%d)",
1408 (int) MaxAllocSize
)));
1411 *hasnulls
= hasnull
;
1418 * takes the internal representation of an array and returns a bytea
1419 * containing the array in its external binary format.
1422 array_send(PG_FUNCTION_ARGS
)
1424 ArrayType
*v
= PG_GETARG_ARRAYTYPE_P(0);
1425 Oid element_type
= ARR_ELEMTYPE(v
);
1437 ArrayMetaState
*my_extra
;
1440 * We arrange to look up info about element type, including its send
1441 * conversion proc, only once per series of calls, assuming the element
1442 * type doesn't change underneath us.
1444 my_extra
= (ArrayMetaState
*) fcinfo
->flinfo
->fn_extra
;
1445 if (my_extra
== NULL
)
1447 fcinfo
->flinfo
->fn_extra
= MemoryContextAlloc(fcinfo
->flinfo
->fn_mcxt
,
1448 sizeof(ArrayMetaState
));
1449 my_extra
= (ArrayMetaState
*) fcinfo
->flinfo
->fn_extra
;
1450 my_extra
->element_type
= ~element_type
;
1453 if (my_extra
->element_type
!= element_type
)
1455 /* Get info about element type, including its send proc */
1456 get_type_io_data(element_type
, IOFunc_send
,
1457 &my_extra
->typlen
, &my_extra
->typbyval
,
1458 &my_extra
->typalign
, &my_extra
->typdelim
,
1459 &my_extra
->typioparam
, &my_extra
->typiofunc
);
1460 if (!OidIsValid(my_extra
->typiofunc
))
1462 (errcode(ERRCODE_UNDEFINED_FUNCTION
),
1463 errmsg("no binary output function available for type %s",
1464 format_type_be(element_type
))));
1465 fmgr_info_cxt(my_extra
->typiofunc
, &my_extra
->proc
,
1466 fcinfo
->flinfo
->fn_mcxt
);
1467 my_extra
->element_type
= element_type
;
1469 typlen
= my_extra
->typlen
;
1470 typbyval
= my_extra
->typbyval
;
1471 typalign
= my_extra
->typalign
;
1475 nitems
= ArrayGetNItems(ndim
, dim
);
1477 pq_begintypsend(&buf
);
1479 /* Send the array header information */
1480 pq_sendint(&buf
, ndim
, 4);
1481 pq_sendint(&buf
, ARR_HASNULL(v
) ? 1 : 0, 4);
1482 pq_sendint(&buf
, element_type
, sizeof(Oid
));
1483 for (i
= 0; i
< ndim
; i
++)
1485 pq_sendint(&buf
, ARR_DIMS(v
)[i
], 4);
1486 pq_sendint(&buf
, ARR_LBOUND(v
)[i
], 4);
1489 /* Send the array elements using the element's own sendproc */
1490 p
= ARR_DATA_PTR(v
);
1491 bitmap
= ARR_NULLBITMAP(v
);
1494 for (i
= 0; i
< nitems
; i
++)
1496 /* Get source element, checking for NULL */
1497 if (bitmap
&& (*bitmap
& bitmask
) == 0)
1499 /* -1 length means a NULL */
1500 pq_sendint(&buf
, -1, 4);
1507 itemvalue
= fetch_att(p
, typbyval
, typlen
);
1508 outputbytes
= SendFunctionCall(&my_extra
->proc
, itemvalue
);
1509 pq_sendint(&buf
, VARSIZE(outputbytes
) - VARHDRSZ
, 4);
1510 pq_sendbytes(&buf
, VARDATA(outputbytes
),
1511 VARSIZE(outputbytes
) - VARHDRSZ
);
1514 p
= att_addlength_pointer(p
, typlen
, p
);
1515 p
= (char *) att_align_nominal(p
, typalign
);
1518 /* advance bitmap pointer if any */
1522 if (bitmask
== 0x100)
1530 PG_RETURN_BYTEA_P(pq_endtypsend(&buf
));
1535 * returns the number of dimensions of the array pointed to by "v"
1538 array_ndims(PG_FUNCTION_ARGS
)
1540 ArrayType
*v
= PG_GETARG_ARRAYTYPE_P(0);
1542 /* Sanity check: does it look like an array at all? */
1543 if (ARR_NDIM(v
) <= 0 || ARR_NDIM(v
) > MAXDIM
)
1546 PG_RETURN_INT32(ARR_NDIM(v
));
1551 * returns the dimensions of the array pointed to by "v", as a "text"
1554 array_dims(PG_FUNCTION_ARGS
)
1556 ArrayType
*v
= PG_GETARG_ARRAYTYPE_P(0);
1563 * 33 since we assume 15 digits per number + ':' +'[]'
1565 * +1 for trailing null
1567 char buf
[MAXDIM
* 33 + 1];
1569 /* Sanity check: does it look like an array at all? */
1570 if (ARR_NDIM(v
) <= 0 || ARR_NDIM(v
) > MAXDIM
)
1577 for (i
= 0; i
< ARR_NDIM(v
); i
++)
1579 sprintf(p
, "[%d:%d]", lb
[i
], dimv
[i
] + lb
[i
] - 1);
1583 PG_RETURN_TEXT_P(cstring_to_text(buf
));
1588 * returns the lower dimension, of the DIM requested, for
1589 * the array pointed to by "v", as an int4
1592 array_lower(PG_FUNCTION_ARGS
)
1594 ArrayType
*v
= PG_GETARG_ARRAYTYPE_P(0);
1595 int reqdim
= PG_GETARG_INT32(1);
1599 /* Sanity check: does it look like an array at all? */
1600 if (ARR_NDIM(v
) <= 0 || ARR_NDIM(v
) > MAXDIM
)
1603 /* Sanity check: was the requested dim valid */
1604 if (reqdim
<= 0 || reqdim
> ARR_NDIM(v
))
1608 result
= lb
[reqdim
- 1];
1610 PG_RETURN_INT32(result
);
1615 * returns the upper dimension, of the DIM requested, for
1616 * the array pointed to by "v", as an int4
1619 array_upper(PG_FUNCTION_ARGS
)
1621 ArrayType
*v
= PG_GETARG_ARRAYTYPE_P(0);
1622 int reqdim
= PG_GETARG_INT32(1);
1627 /* Sanity check: does it look like an array at all? */
1628 if (ARR_NDIM(v
) <= 0 || ARR_NDIM(v
) > MAXDIM
)
1631 /* Sanity check: was the requested dim valid */
1632 if (reqdim
<= 0 || reqdim
> ARR_NDIM(v
))
1638 result
= dimv
[reqdim
- 1] + lb
[reqdim
- 1] - 1;
1640 PG_RETURN_INT32(result
);
1645 * returns the length, of the dimension requested, for
1646 * the array pointed to by "v", as an int4
1649 array_length(PG_FUNCTION_ARGS
)
1651 ArrayType
*v
= PG_GETARG_ARRAYTYPE_P(0);
1652 int reqdim
= PG_GETARG_INT32(1);
1656 /* Sanity check: does it look like an array at all? */
1657 if (ARR_NDIM(v
) <= 0 || ARR_NDIM(v
) > MAXDIM
)
1660 /* Sanity check: was the requested dim valid */
1661 if (reqdim
<= 0 || reqdim
> ARR_NDIM(v
))
1666 result
= dimv
[reqdim
- 1];
1668 PG_RETURN_INT32(result
);
1673 * This routine takes an array pointer and a subscript array and returns
1674 * the referenced item as a Datum. Note that for a pass-by-reference
1675 * datatype, the returned Datum is a pointer into the array object.
1677 * This handles both ordinary varlena arrays and fixed-length arrays.
1680 * array: the array object (mustn't be NULL)
1681 * nSubscripts: number of subscripts supplied
1682 * indx[]: the subscript values
1683 * arraytyplen: pg_type.typlen for the array type
1684 * elmlen: pg_type.typlen for the array's element type
1685 * elmbyval: pg_type.typbyval for the array's element type
1686 * elmalign: pg_type.typalign for the array's element type
1689 * The return value is the element Datum.
1690 * *isNull is set to indicate whether the element is NULL.
1693 array_ref(ArrayType
*array
,
1711 bits8
*arraynullsptr
;
1713 if (arraytyplen
> 0)
1716 * fixed-length arrays -- these are assumed to be 1-d, 0-based
1719 fixedDim
[0] = arraytyplen
/ elmlen
;
1723 arraydataptr
= (char *) array
;
1724 arraynullsptr
= NULL
;
1728 /* detoast input array if necessary */
1729 array
= DatumGetArrayTypeP(PointerGetDatum(array
));
1731 ndim
= ARR_NDIM(array
);
1732 dim
= ARR_DIMS(array
);
1733 lb
= ARR_LBOUND(array
);
1734 arraydataptr
= ARR_DATA_PTR(array
);
1735 arraynullsptr
= ARR_NULLBITMAP(array
);
1739 * Return NULL for invalid subscript
1741 if (ndim
!= nSubscripts
|| ndim
<= 0 || ndim
> MAXDIM
)
1746 for (i
= 0; i
< ndim
; i
++)
1748 if (indx
[i
] < lb
[i
] || indx
[i
] >= (dim
[i
] + lb
[i
]))
1756 * Calculate the element number
1758 offset
= ArrayGetOffset(nSubscripts
, dim
, lb
, indx
);
1761 * Check for NULL array element
1763 if (array_get_isnull(arraynullsptr
, offset
))
1770 * OK, get the element
1773 retptr
= array_seek(arraydataptr
, 0, arraynullsptr
, offset
,
1774 elmlen
, elmbyval
, elmalign
);
1775 return ArrayCast(retptr
, elmbyval
, elmlen
);
1780 * This routine takes an array and a range of indices (upperIndex and
1781 * lowerIndx), creates a new array structure for the referred elements
1782 * and returns a pointer to it.
1784 * This handles both ordinary varlena arrays and fixed-length arrays.
1787 * array: the array object (mustn't be NULL)
1788 * nSubscripts: number of subscripts supplied (must be same for upper/lower)
1789 * upperIndx[]: the upper subscript values
1790 * lowerIndx[]: the lower subscript values
1791 * arraytyplen: pg_type.typlen for the array type
1792 * elmlen: pg_type.typlen for the array's element type
1793 * elmbyval: pg_type.typbyval for the array's element type
1794 * elmalign: pg_type.typalign for the array's element type
1797 * The return value is the new array Datum (it's never NULL)
1799 * NOTE: we assume it is OK to scribble on the provided subscript arrays
1800 * lowerIndx[] and upperIndx[]. These are generally just temporaries.
1803 array_get_slice(ArrayType
*array
,
1812 ArrayType
*newarray
;
1822 bits8
*arraynullsptr
;
1827 if (arraytyplen
> 0)
1830 * fixed-length arrays -- currently, cannot slice these because parser
1831 * labels output as being of the fixed-length array type! Code below
1832 * shows how we could support it if the parser were changed to label
1833 * output as a suitable varlena array type.
1836 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
1837 errmsg("slices of fixed-length arrays not implemented")));
1840 * fixed-length arrays -- these are assumed to be 1-d, 0-based
1842 * XXX where would we get the correct ELEMTYPE from?
1845 fixedDim
[0] = arraytyplen
/ elmlen
;
1849 elemtype
= InvalidOid
; /* XXX */
1850 arraydataptr
= (char *) array
;
1851 arraynullsptr
= NULL
;
1855 /* detoast input array if necessary */
1856 array
= DatumGetArrayTypeP(PointerGetDatum(array
));
1858 ndim
= ARR_NDIM(array
);
1859 dim
= ARR_DIMS(array
);
1860 lb
= ARR_LBOUND(array
);
1861 elemtype
= ARR_ELEMTYPE(array
);
1862 arraydataptr
= ARR_DATA_PTR(array
);
1863 arraynullsptr
= ARR_NULLBITMAP(array
);
1867 * Check provided subscripts. A slice exceeding the current array limits
1868 * is silently truncated to the array limits. If we end up with an empty
1869 * slice, return an empty array.
1871 if (ndim
< nSubscripts
|| ndim
<= 0 || ndim
> MAXDIM
)
1872 return construct_empty_array(elemtype
);
1874 for (i
= 0; i
< nSubscripts
; i
++)
1876 if (lowerIndx
[i
] < lb
[i
])
1877 lowerIndx
[i
] = lb
[i
];
1878 if (upperIndx
[i
] >= (dim
[i
] + lb
[i
]))
1879 upperIndx
[i
] = dim
[i
] + lb
[i
] - 1;
1880 if (lowerIndx
[i
] > upperIndx
[i
])
1881 return construct_empty_array(elemtype
);
1883 /* fill any missing subscript positions with full array range */
1884 for (; i
< ndim
; i
++)
1886 lowerIndx
[i
] = lb
[i
];
1887 upperIndx
[i
] = dim
[i
] + lb
[i
] - 1;
1888 if (lowerIndx
[i
] > upperIndx
[i
])
1889 return construct_empty_array(elemtype
);
1892 mda_get_range(ndim
, span
, lowerIndx
, upperIndx
);
1894 bytes
= array_slice_size(arraydataptr
, arraynullsptr
,
1896 lowerIndx
, upperIndx
,
1897 elmlen
, elmbyval
, elmalign
);
1900 * Currently, we put a null bitmap in the result if the source has one;
1901 * could be smarter ...
1905 dataoffset
= ARR_OVERHEAD_WITHNULLS(ndim
, ArrayGetNItems(ndim
, span
));
1906 bytes
+= dataoffset
;
1910 dataoffset
= 0; /* marker for no null bitmap */
1911 bytes
+= ARR_OVERHEAD_NONULLS(ndim
);
1914 newarray
= (ArrayType
*) palloc(bytes
);
1915 SET_VARSIZE(newarray
, bytes
);
1916 newarray
->ndim
= ndim
;
1917 newarray
->dataoffset
= dataoffset
;
1918 newarray
->elemtype
= elemtype
;
1919 memcpy(ARR_DIMS(newarray
), span
, ndim
* sizeof(int));
1922 * Lower bounds of the new array are set to 1. Formerly (before 7.3) we
1923 * copied the given lowerIndx values ... but that seems confusing.
1925 newlb
= ARR_LBOUND(newarray
);
1926 for (i
= 0; i
< ndim
; i
++)
1929 array_extract_slice(newarray
,
1931 arraydataptr
, arraynullsptr
,
1932 lowerIndx
, upperIndx
,
1933 elmlen
, elmbyval
, elmalign
);
1940 * This routine sets the value of an array element (specified by
1941 * a subscript array) to a new value specified by "dataValue".
1943 * This handles both ordinary varlena arrays and fixed-length arrays.
1946 * array: the initial array object (mustn't be NULL)
1947 * nSubscripts: number of subscripts supplied
1948 * indx[]: the subscript values
1949 * dataValue: the datum to be inserted at the given position
1950 * isNull: whether dataValue is NULL
1951 * arraytyplen: pg_type.typlen for the array type
1952 * elmlen: pg_type.typlen for the array's element type
1953 * elmbyval: pg_type.typbyval for the array's element type
1954 * elmalign: pg_type.typalign for the array's element type
1957 * A new array is returned, just like the old except for the one
1958 * modified entry. The original array object is not changed.
1960 * For one-dimensional arrays only, we allow the array to be extended
1961 * by assigning to a position outside the existing subscript range; any
1962 * positions between the existing elements and the new one are set to NULLs.
1963 * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
1965 * NOTE: For assignments, we throw an error for invalid subscripts etc,
1966 * rather than returning a NULL as the fetch operations do.
1969 array_set(ArrayType
*array
,
1979 ArrayType
*newarray
;
1987 bits8
*oldnullbitmap
;
2001 if (arraytyplen
> 0)
2004 * fixed-length arrays -- these are assumed to be 1-d, 0-based. We
2005 * cannot extend them, either.
2007 if (nSubscripts
!= 1)
2009 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
2010 errmsg("wrong number of array subscripts")));
2012 if (indx
[0] < 0 || indx
[0] * elmlen
>= arraytyplen
)
2014 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
2015 errmsg("array subscript out of range")));
2019 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED
),
2020 errmsg("cannot assign null value to an element of a fixed-length array")));
2022 newarray
= (ArrayType
*) palloc(arraytyplen
);
2023 memcpy(newarray
, array
, arraytyplen
);
2024 elt_ptr
= (char *) newarray
+ indx
[0] * elmlen
;
2025 ArrayCastAndSet(dataValue
, elmlen
, elmbyval
, elmalign
, elt_ptr
);
2029 if (nSubscripts
<= 0 || nSubscripts
> MAXDIM
)
2031 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
2032 errmsg("wrong number of array subscripts")));
2034 /* make sure item to be inserted is not toasted */
2035 if (elmlen
== -1 && !isNull
)
2036 dataValue
= PointerGetDatum(PG_DETOAST_DATUM(dataValue
));
2038 /* detoast input array if necessary */
2039 array
= DatumGetArrayTypeP(PointerGetDatum(array
));
2041 ndim
= ARR_NDIM(array
);
2044 * if number of dims is zero, i.e. an empty array, create an array with
2045 * nSubscripts dimensions, and set the lower bounds to the supplied
2050 Oid elmtype
= ARR_ELEMTYPE(array
);
2052 for (i
= 0; i
< nSubscripts
; i
++)
2058 return construct_md_array(&dataValue
, &isNull
, nSubscripts
,
2060 elmlen
, elmbyval
, elmalign
);
2063 if (ndim
!= nSubscripts
)
2065 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
2066 errmsg("wrong number of array subscripts")));
2068 /* copy dim/lb since we may modify them */
2069 memcpy(dim
, ARR_DIMS(array
), ndim
* sizeof(int));
2070 memcpy(lb
, ARR_LBOUND(array
), ndim
* sizeof(int));
2072 newhasnulls
= (ARR_HASNULL(array
) || isNull
);
2073 addedbefore
= addedafter
= 0;
2080 if (indx
[0] < lb
[0])
2082 addedbefore
= lb
[0] - indx
[0];
2083 dim
[0] += addedbefore
;
2085 if (addedbefore
> 1)
2086 newhasnulls
= true; /* will insert nulls */
2088 if (indx
[0] >= (dim
[0] + lb
[0]))
2090 addedafter
= indx
[0] - (dim
[0] + lb
[0]) + 1;
2091 dim
[0] += addedafter
;
2093 newhasnulls
= true; /* will insert nulls */
2099 * XXX currently we do not support extending multi-dimensional arrays
2102 for (i
= 0; i
< ndim
; i
++)
2104 if (indx
[i
] < lb
[i
] ||
2105 indx
[i
] >= (dim
[i
] + lb
[i
]))
2107 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
2108 errmsg("array subscript out of range")));
2113 * Compute sizes of items and areas to copy
2115 newnitems
= ArrayGetNItems(ndim
, dim
);
2117 overheadlen
= ARR_OVERHEAD_WITHNULLS(ndim
, newnitems
);
2119 overheadlen
= ARR_OVERHEAD_NONULLS(ndim
);
2120 oldnitems
= ArrayGetNItems(ndim
, ARR_DIMS(array
));
2121 oldnullbitmap
= ARR_NULLBITMAP(array
);
2122 oldoverheadlen
= ARR_DATA_OFFSET(array
);
2123 olddatasize
= ARR_SIZE(array
) - oldoverheadlen
;
2129 lenafter
= olddatasize
;
2131 else if (addedafter
)
2134 lenbefore
= olddatasize
;
2140 offset
= ArrayGetOffset(nSubscripts
, dim
, lb
, indx
);
2141 elt_ptr
= array_seek(ARR_DATA_PTR(array
), 0, oldnullbitmap
, offset
,
2142 elmlen
, elmbyval
, elmalign
);
2143 lenbefore
= (int) (elt_ptr
- ARR_DATA_PTR(array
));
2144 if (array_get_isnull(oldnullbitmap
, offset
))
2148 olditemlen
= att_addlength_pointer(0, elmlen
, elt_ptr
);
2149 olditemlen
= att_align_nominal(olditemlen
, elmalign
);
2151 lenafter
= (int) (olddatasize
- lenbefore
- olditemlen
);
2158 newitemlen
= att_addlength_datum(0, elmlen
, dataValue
);
2159 newitemlen
= att_align_nominal(newitemlen
, elmalign
);
2162 newsize
= overheadlen
+ lenbefore
+ newitemlen
+ lenafter
;
2165 * OK, create the new array and fill in header/dimensions
2167 newarray
= (ArrayType
*) palloc(newsize
);
2168 SET_VARSIZE(newarray
, newsize
);
2169 newarray
->ndim
= ndim
;
2170 newarray
->dataoffset
= newhasnulls
? overheadlen
: 0;
2171 newarray
->elemtype
= ARR_ELEMTYPE(array
);
2172 memcpy(ARR_DIMS(newarray
), dim
, ndim
* sizeof(int));
2173 memcpy(ARR_LBOUND(newarray
), lb
, ndim
* sizeof(int));
2178 memcpy((char *) newarray
+ overheadlen
,
2179 (char *) array
+ oldoverheadlen
,
2182 ArrayCastAndSet(dataValue
, elmlen
, elmbyval
, elmalign
,
2183 (char *) newarray
+ overheadlen
+ lenbefore
);
2184 memcpy((char *) newarray
+ overheadlen
+ lenbefore
+ newitemlen
,
2185 (char *) array
+ oldoverheadlen
+ lenbefore
+ olditemlen
,
2189 * Fill in nulls bitmap if needed
2191 * Note: it's possible we just replaced the last NULL with a non-NULL, and
2192 * could get rid of the bitmap. Seems not worth testing for though.
2196 bits8
*newnullbitmap
= ARR_NULLBITMAP(newarray
);
2198 /* Zero the bitmap to take care of marking inserted positions null */
2199 MemSet(newnullbitmap
, 0, (newnitems
+ 7) / 8);
2200 /* Fix the inserted value */
2202 array_set_isnull(newnullbitmap
, newnitems
- 1, isNull
);
2204 array_set_isnull(newnullbitmap
, offset
, isNull
);
2205 /* Fix the copied range(s) */
2207 array_bitmap_copy(newnullbitmap
, addedbefore
,
2212 array_bitmap_copy(newnullbitmap
, 0,
2215 if (addedafter
== 0)
2216 array_bitmap_copy(newnullbitmap
, offset
+ 1,
2217 oldnullbitmap
, offset
+ 1,
2218 oldnitems
- offset
- 1);
2227 * This routine sets the value of a range of array locations (specified
2228 * by upper and lower subscript values) to new values passed as
2231 * This handles both ordinary varlena arrays and fixed-length arrays.
2234 * array: the initial array object (mustn't be NULL)
2235 * nSubscripts: number of subscripts supplied (must be same for upper/lower)
2236 * upperIndx[]: the upper subscript values
2237 * lowerIndx[]: the lower subscript values
2238 * srcArray: the source for the inserted values
2239 * isNull: indicates whether srcArray is NULL
2240 * arraytyplen: pg_type.typlen for the array type
2241 * elmlen: pg_type.typlen for the array's element type
2242 * elmbyval: pg_type.typbyval for the array's element type
2243 * elmalign: pg_type.typalign for the array's element type
2246 * A new array is returned, just like the old except for the
2247 * modified range. The original array object is not changed.
2249 * For one-dimensional arrays only, we allow the array to be extended
2250 * by assigning to positions outside the existing subscript range; any
2251 * positions between the existing elements and the new ones are set to NULLs.
2252 * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
2254 * NOTE: we assume it is OK to scribble on the provided index arrays
2255 * lowerIndx[] and upperIndx[]. These are generally just temporaries.
2257 * NOTE: For assignments, we throw an error for silly subscripts etc,
2258 * rather than returning a NULL or empty array as the fetch operations do.
2261 array_set_slice(ArrayType
*array
,
2265 ArrayType
*srcArray
,
2272 ArrayType
*newarray
;
2295 /* Currently, assignment from a NULL source array is a no-op */
2299 if (arraytyplen
> 0)
2302 * fixed-length arrays -- not got round to doing this...
2305 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
2306 errmsg("updates on slices of fixed-length arrays not implemented")));
2309 /* detoast arrays if necessary */
2310 array
= DatumGetArrayTypeP(PointerGetDatum(array
));
2311 srcArray
= DatumGetArrayTypeP(PointerGetDatum(srcArray
));
2313 /* note: we assume srcArray contains no toasted elements */
2315 ndim
= ARR_NDIM(array
);
2318 * if number of dims is zero, i.e. an empty array, create an array with
2319 * nSubscripts dimensions, and set the upper and lower bounds to the
2320 * supplied subscripts
2327 Oid elmtype
= ARR_ELEMTYPE(array
);
2329 deconstruct_array(srcArray
, elmtype
, elmlen
, elmbyval
, elmalign
,
2330 &dvalues
, &dnulls
, &nelems
);
2332 for (i
= 0; i
< nSubscripts
; i
++)
2334 dim
[i
] = 1 + upperIndx
[i
] - lowerIndx
[i
];
2335 lb
[i
] = lowerIndx
[i
];
2338 /* complain if too few source items; we ignore extras, however */
2339 if (nelems
< ArrayGetNItems(nSubscripts
, dim
))
2341 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
2342 errmsg("source array too small")));
2344 return construct_md_array(dvalues
, dnulls
, nSubscripts
,
2346 elmlen
, elmbyval
, elmalign
);
2349 if (ndim
< nSubscripts
|| ndim
<= 0 || ndim
> MAXDIM
)
2351 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
2352 errmsg("wrong number of array subscripts")));
2354 /* copy dim/lb since we may modify them */
2355 memcpy(dim
, ARR_DIMS(array
), ndim
* sizeof(int));
2356 memcpy(lb
, ARR_LBOUND(array
), ndim
* sizeof(int));
2358 newhasnulls
= (ARR_HASNULL(array
) || ARR_HASNULL(srcArray
));
2359 addedbefore
= addedafter
= 0;
2366 Assert(nSubscripts
== 1);
2367 if (lowerIndx
[0] > upperIndx
[0])
2369 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
2370 errmsg("upper bound cannot be less than lower bound")));
2371 if (lowerIndx
[0] < lb
[0])
2373 if (upperIndx
[0] < lb
[0] - 1)
2374 newhasnulls
= true; /* will insert nulls */
2375 addedbefore
= lb
[0] - lowerIndx
[0];
2376 dim
[0] += addedbefore
;
2377 lb
[0] = lowerIndx
[0];
2379 if (upperIndx
[0] >= (dim
[0] + lb
[0]))
2381 if (lowerIndx
[0] > (dim
[0] + lb
[0]))
2382 newhasnulls
= true; /* will insert nulls */
2383 addedafter
= upperIndx
[0] - (dim
[0] + lb
[0]) + 1;
2384 dim
[0] += addedafter
;
2390 * XXX currently we do not support extending multi-dimensional arrays
2393 for (i
= 0; i
< nSubscripts
; i
++)
2395 if (lowerIndx
[i
] > upperIndx
[i
])
2397 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
2398 errmsg("upper bound cannot be less than lower bound")));
2399 if (lowerIndx
[i
] < lb
[i
] ||
2400 upperIndx
[i
] >= (dim
[i
] + lb
[i
]))
2402 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
2403 errmsg("array subscript out of range")));
2405 /* fill any missing subscript positions with full array range */
2406 for (; i
< ndim
; i
++)
2408 lowerIndx
[i
] = lb
[i
];
2409 upperIndx
[i
] = dim
[i
] + lb
[i
] - 1;
2410 if (lowerIndx
[i
] > upperIndx
[i
])
2412 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
2413 errmsg("upper bound cannot be less than lower bound")));
2417 /* Do this mainly to check for overflow */
2418 nitems
= ArrayGetNItems(ndim
, dim
);
2421 * Make sure source array has enough entries. Note we ignore the shape of
2422 * the source array and just read entries serially.
2424 mda_get_range(ndim
, span
, lowerIndx
, upperIndx
);
2425 nsrcitems
= ArrayGetNItems(ndim
, span
);
2426 if (nsrcitems
> ArrayGetNItems(ARR_NDIM(srcArray
), ARR_DIMS(srcArray
)))
2428 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
2429 errmsg("source array too small")));
2432 * Compute space occupied by new entries, space occupied by replaced
2433 * entries, and required space for new array.
2436 overheadlen
= ARR_OVERHEAD_WITHNULLS(ndim
, nitems
);
2438 overheadlen
= ARR_OVERHEAD_NONULLS(ndim
);
2439 newitemsize
= array_nelems_size(ARR_DATA_PTR(srcArray
), 0,
2440 ARR_NULLBITMAP(srcArray
), nsrcitems
,
2441 elmlen
, elmbyval
, elmalign
);
2442 oldoverheadlen
= ARR_DATA_OFFSET(array
);
2443 olddatasize
= ARR_SIZE(array
) - oldoverheadlen
;
2447 * here we do not need to cope with extension of the array; it would
2448 * be a lot more complicated if we had to do so...
2450 olditemsize
= array_slice_size(ARR_DATA_PTR(array
),
2451 ARR_NULLBITMAP(array
),
2453 lowerIndx
, upperIndx
,
2454 elmlen
, elmbyval
, elmalign
);
2455 lenbefore
= lenafter
= 0; /* keep compiler quiet */
2456 itemsbefore
= itemsafter
= nolditems
= 0;
2461 * here we must allow for possibility of slice larger than orig array
2463 int oldlb
= ARR_LBOUND(array
)[0];
2464 int oldub
= oldlb
+ ARR_DIMS(array
)[0] - 1;
2465 int slicelb
= Max(oldlb
, lowerIndx
[0]);
2466 int sliceub
= Min(oldub
, upperIndx
[0]);
2467 char *oldarraydata
= ARR_DATA_PTR(array
);
2468 bits8
*oldarraybitmap
= ARR_NULLBITMAP(array
);
2470 itemsbefore
= Min(slicelb
, oldub
+ 1) - oldlb
;
2471 lenbefore
= array_nelems_size(oldarraydata
, 0, oldarraybitmap
,
2473 elmlen
, elmbyval
, elmalign
);
2474 if (slicelb
> sliceub
)
2481 nolditems
= sliceub
- slicelb
+ 1;
2482 olditemsize
= array_nelems_size(oldarraydata
+ lenbefore
,
2483 itemsbefore
, oldarraybitmap
,
2485 elmlen
, elmbyval
, elmalign
);
2487 itemsafter
= oldub
- sliceub
;
2488 lenafter
= olddatasize
- lenbefore
- olditemsize
;
2491 newsize
= overheadlen
+ olddatasize
- olditemsize
+ newitemsize
;
2493 newarray
= (ArrayType
*) palloc(newsize
);
2494 SET_VARSIZE(newarray
, newsize
);
2495 newarray
->ndim
= ndim
;
2496 newarray
->dataoffset
= newhasnulls
? overheadlen
: 0;
2497 newarray
->elemtype
= ARR_ELEMTYPE(array
);
2498 memcpy(ARR_DIMS(newarray
), dim
, ndim
* sizeof(int));
2499 memcpy(ARR_LBOUND(newarray
), lb
, ndim
* sizeof(int));
2504 * here we do not need to cope with extension of the array; it would
2505 * be a lot more complicated if we had to do so...
2507 array_insert_slice(newarray
, array
, srcArray
,
2509 lowerIndx
, upperIndx
,
2510 elmlen
, elmbyval
, elmalign
);
2515 memcpy((char *) newarray
+ overheadlen
,
2516 (char *) array
+ oldoverheadlen
,
2518 memcpy((char *) newarray
+ overheadlen
+ lenbefore
,
2519 ARR_DATA_PTR(srcArray
),
2521 memcpy((char *) newarray
+ overheadlen
+ lenbefore
+ newitemsize
,
2522 (char *) array
+ oldoverheadlen
+ lenbefore
+ olditemsize
,
2524 /* fill in nulls bitmap if needed */
2527 bits8
*newnullbitmap
= ARR_NULLBITMAP(newarray
);
2528 bits8
*oldnullbitmap
= ARR_NULLBITMAP(array
);
2530 /* Zero the bitmap to handle marking inserted positions null */
2531 MemSet(newnullbitmap
, 0, (nitems
+ 7) / 8);
2532 array_bitmap_copy(newnullbitmap
, addedbefore
,
2535 array_bitmap_copy(newnullbitmap
, lowerIndx
[0] - lb
[0],
2536 ARR_NULLBITMAP(srcArray
), 0,
2538 array_bitmap_copy(newnullbitmap
, addedbefore
+ itemsbefore
+ nolditems
,
2539 oldnullbitmap
, itemsbefore
+ nolditems
,
2550 * Map an array through an arbitrary function. Return a new array with
2551 * same dimensions and each source element transformed by fn(). Each
2552 * source element is passed as the first argument to fn(); additional
2553 * arguments to be passed to fn() can be specified by the caller.
2554 * The output array can have a different element type than the input.
2557 * * fcinfo: a function-call data structure pre-constructed by the caller
2558 * to be ready to call the desired function, with everything except the
2559 * first argument position filled in. In particular, flinfo identifies
2560 * the function fn(), and if nargs > 1 then argument positions after the
2561 * first must be preset to the additional values to be passed. The
2562 * first argument position initially holds the input array value.
2563 * * inpType: OID of element type of input array. This must be the same as,
2564 * or binary-compatible with, the first argument type of fn().
2565 * * retType: OID of element type of output array. This must be the same as,
2566 * or binary-compatible with, the result type of fn().
2567 * * amstate: workspace for array_map. Must be zeroed by caller before
2568 * first call, and not touched after that.
2570 * It is legitimate to pass a freshly-zeroed ArrayMapState on each call,
2571 * but better performance can be had if the state can be preserved across
2572 * a series of calls.
2574 * NB: caller must assure that input array is not NULL. NULL elements in
2575 * the array are OK however.
2578 array_map(FunctionCallInfo fcinfo
, Oid inpType
, Oid retType
,
2579 ArrayMapState
*amstate
)
2602 ArrayMetaState
*inp_extra
;
2603 ArrayMetaState
*ret_extra
;
2605 /* Get input array */
2606 if (fcinfo
->nargs
< 1)
2607 elog(ERROR
, "invalid nargs: %d", fcinfo
->nargs
);
2608 if (PG_ARGISNULL(0))
2609 elog(ERROR
, "null input array");
2610 v
= PG_GETARG_ARRAYTYPE_P(0);
2612 Assert(ARR_ELEMTYPE(v
) == inpType
);
2616 nitems
= ArrayGetNItems(ndim
, dim
);
2618 /* Check for empty array */
2621 /* Return empty array */
2622 PG_RETURN_ARRAYTYPE_P(construct_empty_array(retType
));
2626 * We arrange to look up info about input and return element types only
2627 * once per series of calls, assuming the element type doesn't change
2630 inp_extra
= &amstate
->inp_extra
;
2631 ret_extra
= &amstate
->ret_extra
;
2633 if (inp_extra
->element_type
!= inpType
)
2635 get_typlenbyvalalign(inpType
,
2637 &inp_extra
->typbyval
,
2638 &inp_extra
->typalign
);
2639 inp_extra
->element_type
= inpType
;
2641 inp_typlen
= inp_extra
->typlen
;
2642 inp_typbyval
= inp_extra
->typbyval
;
2643 inp_typalign
= inp_extra
->typalign
;
2645 if (ret_extra
->element_type
!= retType
)
2647 get_typlenbyvalalign(retType
,
2649 &ret_extra
->typbyval
,
2650 &ret_extra
->typalign
);
2651 ret_extra
->element_type
= retType
;
2653 typlen
= ret_extra
->typlen
;
2654 typbyval
= ret_extra
->typbyval
;
2655 typalign
= ret_extra
->typalign
;
2657 /* Allocate temporary arrays for new values */
2658 values
= (Datum
*) palloc(nitems
* sizeof(Datum
));
2659 nulls
= (bool *) palloc(nitems
* sizeof(bool));
2661 /* Loop over source data */
2662 s
= ARR_DATA_PTR(v
);
2663 bitmap
= ARR_NULLBITMAP(v
);
2667 for (i
= 0; i
< nitems
; i
++)
2671 /* Get source element, checking for NULL */
2672 if (bitmap
&& (*bitmap
& bitmask
) == 0)
2674 fcinfo
->argnull
[0] = true;
2678 elt
= fetch_att(s
, inp_typbyval
, inp_typlen
);
2679 s
= att_addlength_datum(s
, inp_typlen
, elt
);
2680 s
= (char *) att_align_nominal(s
, inp_typalign
);
2681 fcinfo
->arg
[0] = elt
;
2682 fcinfo
->argnull
[0] = false;
2686 * Apply the given function to source elt and extra args.
2688 if (fcinfo
->flinfo
->fn_strict
)
2692 for (j
= 0; j
< fcinfo
->nargs
; j
++)
2694 if (fcinfo
->argnull
[j
])
2704 fcinfo
->isnull
= false;
2705 values
[i
] = FunctionCallInvoke(fcinfo
);
2708 fcinfo
->isnull
= true;
2710 nulls
[i
] = fcinfo
->isnull
;
2715 /* Ensure data is not toasted */
2717 values
[i
] = PointerGetDatum(PG_DETOAST_DATUM(values
[i
]));
2718 /* Update total result size */
2719 nbytes
= att_addlength_datum(nbytes
, typlen
, values
[i
]);
2720 nbytes
= att_align_nominal(nbytes
, typalign
);
2721 /* check for overflow of total request */
2722 if (!AllocSizeIsValid(nbytes
))
2724 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
2725 errmsg("array size exceeds the maximum allowed (%d)",
2726 (int) MaxAllocSize
)));
2729 /* advance bitmap pointer if any */
2733 if (bitmask
== 0x100)
2741 /* Allocate and initialize the result array */
2744 dataoffset
= ARR_OVERHEAD_WITHNULLS(ndim
, nitems
);
2745 nbytes
+= dataoffset
;
2749 dataoffset
= 0; /* marker for no null bitmap */
2750 nbytes
+= ARR_OVERHEAD_NONULLS(ndim
);
2752 result
= (ArrayType
*) palloc(nbytes
);
2753 SET_VARSIZE(result
, nbytes
);
2754 result
->ndim
= ndim
;
2755 result
->dataoffset
= dataoffset
;
2756 result
->elemtype
= retType
;
2757 memcpy(ARR_DIMS(result
), ARR_DIMS(v
), 2 * ndim
* sizeof(int));
2760 * Note: do not risk trying to pfree the results of the called function
2762 CopyArrayEls(result
,
2763 values
, nulls
, nitems
,
2764 typlen
, typbyval
, typalign
,
2770 PG_RETURN_ARRAYTYPE_P(result
);
2774 * construct_array --- simple method for constructing an array object
2776 * elems: array of Datum items to become the array contents
2777 * (NULL element values are not supported).
2778 * nelems: number of items
2779 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
2781 * A palloc'd 1-D array object is constructed and returned. Note that
2782 * elem values will be copied into the object even if pass-by-ref type.
2784 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
2785 * from the system catalogs, given the elmtype. However, the caller is
2786 * in a better position to cache this info across multiple uses, or even
2787 * to hard-wire values if the element type is hard-wired.
2790 construct_array(Datum
*elems
, int nelems
,
2792 int elmlen
, bool elmbyval
, char elmalign
)
2800 return construct_md_array(elems
, NULL
, 1, dims
, lbs
,
2801 elmtype
, elmlen
, elmbyval
, elmalign
);
2805 * construct_md_array --- simple method for constructing an array object
2806 * with arbitrary dimensions and possible NULLs
2808 * elems: array of Datum items to become the array contents
2809 * nulls: array of is-null flags (can be NULL if no nulls)
2810 * ndims: number of dimensions
2811 * dims: integer array with size of each dimension
2812 * lbs: integer array with lower bound of each dimension
2813 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
2815 * A palloc'd ndims-D array object is constructed and returned. Note that
2816 * elem values will be copied into the object even if pass-by-ref type.
2818 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
2819 * from the system catalogs, given the elmtype. However, the caller is
2820 * in a better position to cache this info across multiple uses, or even
2821 * to hard-wire values if the element type is hard-wired.
2824 construct_md_array(Datum
*elems
,
2829 Oid elmtype
, int elmlen
, bool elmbyval
, char elmalign
)
2838 if (ndims
< 0) /* we do allow zero-dimension arrays */
2840 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
2841 errmsg("invalid number of dimensions: %d", ndims
)));
2844 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
2845 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
2848 /* fast track for empty array */
2850 return construct_empty_array(elmtype
);
2852 nelems
= ArrayGetNItems(ndims
, dims
);
2854 /* compute required space */
2857 for (i
= 0; i
< nelems
; i
++)
2859 if (nulls
&& nulls
[i
])
2864 /* make sure data is not toasted */
2866 elems
[i
] = PointerGetDatum(PG_DETOAST_DATUM(elems
[i
]));
2867 nbytes
= att_addlength_datum(nbytes
, elmlen
, elems
[i
]);
2868 nbytes
= att_align_nominal(nbytes
, elmalign
);
2869 /* check for overflow of total request */
2870 if (!AllocSizeIsValid(nbytes
))
2872 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
2873 errmsg("array size exceeds the maximum allowed (%d)",
2874 (int) MaxAllocSize
)));
2877 /* Allocate and initialize result array */
2880 dataoffset
= ARR_OVERHEAD_WITHNULLS(ndims
, nelems
);
2881 nbytes
+= dataoffset
;
2885 dataoffset
= 0; /* marker for no null bitmap */
2886 nbytes
+= ARR_OVERHEAD_NONULLS(ndims
);
2888 result
= (ArrayType
*) palloc(nbytes
);
2889 SET_VARSIZE(result
, nbytes
);
2890 result
->ndim
= ndims
;
2891 result
->dataoffset
= dataoffset
;
2892 result
->elemtype
= elmtype
;
2893 memcpy(ARR_DIMS(result
), dims
, ndims
* sizeof(int));
2894 memcpy(ARR_LBOUND(result
), lbs
, ndims
* sizeof(int));
2896 CopyArrayEls(result
,
2897 elems
, nulls
, nelems
,
2898 elmlen
, elmbyval
, elmalign
,
2905 * construct_empty_array --- make a zero-dimensional array of given type
2908 construct_empty_array(Oid elmtype
)
2912 result
= (ArrayType
*) palloc(sizeof(ArrayType
));
2913 SET_VARSIZE(result
, sizeof(ArrayType
));
2915 result
->dataoffset
= 0;
2916 result
->elemtype
= elmtype
;
2921 * deconstruct_array --- simple method for extracting data from an array
2923 * array: array object to examine (must not be NULL)
2924 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
2925 * elemsp: return value, set to point to palloc'd array of Datum values
2926 * nullsp: return value, set to point to palloc'd array of isnull markers
2927 * nelemsp: return value, set to number of extracted values
2929 * The caller may pass nullsp == NULL if it does not support NULLs in the
2930 * array. Note that this produces a very uninformative error message,
2931 * so do it only in cases where a NULL is really not expected.
2933 * If array elements are pass-by-ref data type, the returned Datums will
2934 * be pointers into the array object.
2936 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
2937 * from the system catalogs, given the elmtype. However, in most current
2938 * uses the type is hard-wired into the caller and so we can save a lookup
2939 * cycle by hard-wiring the type info as well.
2942 deconstruct_array(ArrayType
*array
,
2944 int elmlen
, bool elmbyval
, char elmalign
,
2945 Datum
**elemsp
, bool **nullsp
, int *nelemsp
)
2955 Assert(ARR_ELEMTYPE(array
) == elmtype
);
2957 nelems
= ArrayGetNItems(ARR_NDIM(array
), ARR_DIMS(array
));
2958 *elemsp
= elems
= (Datum
*) palloc(nelems
* sizeof(Datum
));
2960 *nullsp
= nulls
= (bool *) palloc(nelems
* sizeof(bool));
2965 p
= ARR_DATA_PTR(array
);
2966 bitmap
= ARR_NULLBITMAP(array
);
2969 for (i
= 0; i
< nelems
; i
++)
2971 /* Get source element, checking for NULL */
2972 if (bitmap
&& (*bitmap
& bitmask
) == 0)
2974 elems
[i
] = (Datum
) 0;
2979 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED
),
2980 errmsg("null array element not allowed in this context")));
2984 elems
[i
] = fetch_att(p
, elmbyval
, elmlen
);
2987 p
= att_addlength_pointer(p
, elmlen
, p
);
2988 p
= (char *) att_align_nominal(p
, elmalign
);
2991 /* advance bitmap pointer if any */
2995 if (bitmask
== 0x100)
3007 * compares two arrays for equality
3009 * returns true if the arrays are equal, false otherwise.
3011 * Note: we do not use array_cmp here, since equality may be meaningful in
3012 * datatypes that don't have a total ordering (and hence no btree support).
3015 array_eq(PG_FUNCTION_ARGS
)
3017 ArrayType
*array1
= PG_GETARG_ARRAYTYPE_P(0);
3018 ArrayType
*array2
= PG_GETARG_ARRAYTYPE_P(1);
3019 int ndims1
= ARR_NDIM(array1
);
3020 int ndims2
= ARR_NDIM(array2
);
3021 int *dims1
= ARR_DIMS(array1
);
3022 int *dims2
= ARR_DIMS(array2
);
3023 Oid element_type
= ARR_ELEMTYPE(array1
);
3026 TypeCacheEntry
*typentry
;
3036 FunctionCallInfoData locfcinfo
;
3038 if (element_type
!= ARR_ELEMTYPE(array2
))
3040 (errcode(ERRCODE_DATATYPE_MISMATCH
),
3041 errmsg("cannot compare arrays of different element types")));
3043 /* fast path if the arrays do not have the same dimensionality */
3044 if (ndims1
!= ndims2
||
3045 memcmp(dims1
, dims2
, 2 * ndims1
* sizeof(int)) != 0)
3050 * We arrange to look up the equality function only once per series of
3051 * calls, assuming the element type doesn't change underneath us. The
3052 * typcache is used so that we have no memory leakage when being used
3053 * as an index support function.
3055 typentry
= (TypeCacheEntry
*) fcinfo
->flinfo
->fn_extra
;
3056 if (typentry
== NULL
||
3057 typentry
->type_id
!= element_type
)
3059 typentry
= lookup_type_cache(element_type
,
3060 TYPECACHE_EQ_OPR_FINFO
);
3061 if (!OidIsValid(typentry
->eq_opr_finfo
.fn_oid
))
3063 (errcode(ERRCODE_UNDEFINED_FUNCTION
),
3064 errmsg("could not identify an equality operator for type %s",
3065 format_type_be(element_type
))));
3066 fcinfo
->flinfo
->fn_extra
= (void *) typentry
;
3068 typlen
= typentry
->typlen
;
3069 typbyval
= typentry
->typbyval
;
3070 typalign
= typentry
->typalign
;
3073 * apply the operator to each pair of array elements.
3075 InitFunctionCallInfoData(locfcinfo
, &typentry
->eq_opr_finfo
, 2,
3078 /* Loop over source data */
3079 nitems
= ArrayGetNItems(ndims1
, dims1
);
3080 ptr1
= ARR_DATA_PTR(array1
);
3081 ptr2
= ARR_DATA_PTR(array2
);
3082 bitmap1
= ARR_NULLBITMAP(array1
);
3083 bitmap2
= ARR_NULLBITMAP(array2
);
3084 bitmask
= 1; /* use same bitmask for both arrays */
3086 for (i
= 0; i
< nitems
; i
++)
3094 /* Get elements, checking for NULL */
3095 if (bitmap1
&& (*bitmap1
& bitmask
) == 0)
3103 elt1
= fetch_att(ptr1
, typbyval
, typlen
);
3104 ptr1
= att_addlength_pointer(ptr1
, typlen
, ptr1
);
3105 ptr1
= (char *) att_align_nominal(ptr1
, typalign
);
3108 if (bitmap2
&& (*bitmap2
& bitmask
) == 0)
3116 elt2
= fetch_att(ptr2
, typbyval
, typlen
);
3117 ptr2
= att_addlength_pointer(ptr2
, typlen
, ptr2
);
3118 ptr2
= (char *) att_align_nominal(ptr2
, typalign
);
3121 /* advance bitmap pointers if any */
3123 if (bitmask
== 0x100)
3133 * We consider two NULLs equal; NULL and not-NULL are unequal.
3135 if (isnull1
&& isnull2
)
3137 if (isnull1
|| isnull2
)
3144 * Apply the operator to the element pair
3146 locfcinfo
.arg
[0] = elt1
;
3147 locfcinfo
.arg
[1] = elt2
;
3148 locfcinfo
.argnull
[0] = false;
3149 locfcinfo
.argnull
[1] = false;
3150 locfcinfo
.isnull
= false;
3151 oprresult
= DatumGetBool(FunctionCallInvoke(&locfcinfo
));
3160 /* Avoid leaking memory when handed toasted input. */
3161 PG_FREE_IF_COPY(array1
, 0);
3162 PG_FREE_IF_COPY(array2
, 1);
3164 PG_RETURN_BOOL(result
);
3168 /*-----------------------------------------------------------------------------
3169 * array-array bool operators:
3170 * Given two arrays, iterate comparison operators
3171 * over the array. Uses logic similar to text comparison
3172 * functions, except element-by-element instead of
3173 * character-by-character.
3174 *----------------------------------------------------------------------------
3178 array_ne(PG_FUNCTION_ARGS
)
3180 PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo
)));
3184 array_lt(PG_FUNCTION_ARGS
)
3186 PG_RETURN_BOOL(array_cmp(fcinfo
) < 0);
3190 array_gt(PG_FUNCTION_ARGS
)
3192 PG_RETURN_BOOL(array_cmp(fcinfo
) > 0);
3196 array_le(PG_FUNCTION_ARGS
)
3198 PG_RETURN_BOOL(array_cmp(fcinfo
) <= 0);
3202 array_ge(PG_FUNCTION_ARGS
)
3204 PG_RETURN_BOOL(array_cmp(fcinfo
) >= 0);
3208 btarraycmp(PG_FUNCTION_ARGS
)
3210 PG_RETURN_INT32(array_cmp(fcinfo
));
3215 * Internal comparison function for arrays.
3217 * Returns -1, 0 or 1
3220 array_cmp(FunctionCallInfo fcinfo
)
3222 ArrayType
*array1
= PG_GETARG_ARRAYTYPE_P(0);
3223 ArrayType
*array2
= PG_GETARG_ARRAYTYPE_P(1);
3224 int ndims1
= ARR_NDIM(array1
);
3225 int ndims2
= ARR_NDIM(array2
);
3226 int *dims1
= ARR_DIMS(array1
);
3227 int *dims2
= ARR_DIMS(array2
);
3228 int nitems1
= ArrayGetNItems(ndims1
, dims1
);
3229 int nitems2
= ArrayGetNItems(ndims2
, dims2
);
3230 Oid element_type
= ARR_ELEMTYPE(array1
);
3232 TypeCacheEntry
*typentry
;
3243 FunctionCallInfoData locfcinfo
;
3245 if (element_type
!= ARR_ELEMTYPE(array2
))
3247 (errcode(ERRCODE_DATATYPE_MISMATCH
),
3248 errmsg("cannot compare arrays of different element types")));
3251 * We arrange to look up the comparison function only once per series of
3252 * calls, assuming the element type doesn't change underneath us. The
3253 * typcache is used so that we have no memory leakage when being used as
3254 * an index support function.
3256 typentry
= (TypeCacheEntry
*) fcinfo
->flinfo
->fn_extra
;
3257 if (typentry
== NULL
||
3258 typentry
->type_id
!= element_type
)
3260 typentry
= lookup_type_cache(element_type
,
3261 TYPECACHE_CMP_PROC_FINFO
);
3262 if (!OidIsValid(typentry
->cmp_proc_finfo
.fn_oid
))
3264 (errcode(ERRCODE_UNDEFINED_FUNCTION
),
3265 errmsg("could not identify a comparison function for type %s",
3266 format_type_be(element_type
))));
3267 fcinfo
->flinfo
->fn_extra
= (void *) typentry
;
3269 typlen
= typentry
->typlen
;
3270 typbyval
= typentry
->typbyval
;
3271 typalign
= typentry
->typalign
;
3274 * apply the operator to each pair of array elements.
3276 InitFunctionCallInfoData(locfcinfo
, &typentry
->cmp_proc_finfo
, 2,
3279 /* Loop over source data */
3280 min_nitems
= Min(nitems1
, nitems2
);
3281 ptr1
= ARR_DATA_PTR(array1
);
3282 ptr2
= ARR_DATA_PTR(array2
);
3283 bitmap1
= ARR_NULLBITMAP(array1
);
3284 bitmap2
= ARR_NULLBITMAP(array2
);
3285 bitmask
= 1; /* use same bitmask for both arrays */
3287 for (i
= 0; i
< min_nitems
; i
++)
3295 /* Get elements, checking for NULL */
3296 if (bitmap1
&& (*bitmap1
& bitmask
) == 0)
3304 elt1
= fetch_att(ptr1
, typbyval
, typlen
);
3305 ptr1
= att_addlength_pointer(ptr1
, typlen
, ptr1
);
3306 ptr1
= (char *) att_align_nominal(ptr1
, typalign
);
3309 if (bitmap2
&& (*bitmap2
& bitmask
) == 0)
3317 elt2
= fetch_att(ptr2
, typbyval
, typlen
);
3318 ptr2
= att_addlength_pointer(ptr2
, typlen
, ptr2
);
3319 ptr2
= (char *) att_align_nominal(ptr2
, typalign
);
3322 /* advance bitmap pointers if any */
3324 if (bitmask
== 0x100)
3334 * We consider two NULLs equal; NULL > not-NULL.
3336 if (isnull1
&& isnull2
)
3340 /* arg1 is greater than arg2 */
3346 /* arg1 is less than arg2 */
3351 /* Compare the pair of elements */
3352 locfcinfo
.arg
[0] = elt1
;
3353 locfcinfo
.arg
[1] = elt2
;
3354 locfcinfo
.argnull
[0] = false;
3355 locfcinfo
.argnull
[1] = false;
3356 locfcinfo
.isnull
= false;
3357 cmpresult
= DatumGetInt32(FunctionCallInvoke(&locfcinfo
));
3360 continue; /* equal */
3364 /* arg1 is less than arg2 */
3370 /* arg1 is greater than arg2 */
3377 * If arrays contain same data (up to end of shorter one), apply
3378 * additional rules to sort by dimensionality. The relative significance
3379 * of the different bits of information is historical; mainly we just care
3380 * that we don't say "equal" for arrays of different dimensionality.
3384 if (nitems1
!= nitems2
)
3385 result
= (nitems1
< nitems2
) ? -1 : 1;
3386 else if (ndims1
!= ndims2
)
3387 result
= (ndims1
< ndims2
) ? -1 : 1;
3390 /* this relies on LB array immediately following DIMS array */
3391 for (i
= 0; i
< ndims1
* 2; i
++)
3393 if (dims1
[i
] != dims2
[i
])
3395 result
= (dims1
[i
] < dims2
[i
]) ? -1 : 1;
3402 /* Avoid leaking memory when handed toasted input. */
3403 PG_FREE_IF_COPY(array1
, 0);
3404 PG_FREE_IF_COPY(array2
, 1);
3410 /*-----------------------------------------------------------------------------
3411 * array overlap/containment comparisons
3412 * These use the same methods of comparing array elements as array_eq.
3413 * We consider only the elements of the arrays, ignoring dimensionality.
3414 *----------------------------------------------------------------------------
3418 * array_contain_compare :
3419 * compares two arrays for overlap/containment
3421 * When matchall is true, return true if all members of array1 are in array2.
3422 * When matchall is false, return true if any members of array1 are in array2.
3425 array_contain_compare(ArrayType
*array1
, ArrayType
*array2
, bool matchall
,
3428 bool result
= matchall
;
3429 Oid element_type
= ARR_ELEMTYPE(array1
);
3430 TypeCacheEntry
*typentry
;
3443 FunctionCallInfoData locfcinfo
;
3445 if (element_type
!= ARR_ELEMTYPE(array2
))
3447 (errcode(ERRCODE_DATATYPE_MISMATCH
),
3448 errmsg("cannot compare arrays of different element types")));
3451 * We arrange to look up the equality function only once per series of
3452 * calls, assuming the element type doesn't change underneath us. The
3453 * typcache is used so that we have no memory leakage when being used as
3454 * an index support function.
3456 typentry
= (TypeCacheEntry
*) *fn_extra
;
3457 if (typentry
== NULL
||
3458 typentry
->type_id
!= element_type
)
3460 typentry
= lookup_type_cache(element_type
,
3461 TYPECACHE_EQ_OPR_FINFO
);
3462 if (!OidIsValid(typentry
->eq_opr_finfo
.fn_oid
))
3464 (errcode(ERRCODE_UNDEFINED_FUNCTION
),
3465 errmsg("could not identify an equality operator for type %s",
3466 format_type_be(element_type
))));
3467 *fn_extra
= (void *) typentry
;
3469 typlen
= typentry
->typlen
;
3470 typbyval
= typentry
->typbyval
;
3471 typalign
= typentry
->typalign
;
3474 * Since we probably will need to scan array2 multiple times, it's
3475 * worthwhile to use deconstruct_array on it. We scan array1 the hard way
3476 * however, since we very likely won't need to look at all of it.
3478 deconstruct_array(array2
, element_type
, typlen
, typbyval
, typalign
,
3479 &values2
, &nulls2
, &nelems2
);
3482 * Apply the comparison operator to each pair of array elements.
3484 InitFunctionCallInfoData(locfcinfo
, &typentry
->eq_opr_finfo
, 2,
3487 /* Loop over source data */
3488 nelems1
= ArrayGetNItems(ARR_NDIM(array1
), ARR_DIMS(array1
));
3489 ptr1
= ARR_DATA_PTR(array1
);
3490 bitmap1
= ARR_NULLBITMAP(array1
);
3493 for (i
= 0; i
< nelems1
; i
++)
3498 /* Get element, checking for NULL */
3499 if (bitmap1
&& (*bitmap1
& bitmask
) == 0)
3507 elt1
= fetch_att(ptr1
, typbyval
, typlen
);
3508 ptr1
= att_addlength_pointer(ptr1
, typlen
, ptr1
);
3509 ptr1
= (char *) att_align_nominal(ptr1
, typalign
);
3512 /* advance bitmap pointer if any */
3514 if (bitmask
== 0x100)
3522 * We assume that the comparison operator is strict, so a NULL can't
3523 * match anything. XXX this diverges from the "NULL=NULL" behavior of
3524 * array_eq, should we act like that?
3536 for (j
= 0; j
< nelems2
; j
++)
3538 Datum elt2
= values2
[j
];
3539 bool isnull2
= nulls2
[j
];
3543 continue; /* can't match */
3546 * Apply the operator to the element pair
3548 locfcinfo
.arg
[0] = elt1
;
3549 locfcinfo
.arg
[1] = elt2
;
3550 locfcinfo
.argnull
[0] = false;
3551 locfcinfo
.argnull
[1] = false;
3552 locfcinfo
.isnull
= false;
3553 oprresult
= DatumGetBool(FunctionCallInvoke(&locfcinfo
));
3560 /* found a match for elt1 */
3569 /* no match for elt1 */
3585 arrayoverlap(PG_FUNCTION_ARGS
)
3587 ArrayType
*array1
= PG_GETARG_ARRAYTYPE_P(0);
3588 ArrayType
*array2
= PG_GETARG_ARRAYTYPE_P(1);
3591 result
= array_contain_compare(array1
, array2
, false,
3592 &fcinfo
->flinfo
->fn_extra
);
3594 /* Avoid leaking memory when handed toasted input. */
3595 PG_FREE_IF_COPY(array1
, 0);
3596 PG_FREE_IF_COPY(array2
, 1);
3598 PG_RETURN_BOOL(result
);
3602 arraycontains(PG_FUNCTION_ARGS
)
3604 ArrayType
*array1
= PG_GETARG_ARRAYTYPE_P(0);
3605 ArrayType
*array2
= PG_GETARG_ARRAYTYPE_P(1);
3608 result
= array_contain_compare(array2
, array1
, true,
3609 &fcinfo
->flinfo
->fn_extra
);
3611 /* Avoid leaking memory when handed toasted input. */
3612 PG_FREE_IF_COPY(array1
, 0);
3613 PG_FREE_IF_COPY(array2
, 1);
3615 PG_RETURN_BOOL(result
);
3619 arraycontained(PG_FUNCTION_ARGS
)
3621 ArrayType
*array1
= PG_GETARG_ARRAYTYPE_P(0);
3622 ArrayType
*array2
= PG_GETARG_ARRAYTYPE_P(1);
3625 result
= array_contain_compare(array1
, array2
, true,
3626 &fcinfo
->flinfo
->fn_extra
);
3628 /* Avoid leaking memory when handed toasted input. */
3629 PG_FREE_IF_COPY(array1
, 0);
3630 PG_FREE_IF_COPY(array2
, 1);
3632 PG_RETURN_BOOL(result
);
3636 /***************************************************************************/
3637 /******************| Support Routines |*****************/
3638 /***************************************************************************/
3641 * Check whether a specific array element is NULL
3643 * nullbitmap: pointer to array's null bitmap (NULL if none)
3644 * offset: 0-based linear element number of array element
3647 array_get_isnull(const bits8
*nullbitmap
, int offset
)
3649 if (nullbitmap
== NULL
)
3650 return false; /* assume not null */
3651 if (nullbitmap
[offset
/ 8] & (1 << (offset
% 8)))
3652 return false; /* not null */
3657 * Set a specific array element's null-bitmap entry
3659 * nullbitmap: pointer to array's null bitmap (mustn't be NULL)
3660 * offset: 0-based linear element number of array element
3661 * isNull: null status to set
3664 array_set_isnull(bits8
*nullbitmap
, int offset
, bool isNull
)
3668 nullbitmap
+= offset
/ 8;
3669 bitmask
= 1 << (offset
% 8);
3671 *nullbitmap
&= ~bitmask
;
3673 *nullbitmap
|= bitmask
;
3677 * Fetch array element at pointer, converted correctly to a Datum
3679 * Caller must have handled case of NULL element
3682 ArrayCast(char *value
, bool byval
, int len
)
3684 return fetch_att(value
, byval
, len
);
3688 * Copy datum to *dest and return total space used (including align padding)
3690 * Caller must have handled case of NULL element
3693 ArrayCastAndSet(Datum src
,
3704 store_att_byval(dest
, src
, typlen
);
3706 memmove(dest
, DatumGetPointer(src
), typlen
);
3707 inc
= att_align_nominal(typlen
, typalign
);
3712 inc
= att_addlength_datum(0, typlen
, src
);
3713 memmove(dest
, DatumGetPointer(src
), inc
);
3714 inc
= att_align_nominal(inc
, typalign
);
3721 * Advance ptr over nitems array elements
3723 * ptr: starting location in array
3724 * offset: 0-based linear element number of first element (the one at *ptr)
3725 * nullbitmap: start of array's null bitmap, or NULL if none
3726 * nitems: number of array elements to advance over (>= 0)
3727 * typlen, typbyval, typalign: storage parameters of array element datatype
3729 * It is caller's responsibility to ensure that nitems is within range
3732 array_seek(char *ptr
, int offset
, bits8
*nullbitmap
, int nitems
,
3733 int typlen
, bool typbyval
, char typalign
)
3738 /* easy if fixed-size elements and no NULLs */
3739 if (typlen
> 0 && !nullbitmap
)
3740 return ptr
+ nitems
* ((Size
) att_align_nominal(typlen
, typalign
));
3742 /* seems worth having separate loops for NULL and no-NULLs cases */
3745 nullbitmap
+= offset
/ 8;
3746 bitmask
= 1 << (offset
% 8);
3748 for (i
= 0; i
< nitems
; i
++)
3750 if (*nullbitmap
& bitmask
)
3752 ptr
= att_addlength_pointer(ptr
, typlen
, ptr
);
3753 ptr
= (char *) att_align_nominal(ptr
, typalign
);
3756 if (bitmask
== 0x100)
3765 for (i
= 0; i
< nitems
; i
++)
3767 ptr
= att_addlength_pointer(ptr
, typlen
, ptr
);
3768 ptr
= (char *) att_align_nominal(ptr
, typalign
);
3775 * Compute total size of the nitems array elements starting at *ptr
3777 * Parameters same as for array_seek
3780 array_nelems_size(char *ptr
, int offset
, bits8
*nullbitmap
, int nitems
,
3781 int typlen
, bool typbyval
, char typalign
)
3783 return array_seek(ptr
, offset
, nullbitmap
, nitems
,
3784 typlen
, typbyval
, typalign
) - ptr
;
3788 * Copy nitems array elements from srcptr to destptr
3790 * destptr: starting destination location (must be enough room!)
3791 * nitems: number of array elements to copy (>= 0)
3792 * srcptr: starting location in source array
3793 * offset: 0-based linear element number of first element (the one at *srcptr)
3794 * nullbitmap: start of source array's null bitmap, or NULL if none
3795 * typlen, typbyval, typalign: storage parameters of array element datatype
3797 * Returns number of bytes copied
3799 * NB: this does not take care of setting up the destination's null bitmap!
3802 array_copy(char *destptr
, int nitems
,
3803 char *srcptr
, int offset
, bits8
*nullbitmap
,
3804 int typlen
, bool typbyval
, char typalign
)
3808 numbytes
= array_nelems_size(srcptr
, offset
, nullbitmap
, nitems
,
3809 typlen
, typbyval
, typalign
);
3810 memcpy(destptr
, srcptr
, numbytes
);
3815 * Copy nitems null-bitmap bits from source to destination
3817 * destbitmap: start of destination array's null bitmap (mustn't be NULL)
3818 * destoffset: 0-based linear element number of first dest element
3819 * srcbitmap: start of source array's null bitmap, or NULL if none
3820 * srcoffset: 0-based linear element number of first source element
3821 * nitems: number of bits to copy (>= 0)
3823 * If srcbitmap is NULL then we assume the source is all-non-NULL and
3824 * fill 1's into the destination bitmap. Note that only the specified
3825 * bits in the destination map are changed, not any before or after.
3827 * Note: this could certainly be optimized using standard bitblt methods.
3828 * However, it's not clear that the typical Postgres array has enough elements
3829 * to make it worth worrying too much. For the moment, KISS.
3832 array_bitmap_copy(bits8
*destbitmap
, int destoffset
,
3833 const bits8
*srcbitmap
, int srcoffset
,
3843 return; /* don't risk fetch off end of memory */
3844 destbitmap
+= destoffset
/ 8;
3845 destbitmask
= 1 << (destoffset
% 8);
3846 destbitval
= *destbitmap
;
3849 srcbitmap
+= srcoffset
/ 8;
3850 srcbitmask
= 1 << (srcoffset
% 8);
3851 srcbitval
= *srcbitmap
;
3852 while (nitems
-- > 0)
3854 if (srcbitval
& srcbitmask
)
3855 destbitval
|= destbitmask
;
3857 destbitval
&= ~destbitmask
;
3859 if (destbitmask
== 0x100)
3861 *destbitmap
++ = destbitval
;
3864 destbitval
= *destbitmap
;
3867 if (srcbitmask
== 0x100)
3872 srcbitval
= *srcbitmap
;
3875 if (destbitmask
!= 1)
3876 *destbitmap
= destbitval
;
3880 while (nitems
-- > 0)
3882 destbitval
|= destbitmask
;
3884 if (destbitmask
== 0x100)
3886 *destbitmap
++ = destbitval
;
3889 destbitval
= *destbitmap
;
3892 if (destbitmask
!= 1)
3893 *destbitmap
= destbitval
;
3898 * Compute space needed for a slice of an array
3900 * We assume the caller has verified that the slice coordinates are valid.
3903 array_slice_size(char *arraydataptr
, bits8
*arraynullsptr
,
3904 int ndim
, int *dim
, int *lb
,
3906 int typlen
, bool typbyval
, char typalign
)
3919 mda_get_range(ndim
, span
, st
, endp
);
3921 /* Pretty easy for fixed element length without nulls ... */
3922 if (typlen
> 0 && !arraynullsptr
)
3923 return ArrayGetNItems(ndim
, span
) * att_align_nominal(typlen
, typalign
);
3925 /* Else gotta do it the hard way */
3926 src_offset
= ArrayGetOffset(ndim
, dim
, lb
, st
);
3927 ptr
= array_seek(arraydataptr
, 0, arraynullsptr
, src_offset
,
3928 typlen
, typbyval
, typalign
);
3929 mda_get_prod(ndim
, dim
, prod
);
3930 mda_get_offset_values(ndim
, dist
, prod
, span
);
3931 for (i
= 0; i
< ndim
; i
++)
3938 ptr
= array_seek(ptr
, src_offset
, arraynullsptr
, dist
[j
],
3939 typlen
, typbyval
, typalign
);
3940 src_offset
+= dist
[j
];
3942 if (!array_get_isnull(arraynullsptr
, src_offset
))
3944 inc
= att_addlength_pointer(0, typlen
, ptr
);
3945 inc
= att_align_nominal(inc
, typalign
);
3950 } while ((j
= mda_next_tuple(ndim
, indx
, span
)) != -1);
3955 * Extract a slice of an array into consecutive elements in the destination
3958 * We assume the caller has verified that the slice coordinates are valid,
3959 * allocated enough storage for the result, and initialized the header
3963 array_extract_slice(ArrayType
*newarray
,
3968 bits8
*arraynullsptr
,
3975 char *destdataptr
= ARR_DATA_PTR(newarray
);
3976 bits8
*destnullsptr
= ARR_NULLBITMAP(newarray
);
3988 src_offset
= ArrayGetOffset(ndim
, dim
, lb
, st
);
3989 srcdataptr
= array_seek(arraydataptr
, 0, arraynullsptr
, src_offset
,
3990 typlen
, typbyval
, typalign
);
3991 mda_get_prod(ndim
, dim
, prod
);
3992 mda_get_range(ndim
, span
, st
, endp
);
3993 mda_get_offset_values(ndim
, dist
, prod
, span
);
3994 for (i
= 0; i
< ndim
; i
++)
4002 /* skip unwanted elements */
4003 srcdataptr
= array_seek(srcdataptr
, src_offset
, arraynullsptr
,
4005 typlen
, typbyval
, typalign
);
4006 src_offset
+= dist
[j
];
4008 inc
= array_copy(destdataptr
, 1,
4009 srcdataptr
, src_offset
, arraynullsptr
,
4010 typlen
, typbyval
, typalign
);
4012 array_bitmap_copy(destnullsptr
, dest_offset
,
4013 arraynullsptr
, src_offset
,
4019 } while ((j
= mda_next_tuple(ndim
, indx
, span
)) != -1);
4023 * Insert a slice into an array.
4025 * ndim/dim[]/lb[] are dimensions of the original array. A new array with
4026 * those same dimensions is to be constructed. destArray must already
4027 * have been allocated and its header initialized.
4029 * st[]/endp[] identify the slice to be replaced. Elements within the slice
4030 * volume are taken from consecutive elements of the srcArray; elements
4031 * outside it are copied from origArray.
4033 * We assume the caller has verified that the slice coordinates are valid.
4036 array_insert_slice(ArrayType
*destArray
,
4037 ArrayType
*origArray
,
4038 ArrayType
*srcArray
,
4048 char *destPtr
= ARR_DATA_PTR(destArray
);
4049 char *origPtr
= ARR_DATA_PTR(origArray
);
4050 char *srcPtr
= ARR_DATA_PTR(srcArray
);
4051 bits8
*destBitmap
= ARR_NULLBITMAP(destArray
);
4052 bits8
*origBitmap
= ARR_NULLBITMAP(origArray
);
4053 bits8
*srcBitmap
= ARR_NULLBITMAP(srcArray
);
4054 int orignitems
= ArrayGetNItems(ARR_NDIM(origArray
),
4055 ARR_DIMS(origArray
));
4067 dest_offset
= ArrayGetOffset(ndim
, dim
, lb
, st
);
4068 /* copy items before the slice start */
4069 inc
= array_copy(destPtr
, dest_offset
,
4070 origPtr
, 0, origBitmap
,
4071 typlen
, typbyval
, typalign
);
4075 array_bitmap_copy(destBitmap
, 0, origBitmap
, 0, dest_offset
);
4076 orig_offset
= dest_offset
;
4077 mda_get_prod(ndim
, dim
, prod
);
4078 mda_get_range(ndim
, span
, st
, endp
);
4079 mda_get_offset_values(ndim
, dist
, prod
, span
);
4080 for (i
= 0; i
< ndim
; i
++)
4086 /* Copy/advance over elements between here and next part of slice */
4089 inc
= array_copy(destPtr
, dist
[j
],
4090 origPtr
, orig_offset
, origBitmap
,
4091 typlen
, typbyval
, typalign
);
4095 array_bitmap_copy(destBitmap
, dest_offset
,
4096 origBitmap
, orig_offset
,
4098 dest_offset
+= dist
[j
];
4099 orig_offset
+= dist
[j
];
4101 /* Copy new element at this slice position */
4102 inc
= array_copy(destPtr
, 1,
4103 srcPtr
, src_offset
, srcBitmap
,
4104 typlen
, typbyval
, typalign
);
4106 array_bitmap_copy(destBitmap
, dest_offset
,
4107 srcBitmap
, src_offset
,
4113 /* Advance over old element at this slice position */
4114 origPtr
= array_seek(origPtr
, orig_offset
, origBitmap
, 1,
4115 typlen
, typbyval
, typalign
);
4117 } while ((j
= mda_next_tuple(ndim
, indx
, span
)) != -1);
4119 /* don't miss any data at the end */
4120 array_copy(destPtr
, orignitems
- orig_offset
,
4121 origPtr
, orig_offset
, origBitmap
,
4122 typlen
, typbyval
, typalign
);
4124 array_bitmap_copy(destBitmap
, dest_offset
,
4125 origBitmap
, orig_offset
,
4126 orignitems
- orig_offset
);
4130 * accumArrayResult - accumulate one (more) Datum for an array result
4132 * astate is working state (NULL on first call)
4133 * rcontext is where to keep working state
4136 accumArrayResult(ArrayBuildState
*astate
,
4137 Datum dvalue
, bool disnull
,
4139 MemoryContext rcontext
)
4141 MemoryContext arr_context
,
4146 /* First time through --- initialize */
4148 /* Make a temporary context to hold all the junk */
4149 arr_context
= AllocSetContextCreate(rcontext
,
4151 ALLOCSET_DEFAULT_MINSIZE
,
4152 ALLOCSET_DEFAULT_INITSIZE
,
4153 ALLOCSET_DEFAULT_MAXSIZE
);
4154 oldcontext
= MemoryContextSwitchTo(arr_context
);
4155 astate
= (ArrayBuildState
*) palloc(sizeof(ArrayBuildState
));
4156 astate
->mcontext
= arr_context
;
4157 astate
->alen
= 64; /* arbitrary starting array size */
4158 astate
->dvalues
= (Datum
*) palloc(astate
->alen
* sizeof(Datum
));
4159 astate
->dnulls
= (bool *) palloc(astate
->alen
* sizeof(bool));
4161 astate
->element_type
= element_type
;
4162 get_typlenbyvalalign(element_type
,
4169 oldcontext
= MemoryContextSwitchTo(astate
->mcontext
);
4170 Assert(astate
->element_type
== element_type
);
4171 /* enlarge dvalues[]/dnulls[] if needed */
4172 if (astate
->nelems
>= astate
->alen
)
4175 astate
->dvalues
= (Datum
*)
4176 repalloc(astate
->dvalues
, astate
->alen
* sizeof(Datum
));
4177 astate
->dnulls
= (bool *)
4178 repalloc(astate
->dnulls
, astate
->alen
* sizeof(bool));
4182 /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
4183 if (!disnull
&& !astate
->typbyval
)
4184 dvalue
= datumCopy(dvalue
, astate
->typbyval
, astate
->typlen
);
4186 astate
->dvalues
[astate
->nelems
] = dvalue
;
4187 astate
->dnulls
[astate
->nelems
] = disnull
;
4190 MemoryContextSwitchTo(oldcontext
);
4196 * makeArrayResult - produce 1-D final result of accumArrayResult
4198 * astate is working state (not NULL)
4199 * rcontext is where to construct result
4202 makeArrayResult(ArrayBuildState
*astate
,
4203 MemoryContext rcontext
)
4208 dims
[0] = astate
->nelems
;
4211 return makeMdArrayResult(astate
, 1, dims
, lbs
, rcontext
, true);
4215 * makeMdArrayResult - produce multi-D final result of accumArrayResult
4217 * beware: no check that specified dimensions match the number of values
4220 * astate is working state (not NULL)
4221 * rcontext is where to construct result
4222 * release is true if okay to release working state
4225 makeMdArrayResult(ArrayBuildState
*astate
,
4229 MemoryContext rcontext
,
4233 MemoryContext oldcontext
;
4235 /* Build the final array result in rcontext */
4236 oldcontext
= MemoryContextSwitchTo(rcontext
);
4238 result
= construct_md_array(astate
->dvalues
,
4243 astate
->element_type
,
4248 MemoryContextSwitchTo(oldcontext
);
4250 /* Clean up all the junk */
4252 MemoryContextDelete(astate
->mcontext
);
4254 return PointerGetDatum(result
);
4258 array_larger(PG_FUNCTION_ARGS
)
4264 v1
= PG_GETARG_ARRAYTYPE_P(0);
4265 v2
= PG_GETARG_ARRAYTYPE_P(1);
4267 result
= ((array_cmp(fcinfo
) > 0) ? v1
: v2
);
4269 PG_RETURN_ARRAYTYPE_P(result
);
4273 array_smaller(PG_FUNCTION_ARGS
)
4279 v1
= PG_GETARG_ARRAYTYPE_P(0);
4280 v2
= PG_GETARG_ARRAYTYPE_P(1);
4282 result
= ((array_cmp(fcinfo
) < 0) ? v1
: v2
);
4284 PG_RETURN_ARRAYTYPE_P(result
);
4288 typedef struct generate_subscripts_fctx
4293 } generate_subscripts_fctx
;
4296 * generate_subscripts(array anyarray, dim int [, reverse bool])
4297 * Returns all subscripts of the array for any dimension
4300 generate_subscripts(PG_FUNCTION_ARGS
)
4302 FuncCallContext
*funcctx
;
4303 MemoryContext oldcontext
;
4304 generate_subscripts_fctx
*fctx
;
4306 /* stuff done only on the first call of the function */
4307 if (SRF_IS_FIRSTCALL())
4309 ArrayType
*v
= PG_GETARG_ARRAYTYPE_P(0);
4310 int reqdim
= PG_GETARG_INT32(1);
4314 /* create a function context for cross-call persistence */
4315 funcctx
= SRF_FIRSTCALL_INIT();
4317 /* Sanity check: does it look like an array at all? */
4318 if (ARR_NDIM(v
) <= 0 || ARR_NDIM(v
) > MAXDIM
)
4319 SRF_RETURN_DONE(funcctx
);
4321 /* Sanity check: was the requested dim valid */
4322 if (reqdim
<= 0 || reqdim
> ARR_NDIM(v
))
4323 SRF_RETURN_DONE(funcctx
);
4326 * switch to memory context appropriate for multiple function calls
4328 oldcontext
= MemoryContextSwitchTo(funcctx
->multi_call_memory_ctx
);
4329 fctx
= (generate_subscripts_fctx
*) palloc(sizeof(generate_subscripts_fctx
));
4334 fctx
->lower
= lb
[reqdim
- 1];
4335 fctx
->upper
= dimv
[reqdim
- 1] + lb
[reqdim
- 1] - 1;
4336 fctx
->reverse
= (PG_NARGS() < 3) ? false : PG_GETARG_BOOL(2);
4338 funcctx
->user_fctx
= fctx
;
4340 MemoryContextSwitchTo(oldcontext
);
4343 funcctx
= SRF_PERCALL_SETUP();
4345 fctx
= funcctx
->user_fctx
;
4347 if (fctx
->lower
<= fctx
->upper
)
4350 SRF_RETURN_NEXT(funcctx
, Int32GetDatum(fctx
->lower
++));
4352 SRF_RETURN_NEXT(funcctx
, Int32GetDatum(fctx
->upper
--));
4355 /* done when there are no more elements left */
4356 SRF_RETURN_DONE(funcctx
);
4360 * generate_subscripts_nodir
4361 * Implements the 2-argument version of generate_subscripts
4364 generate_subscripts_nodir(PG_FUNCTION_ARGS
)
4366 /* just call the other one -- it can handle both cases */
4367 return generate_subscripts(fcinfo
);
4371 * array_fill_with_lower_bounds
4372 * Create and fill array with defined lower bounds.
4375 array_fill_with_lower_bounds(PG_FUNCTION_ARGS
)
4384 if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
4386 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED
),
4387 errmsg("dimension array or low bound array cannot be NULL")));
4389 dims
= PG_GETARG_ARRAYTYPE_P(1);
4390 lbs
= PG_GETARG_ARRAYTYPE_P(2);
4392 if (!PG_ARGISNULL(0))
4394 value
= PG_GETARG_DATUM(0);
4403 elmtype
= get_fn_expr_argtype(fcinfo
->flinfo
, 0);
4404 if (!OidIsValid(elmtype
))
4405 elog(ERROR
, "could not determine data type of input");
4407 result
= array_fill_internal(dims
, lbs
, value
, isnull
, elmtype
, fcinfo
);
4408 PG_RETURN_ARRAYTYPE_P(result
);
4413 * Create and fill array with default lower bounds.
4416 array_fill(PG_FUNCTION_ARGS
)
4424 if (PG_ARGISNULL(1))
4426 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED
),
4427 errmsg("dimension array or low bound array cannot be NULL")));
4429 dims
= PG_GETARG_ARRAYTYPE_P(1);
4431 if (!PG_ARGISNULL(0))
4433 value
= PG_GETARG_DATUM(0);
4442 elmtype
= get_fn_expr_argtype(fcinfo
->flinfo
, 0);
4443 if (!OidIsValid(elmtype
))
4444 elog(ERROR
, "could not determine data type of input");
4446 result
= array_fill_internal(dims
, NULL
, value
, isnull
, elmtype
, fcinfo
);
4447 PG_RETURN_ARRAYTYPE_P(result
);
4451 create_array_envelope(int ndims
, int *dimv
, int *lbsv
, int nbytes
,
4452 Oid elmtype
, int dataoffset
)
4456 result
= (ArrayType
*) palloc0(nbytes
);
4457 SET_VARSIZE(result
, nbytes
);
4458 result
->ndim
= ndims
;
4459 result
->dataoffset
= dataoffset
;
4460 result
->elemtype
= elmtype
;
4461 memcpy(ARR_DIMS(result
), dimv
, ndims
* sizeof(int));
4462 memcpy(ARR_LBOUND(result
), lbsv
, ndims
* sizeof(int));
4468 array_fill_internal(ArrayType
*dims
, ArrayType
*lbs
,
4469 Datum value
, bool isnull
, Oid elmtype
,
4470 FunctionCallInfo fcinfo
)
4481 ArrayMetaState
*my_extra
;
4486 if (ARR_NDIM(dims
) != 1)
4488 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
4489 errmsg("wrong number of array subscripts"),
4490 errdetail("Dimension array must be one dimensional.")));
4492 if (ARR_LBOUND(dims
)[0] != 1)
4494 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
4495 errmsg("wrong range of array_subscripts"),
4496 errdetail("Lower bound of dimension array must be one.")));
4498 if (ARR_HASNULL(dims
))
4500 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED
),
4501 errmsg("dimension values cannot be null")));
4503 dimv
= (int *) ARR_DATA_PTR(dims
);
4504 ndims
= ARR_DIMS(dims
)[0];
4506 if (ndims
< 0) /* we do allow zero-dimension arrays */
4508 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
4509 errmsg("invalid number of dimensions: %d", ndims
)));
4512 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
4513 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
4518 if (ARR_NDIM(lbs
) != 1)
4520 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
4521 errmsg("wrong number of array subscripts"),
4522 errdetail("Dimension array must be one dimensional.")));
4524 if (ARR_LBOUND(lbs
)[0] != 1)
4526 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
4527 errmsg("wrong range of array_subscripts"),
4528 errdetail("Lower bound of dimension array must be one.")));
4530 if (ARR_HASNULL(lbs
))
4532 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED
),
4533 errmsg("dimension values cannot be null")));
4535 if (ARR_DIMS(lbs
)[0] != ndims
)
4537 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
4538 errmsg("wrong number of array_subscripts"),
4539 errdetail("Low bound array has different size than dimensions array.")));
4541 lbsv
= (int *) ARR_DATA_PTR(lbs
);
4547 for (i
= 0; i
< MAXDIM
; i
++)
4553 /* fast track for empty array */
4555 return construct_empty_array(elmtype
);
4557 nitems
= ArrayGetNItems(ndims
, dimv
);
4560 * We arrange to look up info about element type only once per series of
4561 * calls, assuming the element type doesn't change underneath us.
4563 my_extra
= (ArrayMetaState
*) fcinfo
->flinfo
->fn_extra
;
4564 if (my_extra
== NULL
)
4566 fcinfo
->flinfo
->fn_extra
= MemoryContextAlloc(fcinfo
->flinfo
->fn_mcxt
,
4567 sizeof(ArrayMetaState
));
4568 my_extra
= (ArrayMetaState
*) fcinfo
->flinfo
->fn_extra
;
4569 my_extra
->element_type
= InvalidOid
;
4572 if (my_extra
->element_type
!= elmtype
)
4574 /* Get info about element type */
4575 get_typlenbyvalalign(elmtype
,
4577 &my_extra
->typbyval
,
4578 &my_extra
->typalign
);
4579 my_extra
->element_type
= elmtype
;
4582 elmlen
= my_extra
->typlen
;
4583 elmbyval
= my_extra
->typbyval
;
4584 elmalign
= my_extra
->typalign
;
4586 /* compute required space */
4594 /* make sure data is not toasted */
4596 value
= PointerGetDatum(PG_DETOAST_DATUM(value
));
4598 nbytes
= att_addlength_datum(0, elmlen
, value
);
4599 nbytes
= att_align_nominal(nbytes
, elmalign
);
4602 totbytes
= nbytes
* nitems
;
4604 /* check for overflow of multiplication or total request */
4605 if (totbytes
/ nbytes
!= nitems
||
4606 !AllocSizeIsValid(totbytes
))
4608 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
4609 errmsg("array size exceeds the maximum allowed (%d)",
4610 (int) MaxAllocSize
)));
4613 * This addition can't overflow, but it might cause us to go past
4614 * MaxAllocSize. We leave it to palloc to complain in that case.
4616 totbytes
+= ARR_OVERHEAD_NONULLS(ndims
);
4618 result
= create_array_envelope(ndims
, dimv
, lbsv
, totbytes
,
4621 p
= ARR_DATA_PTR(result
);
4622 for (i
= 0; i
< nitems
; i
++)
4623 p
+= ArrayCastAndSet(value
, elmlen
, elmbyval
, elmalign
, p
);
4630 dataoffset
= ARR_OVERHEAD_WITHNULLS(ndims
, nitems
);
4631 nbytes
= dataoffset
;
4633 result
= create_array_envelope(ndims
, dimv
, lbsv
, nbytes
,
4634 elmtype
, dataoffset
);
4636 /* create_array_envelope already zeroed the bitmap, so we're done */
4647 array_unnest(PG_FUNCTION_ARGS
)
4654 char *elemdataptr
; /* this moves with nextelem */
4655 bits8
*arraynullsptr
; /* this does not */
4659 } array_unnest_fctx
;
4661 FuncCallContext
*funcctx
;
4662 array_unnest_fctx
*fctx
;
4663 MemoryContext oldcontext
;
4665 /* stuff done only on the first call of the function */
4666 if (SRF_IS_FIRSTCALL())
4670 /* create a function context for cross-call persistence */
4671 funcctx
= SRF_FIRSTCALL_INIT();
4674 * switch to memory context appropriate for multiple function calls
4676 oldcontext
= MemoryContextSwitchTo(funcctx
->multi_call_memory_ctx
);
4679 * Get the array value and detoast if needed. We can't do this
4680 * earlier because if we have to detoast, we want the detoasted
4681 * copy to be in multi_call_memory_ctx, so it will go away when
4682 * we're done and not before. (If no detoast happens, we assume
4683 * the originally passed array will stick around till then.)
4685 arr
= PG_GETARG_ARRAYTYPE_P(0);
4687 /* allocate memory for user context */
4688 fctx
= (array_unnest_fctx
*) palloc(sizeof(array_unnest_fctx
));
4690 /* initialize state */
4693 fctx
->numelems
= ArrayGetNItems(ARR_NDIM(arr
), ARR_DIMS(arr
));
4695 fctx
->elemdataptr
= ARR_DATA_PTR(arr
);
4696 fctx
->arraynullsptr
= ARR_NULLBITMAP(arr
);
4698 get_typlenbyvalalign(ARR_ELEMTYPE(arr
),
4703 funcctx
->user_fctx
= fctx
;
4704 MemoryContextSwitchTo(oldcontext
);
4707 /* stuff done on every call of the function */
4708 funcctx
= SRF_PERCALL_SETUP();
4709 fctx
= funcctx
->user_fctx
;
4711 if (fctx
->nextelem
< fctx
->numelems
)
4713 int offset
= fctx
->nextelem
++;
4717 * Check for NULL array element
4719 if (array_get_isnull(fctx
->arraynullsptr
, offset
))
4721 fcinfo
->isnull
= true;
4723 /* elemdataptr does not move */
4728 * OK, get the element
4730 char *ptr
= fctx
->elemdataptr
;
4732 fcinfo
->isnull
= false;
4733 elem
= ArrayCast(ptr
, fctx
->elmbyval
, fctx
->elmlen
);
4736 * Advance elemdataptr over it
4738 ptr
= att_addlength_pointer(ptr
, fctx
->elmlen
, ptr
);
4739 ptr
= (char *) att_align_nominal(ptr
, fctx
->elmalign
);
4740 fctx
->elemdataptr
= ptr
;
4743 SRF_RETURN_NEXT(funcctx
, elem
);
4747 /* do when there is no more left */
4748 SRF_RETURN_DONE(funcctx
);