1 /*-------------------------------------------------------------------------
4 * Support functions for arrays.
6 * Portions Copyright (c) 1996-2008, 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 dimensions of the array pointed to by "v", as a "text"
1538 array_dims(PG_FUNCTION_ARGS
)
1540 ArrayType
*v
= PG_GETARG_ARRAYTYPE_P(0);
1547 * 33 since we assume 15 digits per number + ':' +'[]'
1549 * +1 for trailing null
1551 char buf
[MAXDIM
* 33 + 1];
1553 /* Sanity check: does it look like an array at all? */
1554 if (ARR_NDIM(v
) <= 0 || ARR_NDIM(v
) > MAXDIM
)
1561 for (i
= 0; i
< ARR_NDIM(v
); i
++)
1563 sprintf(p
, "[%d:%d]", lb
[i
], dimv
[i
] + lb
[i
] - 1);
1567 PG_RETURN_TEXT_P(cstring_to_text(buf
));
1572 * returns the lower dimension, of the DIM requested, for
1573 * the array pointed to by "v", as an int4
1576 array_lower(PG_FUNCTION_ARGS
)
1578 ArrayType
*v
= PG_GETARG_ARRAYTYPE_P(0);
1579 int reqdim
= PG_GETARG_INT32(1);
1583 /* Sanity check: does it look like an array at all? */
1584 if (ARR_NDIM(v
) <= 0 || ARR_NDIM(v
) > MAXDIM
)
1587 /* Sanity check: was the requested dim valid */
1588 if (reqdim
<= 0 || reqdim
> ARR_NDIM(v
))
1592 result
= lb
[reqdim
- 1];
1594 PG_RETURN_INT32(result
);
1599 * returns the upper dimension, of the DIM requested, for
1600 * the array pointed to by "v", as an int4
1603 array_upper(PG_FUNCTION_ARGS
)
1605 ArrayType
*v
= PG_GETARG_ARRAYTYPE_P(0);
1606 int reqdim
= PG_GETARG_INT32(1);
1611 /* Sanity check: does it look like an array at all? */
1612 if (ARR_NDIM(v
) <= 0 || ARR_NDIM(v
) > MAXDIM
)
1615 /* Sanity check: was the requested dim valid */
1616 if (reqdim
<= 0 || reqdim
> ARR_NDIM(v
))
1622 result
= dimv
[reqdim
- 1] + lb
[reqdim
- 1] - 1;
1624 PG_RETURN_INT32(result
);
1629 * This routine takes an array pointer and a subscript array and returns
1630 * the referenced item as a Datum. Note that for a pass-by-reference
1631 * datatype, the returned Datum is a pointer into the array object.
1633 * This handles both ordinary varlena arrays and fixed-length arrays.
1636 * array: the array object (mustn't be NULL)
1637 * nSubscripts: number of subscripts supplied
1638 * indx[]: the subscript values
1639 * arraytyplen: pg_type.typlen for the array type
1640 * elmlen: pg_type.typlen for the array's element type
1641 * elmbyval: pg_type.typbyval for the array's element type
1642 * elmalign: pg_type.typalign for the array's element type
1645 * The return value is the element Datum.
1646 * *isNull is set to indicate whether the element is NULL.
1649 array_ref(ArrayType
*array
,
1667 bits8
*arraynullsptr
;
1669 if (arraytyplen
> 0)
1672 * fixed-length arrays -- these are assumed to be 1-d, 0-based
1675 fixedDim
[0] = arraytyplen
/ elmlen
;
1679 arraydataptr
= (char *) array
;
1680 arraynullsptr
= NULL
;
1684 /* detoast input array if necessary */
1685 array
= DatumGetArrayTypeP(PointerGetDatum(array
));
1687 ndim
= ARR_NDIM(array
);
1688 dim
= ARR_DIMS(array
);
1689 lb
= ARR_LBOUND(array
);
1690 arraydataptr
= ARR_DATA_PTR(array
);
1691 arraynullsptr
= ARR_NULLBITMAP(array
);
1695 * Return NULL for invalid subscript
1697 if (ndim
!= nSubscripts
|| ndim
<= 0 || ndim
> MAXDIM
)
1702 for (i
= 0; i
< ndim
; i
++)
1704 if (indx
[i
] < lb
[i
] || indx
[i
] >= (dim
[i
] + lb
[i
]))
1712 * Calculate the element number
1714 offset
= ArrayGetOffset(nSubscripts
, dim
, lb
, indx
);
1717 * Check for NULL array element
1719 if (array_get_isnull(arraynullsptr
, offset
))
1726 * OK, get the element
1729 retptr
= array_seek(arraydataptr
, 0, arraynullsptr
, offset
,
1730 elmlen
, elmbyval
, elmalign
);
1731 return ArrayCast(retptr
, elmbyval
, elmlen
);
1736 * This routine takes an array and a range of indices (upperIndex and
1737 * lowerIndx), creates a new array structure for the referred elements
1738 * and returns a pointer to it.
1740 * This handles both ordinary varlena arrays and fixed-length arrays.
1743 * array: the array object (mustn't be NULL)
1744 * nSubscripts: number of subscripts supplied (must be same for upper/lower)
1745 * upperIndx[]: the upper subscript values
1746 * lowerIndx[]: the lower subscript values
1747 * arraytyplen: pg_type.typlen for the array type
1748 * elmlen: pg_type.typlen for the array's element type
1749 * elmbyval: pg_type.typbyval for the array's element type
1750 * elmalign: pg_type.typalign for the array's element type
1753 * The return value is the new array Datum (it's never NULL)
1755 * NOTE: we assume it is OK to scribble on the provided subscript arrays
1756 * lowerIndx[] and upperIndx[]. These are generally just temporaries.
1759 array_get_slice(ArrayType
*array
,
1768 ArrayType
*newarray
;
1778 bits8
*arraynullsptr
;
1783 if (arraytyplen
> 0)
1786 * fixed-length arrays -- currently, cannot slice these because parser
1787 * labels output as being of the fixed-length array type! Code below
1788 * shows how we could support it if the parser were changed to label
1789 * output as a suitable varlena array type.
1792 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
1793 errmsg("slices of fixed-length arrays not implemented")));
1796 * fixed-length arrays -- these are assumed to be 1-d, 0-based
1798 * XXX where would we get the correct ELEMTYPE from?
1801 fixedDim
[0] = arraytyplen
/ elmlen
;
1805 elemtype
= InvalidOid
; /* XXX */
1806 arraydataptr
= (char *) array
;
1807 arraynullsptr
= NULL
;
1811 /* detoast input array if necessary */
1812 array
= DatumGetArrayTypeP(PointerGetDatum(array
));
1814 ndim
= ARR_NDIM(array
);
1815 dim
= ARR_DIMS(array
);
1816 lb
= ARR_LBOUND(array
);
1817 elemtype
= ARR_ELEMTYPE(array
);
1818 arraydataptr
= ARR_DATA_PTR(array
);
1819 arraynullsptr
= ARR_NULLBITMAP(array
);
1823 * Check provided subscripts. A slice exceeding the current array limits
1824 * is silently truncated to the array limits. If we end up with an empty
1825 * slice, return an empty array.
1827 if (ndim
< nSubscripts
|| ndim
<= 0 || ndim
> MAXDIM
)
1828 return construct_empty_array(elemtype
);
1830 for (i
= 0; i
< nSubscripts
; i
++)
1832 if (lowerIndx
[i
] < lb
[i
])
1833 lowerIndx
[i
] = lb
[i
];
1834 if (upperIndx
[i
] >= (dim
[i
] + lb
[i
]))
1835 upperIndx
[i
] = dim
[i
] + lb
[i
] - 1;
1836 if (lowerIndx
[i
] > upperIndx
[i
])
1837 return construct_empty_array(elemtype
);
1839 /* fill any missing subscript positions with full array range */
1840 for (; i
< ndim
; i
++)
1842 lowerIndx
[i
] = lb
[i
];
1843 upperIndx
[i
] = dim
[i
] + lb
[i
] - 1;
1844 if (lowerIndx
[i
] > upperIndx
[i
])
1845 return construct_empty_array(elemtype
);
1848 mda_get_range(ndim
, span
, lowerIndx
, upperIndx
);
1850 bytes
= array_slice_size(arraydataptr
, arraynullsptr
,
1852 lowerIndx
, upperIndx
,
1853 elmlen
, elmbyval
, elmalign
);
1856 * Currently, we put a null bitmap in the result if the source has one;
1857 * could be smarter ...
1861 dataoffset
= ARR_OVERHEAD_WITHNULLS(ndim
, ArrayGetNItems(ndim
, span
));
1862 bytes
+= dataoffset
;
1866 dataoffset
= 0; /* marker for no null bitmap */
1867 bytes
+= ARR_OVERHEAD_NONULLS(ndim
);
1870 newarray
= (ArrayType
*) palloc(bytes
);
1871 SET_VARSIZE(newarray
, bytes
);
1872 newarray
->ndim
= ndim
;
1873 newarray
->dataoffset
= dataoffset
;
1874 newarray
->elemtype
= elemtype
;
1875 memcpy(ARR_DIMS(newarray
), span
, ndim
* sizeof(int));
1878 * Lower bounds of the new array are set to 1. Formerly (before 7.3) we
1879 * copied the given lowerIndx values ... but that seems confusing.
1881 newlb
= ARR_LBOUND(newarray
);
1882 for (i
= 0; i
< ndim
; i
++)
1885 array_extract_slice(newarray
,
1887 arraydataptr
, arraynullsptr
,
1888 lowerIndx
, upperIndx
,
1889 elmlen
, elmbyval
, elmalign
);
1896 * This routine sets the value of an array element (specified by
1897 * a subscript array) to a new value specified by "dataValue".
1899 * This handles both ordinary varlena arrays and fixed-length arrays.
1902 * array: the initial array object (mustn't be NULL)
1903 * nSubscripts: number of subscripts supplied
1904 * indx[]: the subscript values
1905 * dataValue: the datum to be inserted at the given position
1906 * isNull: whether dataValue is NULL
1907 * arraytyplen: pg_type.typlen for the array type
1908 * elmlen: pg_type.typlen for the array's element type
1909 * elmbyval: pg_type.typbyval for the array's element type
1910 * elmalign: pg_type.typalign for the array's element type
1913 * A new array is returned, just like the old except for the one
1914 * modified entry. The original array object is not changed.
1916 * For one-dimensional arrays only, we allow the array to be extended
1917 * by assigning to a position outside the existing subscript range; any
1918 * positions between the existing elements and the new one are set to NULLs.
1919 * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
1921 * NOTE: For assignments, we throw an error for invalid subscripts etc,
1922 * rather than returning a NULL as the fetch operations do.
1925 array_set(ArrayType
*array
,
1935 ArrayType
*newarray
;
1943 bits8
*oldnullbitmap
;
1957 if (arraytyplen
> 0)
1960 * fixed-length arrays -- these are assumed to be 1-d, 0-based. We
1961 * cannot extend them, either.
1963 if (nSubscripts
!= 1)
1965 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
1966 errmsg("wrong number of array subscripts")));
1968 if (indx
[0] < 0 || indx
[0] * elmlen
>= arraytyplen
)
1970 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
1971 errmsg("array subscript out of range")));
1975 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED
),
1976 errmsg("cannot assign null value to an element of a fixed-length array")));
1978 newarray
= (ArrayType
*) palloc(arraytyplen
);
1979 memcpy(newarray
, array
, arraytyplen
);
1980 elt_ptr
= (char *) newarray
+ indx
[0] * elmlen
;
1981 ArrayCastAndSet(dataValue
, elmlen
, elmbyval
, elmalign
, elt_ptr
);
1985 if (nSubscripts
<= 0 || nSubscripts
> MAXDIM
)
1987 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
1988 errmsg("wrong number of array subscripts")));
1990 /* make sure item to be inserted is not toasted */
1991 if (elmlen
== -1 && !isNull
)
1992 dataValue
= PointerGetDatum(PG_DETOAST_DATUM(dataValue
));
1994 /* detoast input array if necessary */
1995 array
= DatumGetArrayTypeP(PointerGetDatum(array
));
1997 ndim
= ARR_NDIM(array
);
2000 * if number of dims is zero, i.e. an empty array, create an array with
2001 * nSubscripts dimensions, and set the lower bounds to the supplied
2006 Oid elmtype
= ARR_ELEMTYPE(array
);
2008 for (i
= 0; i
< nSubscripts
; i
++)
2014 return construct_md_array(&dataValue
, &isNull
, nSubscripts
,
2016 elmlen
, elmbyval
, elmalign
);
2019 if (ndim
!= nSubscripts
)
2021 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
2022 errmsg("wrong number of array subscripts")));
2024 /* copy dim/lb since we may modify them */
2025 memcpy(dim
, ARR_DIMS(array
), ndim
* sizeof(int));
2026 memcpy(lb
, ARR_LBOUND(array
), ndim
* sizeof(int));
2028 newhasnulls
= (ARR_HASNULL(array
) || isNull
);
2029 addedbefore
= addedafter
= 0;
2036 if (indx
[0] < lb
[0])
2038 addedbefore
= lb
[0] - indx
[0];
2039 dim
[0] += addedbefore
;
2041 if (addedbefore
> 1)
2042 newhasnulls
= true; /* will insert nulls */
2044 if (indx
[0] >= (dim
[0] + lb
[0]))
2046 addedafter
= indx
[0] - (dim
[0] + lb
[0]) + 1;
2047 dim
[0] += addedafter
;
2049 newhasnulls
= true; /* will insert nulls */
2055 * XXX currently we do not support extending multi-dimensional arrays
2058 for (i
= 0; i
< ndim
; i
++)
2060 if (indx
[i
] < lb
[i
] ||
2061 indx
[i
] >= (dim
[i
] + lb
[i
]))
2063 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
2064 errmsg("array subscript out of range")));
2069 * Compute sizes of items and areas to copy
2071 newnitems
= ArrayGetNItems(ndim
, dim
);
2073 overheadlen
= ARR_OVERHEAD_WITHNULLS(ndim
, newnitems
);
2075 overheadlen
= ARR_OVERHEAD_NONULLS(ndim
);
2076 oldnitems
= ArrayGetNItems(ndim
, ARR_DIMS(array
));
2077 oldnullbitmap
= ARR_NULLBITMAP(array
);
2078 oldoverheadlen
= ARR_DATA_OFFSET(array
);
2079 olddatasize
= ARR_SIZE(array
) - oldoverheadlen
;
2085 lenafter
= olddatasize
;
2087 else if (addedafter
)
2090 lenbefore
= olddatasize
;
2096 offset
= ArrayGetOffset(nSubscripts
, dim
, lb
, indx
);
2097 elt_ptr
= array_seek(ARR_DATA_PTR(array
), 0, oldnullbitmap
, offset
,
2098 elmlen
, elmbyval
, elmalign
);
2099 lenbefore
= (int) (elt_ptr
- ARR_DATA_PTR(array
));
2100 if (array_get_isnull(oldnullbitmap
, offset
))
2104 olditemlen
= att_addlength_pointer(0, elmlen
, elt_ptr
);
2105 olditemlen
= att_align_nominal(olditemlen
, elmalign
);
2107 lenafter
= (int) (olddatasize
- lenbefore
- olditemlen
);
2114 newitemlen
= att_addlength_datum(0, elmlen
, dataValue
);
2115 newitemlen
= att_align_nominal(newitemlen
, elmalign
);
2118 newsize
= overheadlen
+ lenbefore
+ newitemlen
+ lenafter
;
2121 * OK, create the new array and fill in header/dimensions
2123 newarray
= (ArrayType
*) palloc(newsize
);
2124 SET_VARSIZE(newarray
, newsize
);
2125 newarray
->ndim
= ndim
;
2126 newarray
->dataoffset
= newhasnulls
? overheadlen
: 0;
2127 newarray
->elemtype
= ARR_ELEMTYPE(array
);
2128 memcpy(ARR_DIMS(newarray
), dim
, ndim
* sizeof(int));
2129 memcpy(ARR_LBOUND(newarray
), lb
, ndim
* sizeof(int));
2134 memcpy((char *) newarray
+ overheadlen
,
2135 (char *) array
+ oldoverheadlen
,
2138 ArrayCastAndSet(dataValue
, elmlen
, elmbyval
, elmalign
,
2139 (char *) newarray
+ overheadlen
+ lenbefore
);
2140 memcpy((char *) newarray
+ overheadlen
+ lenbefore
+ newitemlen
,
2141 (char *) array
+ oldoverheadlen
+ lenbefore
+ olditemlen
,
2145 * Fill in nulls bitmap if needed
2147 * Note: it's possible we just replaced the last NULL with a non-NULL, and
2148 * could get rid of the bitmap. Seems not worth testing for though.
2152 bits8
*newnullbitmap
= ARR_NULLBITMAP(newarray
);
2154 /* Zero the bitmap to take care of marking inserted positions null */
2155 MemSet(newnullbitmap
, 0, (newnitems
+ 7) / 8);
2156 /* Fix the inserted value */
2158 array_set_isnull(newnullbitmap
, newnitems
- 1, isNull
);
2160 array_set_isnull(newnullbitmap
, offset
, isNull
);
2161 /* Fix the copied range(s) */
2163 array_bitmap_copy(newnullbitmap
, addedbefore
,
2168 array_bitmap_copy(newnullbitmap
, 0,
2171 if (addedafter
== 0)
2172 array_bitmap_copy(newnullbitmap
, offset
+ 1,
2173 oldnullbitmap
, offset
+ 1,
2174 oldnitems
- offset
- 1);
2183 * This routine sets the value of a range of array locations (specified
2184 * by upper and lower subscript values) to new values passed as
2187 * This handles both ordinary varlena arrays and fixed-length arrays.
2190 * array: the initial array object (mustn't be NULL)
2191 * nSubscripts: number of subscripts supplied (must be same for upper/lower)
2192 * upperIndx[]: the upper subscript values
2193 * lowerIndx[]: the lower subscript values
2194 * srcArray: the source for the inserted values
2195 * isNull: indicates whether srcArray is NULL
2196 * arraytyplen: pg_type.typlen for the array type
2197 * elmlen: pg_type.typlen for the array's element type
2198 * elmbyval: pg_type.typbyval for the array's element type
2199 * elmalign: pg_type.typalign for the array's element type
2202 * A new array is returned, just like the old except for the
2203 * modified range. The original array object is not changed.
2205 * For one-dimensional arrays only, we allow the array to be extended
2206 * by assigning to positions outside the existing subscript range; any
2207 * positions between the existing elements and the new ones are set to NULLs.
2208 * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
2210 * NOTE: we assume it is OK to scribble on the provided index arrays
2211 * lowerIndx[] and upperIndx[]. These are generally just temporaries.
2213 * NOTE: For assignments, we throw an error for silly subscripts etc,
2214 * rather than returning a NULL or empty array as the fetch operations do.
2217 array_set_slice(ArrayType
*array
,
2221 ArrayType
*srcArray
,
2228 ArrayType
*newarray
;
2251 /* Currently, assignment from a NULL source array is a no-op */
2255 if (arraytyplen
> 0)
2258 * fixed-length arrays -- not got round to doing this...
2261 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
2262 errmsg("updates on slices of fixed-length arrays not implemented")));
2265 /* detoast arrays if necessary */
2266 array
= DatumGetArrayTypeP(PointerGetDatum(array
));
2267 srcArray
= DatumGetArrayTypeP(PointerGetDatum(srcArray
));
2269 /* note: we assume srcArray contains no toasted elements */
2271 ndim
= ARR_NDIM(array
);
2274 * if number of dims is zero, i.e. an empty array, create an array with
2275 * nSubscripts dimensions, and set the upper and lower bounds to the
2276 * supplied subscripts
2283 Oid elmtype
= ARR_ELEMTYPE(array
);
2285 deconstruct_array(srcArray
, elmtype
, elmlen
, elmbyval
, elmalign
,
2286 &dvalues
, &dnulls
, &nelems
);
2288 for (i
= 0; i
< nSubscripts
; i
++)
2290 dim
[i
] = 1 + upperIndx
[i
] - lowerIndx
[i
];
2291 lb
[i
] = lowerIndx
[i
];
2294 /* complain if too few source items; we ignore extras, however */
2295 if (nelems
< ArrayGetNItems(nSubscripts
, dim
))
2297 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
2298 errmsg("source array too small")));
2300 return construct_md_array(dvalues
, dnulls
, nSubscripts
,
2302 elmlen
, elmbyval
, elmalign
);
2305 if (ndim
< nSubscripts
|| ndim
<= 0 || ndim
> MAXDIM
)
2307 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
2308 errmsg("wrong number of array subscripts")));
2310 /* copy dim/lb since we may modify them */
2311 memcpy(dim
, ARR_DIMS(array
), ndim
* sizeof(int));
2312 memcpy(lb
, ARR_LBOUND(array
), ndim
* sizeof(int));
2314 newhasnulls
= (ARR_HASNULL(array
) || ARR_HASNULL(srcArray
));
2315 addedbefore
= addedafter
= 0;
2322 Assert(nSubscripts
== 1);
2323 if (lowerIndx
[0] > upperIndx
[0])
2325 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
2326 errmsg("upper bound cannot be less than lower bound")));
2327 if (lowerIndx
[0] < lb
[0])
2329 if (upperIndx
[0] < lb
[0] - 1)
2330 newhasnulls
= true; /* will insert nulls */
2331 addedbefore
= lb
[0] - lowerIndx
[0];
2332 dim
[0] += addedbefore
;
2333 lb
[0] = lowerIndx
[0];
2335 if (upperIndx
[0] >= (dim
[0] + lb
[0]))
2337 if (lowerIndx
[0] > (dim
[0] + lb
[0]))
2338 newhasnulls
= true; /* will insert nulls */
2339 addedafter
= upperIndx
[0] - (dim
[0] + lb
[0]) + 1;
2340 dim
[0] += addedafter
;
2346 * XXX currently we do not support extending multi-dimensional arrays
2349 for (i
= 0; i
< nSubscripts
; i
++)
2351 if (lowerIndx
[i
] > upperIndx
[i
])
2353 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
2354 errmsg("upper bound cannot be less than lower bound")));
2355 if (lowerIndx
[i
] < lb
[i
] ||
2356 upperIndx
[i
] >= (dim
[i
] + lb
[i
]))
2358 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
2359 errmsg("array subscript out of range")));
2361 /* fill any missing subscript positions with full array range */
2362 for (; i
< ndim
; i
++)
2364 lowerIndx
[i
] = lb
[i
];
2365 upperIndx
[i
] = dim
[i
] + lb
[i
] - 1;
2366 if (lowerIndx
[i
] > upperIndx
[i
])
2368 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
2369 errmsg("upper bound cannot be less than lower bound")));
2373 /* Do this mainly to check for overflow */
2374 nitems
= ArrayGetNItems(ndim
, dim
);
2377 * Make sure source array has enough entries. Note we ignore the shape of
2378 * the source array and just read entries serially.
2380 mda_get_range(ndim
, span
, lowerIndx
, upperIndx
);
2381 nsrcitems
= ArrayGetNItems(ndim
, span
);
2382 if (nsrcitems
> ArrayGetNItems(ARR_NDIM(srcArray
), ARR_DIMS(srcArray
)))
2384 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
2385 errmsg("source array too small")));
2388 * Compute space occupied by new entries, space occupied by replaced
2389 * entries, and required space for new array.
2392 overheadlen
= ARR_OVERHEAD_WITHNULLS(ndim
, nitems
);
2394 overheadlen
= ARR_OVERHEAD_NONULLS(ndim
);
2395 newitemsize
= array_nelems_size(ARR_DATA_PTR(srcArray
), 0,
2396 ARR_NULLBITMAP(srcArray
), nsrcitems
,
2397 elmlen
, elmbyval
, elmalign
);
2398 oldoverheadlen
= ARR_DATA_OFFSET(array
);
2399 olddatasize
= ARR_SIZE(array
) - oldoverheadlen
;
2403 * here we do not need to cope with extension of the array; it would
2404 * be a lot more complicated if we had to do so...
2406 olditemsize
= array_slice_size(ARR_DATA_PTR(array
),
2407 ARR_NULLBITMAP(array
),
2409 lowerIndx
, upperIndx
,
2410 elmlen
, elmbyval
, elmalign
);
2411 lenbefore
= lenafter
= 0; /* keep compiler quiet */
2412 itemsbefore
= itemsafter
= nolditems
= 0;
2417 * here we must allow for possibility of slice larger than orig array
2419 int oldlb
= ARR_LBOUND(array
)[0];
2420 int oldub
= oldlb
+ ARR_DIMS(array
)[0] - 1;
2421 int slicelb
= Max(oldlb
, lowerIndx
[0]);
2422 int sliceub
= Min(oldub
, upperIndx
[0]);
2423 char *oldarraydata
= ARR_DATA_PTR(array
);
2424 bits8
*oldarraybitmap
= ARR_NULLBITMAP(array
);
2426 itemsbefore
= Min(slicelb
, oldub
+ 1) - oldlb
;
2427 lenbefore
= array_nelems_size(oldarraydata
, 0, oldarraybitmap
,
2429 elmlen
, elmbyval
, elmalign
);
2430 if (slicelb
> sliceub
)
2437 nolditems
= sliceub
- slicelb
+ 1;
2438 olditemsize
= array_nelems_size(oldarraydata
+ lenbefore
,
2439 itemsbefore
, oldarraybitmap
,
2441 elmlen
, elmbyval
, elmalign
);
2443 itemsafter
= oldub
- sliceub
;
2444 lenafter
= olddatasize
- lenbefore
- olditemsize
;
2447 newsize
= overheadlen
+ olddatasize
- olditemsize
+ newitemsize
;
2449 newarray
= (ArrayType
*) palloc(newsize
);
2450 SET_VARSIZE(newarray
, newsize
);
2451 newarray
->ndim
= ndim
;
2452 newarray
->dataoffset
= newhasnulls
? overheadlen
: 0;
2453 newarray
->elemtype
= ARR_ELEMTYPE(array
);
2454 memcpy(ARR_DIMS(newarray
), dim
, ndim
* sizeof(int));
2455 memcpy(ARR_LBOUND(newarray
), lb
, ndim
* sizeof(int));
2460 * here we do not need to cope with extension of the array; it would
2461 * be a lot more complicated if we had to do so...
2463 array_insert_slice(newarray
, array
, srcArray
,
2465 lowerIndx
, upperIndx
,
2466 elmlen
, elmbyval
, elmalign
);
2471 memcpy((char *) newarray
+ overheadlen
,
2472 (char *) array
+ oldoverheadlen
,
2474 memcpy((char *) newarray
+ overheadlen
+ lenbefore
,
2475 ARR_DATA_PTR(srcArray
),
2477 memcpy((char *) newarray
+ overheadlen
+ lenbefore
+ newitemsize
,
2478 (char *) array
+ oldoverheadlen
+ lenbefore
+ olditemsize
,
2480 /* fill in nulls bitmap if needed */
2483 bits8
*newnullbitmap
= ARR_NULLBITMAP(newarray
);
2484 bits8
*oldnullbitmap
= ARR_NULLBITMAP(array
);
2486 /* Zero the bitmap to handle marking inserted positions null */
2487 MemSet(newnullbitmap
, 0, (nitems
+ 7) / 8);
2488 array_bitmap_copy(newnullbitmap
, addedbefore
,
2491 array_bitmap_copy(newnullbitmap
, lowerIndx
[0] - lb
[0],
2492 ARR_NULLBITMAP(srcArray
), 0,
2494 array_bitmap_copy(newnullbitmap
, addedbefore
+ itemsbefore
+ nolditems
,
2495 oldnullbitmap
, itemsbefore
+ nolditems
,
2506 * Map an array through an arbitrary function. Return a new array with
2507 * same dimensions and each source element transformed by fn(). Each
2508 * source element is passed as the first argument to fn(); additional
2509 * arguments to be passed to fn() can be specified by the caller.
2510 * The output array can have a different element type than the input.
2513 * * fcinfo: a function-call data structure pre-constructed by the caller
2514 * to be ready to call the desired function, with everything except the
2515 * first argument position filled in. In particular, flinfo identifies
2516 * the function fn(), and if nargs > 1 then argument positions after the
2517 * first must be preset to the additional values to be passed. The
2518 * first argument position initially holds the input array value.
2519 * * inpType: OID of element type of input array. This must be the same as,
2520 * or binary-compatible with, the first argument type of fn().
2521 * * retType: OID of element type of output array. This must be the same as,
2522 * or binary-compatible with, the result type of fn().
2523 * * amstate: workspace for array_map. Must be zeroed by caller before
2524 * first call, and not touched after that.
2526 * It is legitimate to pass a freshly-zeroed ArrayMapState on each call,
2527 * but better performance can be had if the state can be preserved across
2528 * a series of calls.
2530 * NB: caller must assure that input array is not NULL. NULL elements in
2531 * the array are OK however.
2534 array_map(FunctionCallInfo fcinfo
, Oid inpType
, Oid retType
,
2535 ArrayMapState
*amstate
)
2558 ArrayMetaState
*inp_extra
;
2559 ArrayMetaState
*ret_extra
;
2561 /* Get input array */
2562 if (fcinfo
->nargs
< 1)
2563 elog(ERROR
, "invalid nargs: %d", fcinfo
->nargs
);
2564 if (PG_ARGISNULL(0))
2565 elog(ERROR
, "null input array");
2566 v
= PG_GETARG_ARRAYTYPE_P(0);
2568 Assert(ARR_ELEMTYPE(v
) == inpType
);
2572 nitems
= ArrayGetNItems(ndim
, dim
);
2574 /* Check for empty array */
2577 /* Return empty array */
2578 PG_RETURN_ARRAYTYPE_P(construct_empty_array(retType
));
2582 * We arrange to look up info about input and return element types only
2583 * once per series of calls, assuming the element type doesn't change
2586 inp_extra
= &amstate
->inp_extra
;
2587 ret_extra
= &amstate
->ret_extra
;
2589 if (inp_extra
->element_type
!= inpType
)
2591 get_typlenbyvalalign(inpType
,
2593 &inp_extra
->typbyval
,
2594 &inp_extra
->typalign
);
2595 inp_extra
->element_type
= inpType
;
2597 inp_typlen
= inp_extra
->typlen
;
2598 inp_typbyval
= inp_extra
->typbyval
;
2599 inp_typalign
= inp_extra
->typalign
;
2601 if (ret_extra
->element_type
!= retType
)
2603 get_typlenbyvalalign(retType
,
2605 &ret_extra
->typbyval
,
2606 &ret_extra
->typalign
);
2607 ret_extra
->element_type
= retType
;
2609 typlen
= ret_extra
->typlen
;
2610 typbyval
= ret_extra
->typbyval
;
2611 typalign
= ret_extra
->typalign
;
2613 /* Allocate temporary arrays for new values */
2614 values
= (Datum
*) palloc(nitems
* sizeof(Datum
));
2615 nulls
= (bool *) palloc(nitems
* sizeof(bool));
2617 /* Loop over source data */
2618 s
= ARR_DATA_PTR(v
);
2619 bitmap
= ARR_NULLBITMAP(v
);
2623 for (i
= 0; i
< nitems
; i
++)
2627 /* Get source element, checking for NULL */
2628 if (bitmap
&& (*bitmap
& bitmask
) == 0)
2630 fcinfo
->argnull
[0] = true;
2634 elt
= fetch_att(s
, inp_typbyval
, inp_typlen
);
2635 s
= att_addlength_datum(s
, inp_typlen
, elt
);
2636 s
= (char *) att_align_nominal(s
, inp_typalign
);
2637 fcinfo
->arg
[0] = elt
;
2638 fcinfo
->argnull
[0] = false;
2642 * Apply the given function to source elt and extra args.
2644 if (fcinfo
->flinfo
->fn_strict
)
2648 for (j
= 0; j
< fcinfo
->nargs
; j
++)
2650 if (fcinfo
->argnull
[j
])
2660 fcinfo
->isnull
= false;
2661 values
[i
] = FunctionCallInvoke(fcinfo
);
2664 fcinfo
->isnull
= true;
2666 nulls
[i
] = fcinfo
->isnull
;
2671 /* Ensure data is not toasted */
2673 values
[i
] = PointerGetDatum(PG_DETOAST_DATUM(values
[i
]));
2674 /* Update total result size */
2675 nbytes
= att_addlength_datum(nbytes
, typlen
, values
[i
]);
2676 nbytes
= att_align_nominal(nbytes
, typalign
);
2677 /* check for overflow of total request */
2678 if (!AllocSizeIsValid(nbytes
))
2680 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
2681 errmsg("array size exceeds the maximum allowed (%d)",
2682 (int) MaxAllocSize
)));
2685 /* advance bitmap pointer if any */
2689 if (bitmask
== 0x100)
2697 /* Allocate and initialize the result array */
2700 dataoffset
= ARR_OVERHEAD_WITHNULLS(ndim
, nitems
);
2701 nbytes
+= dataoffset
;
2705 dataoffset
= 0; /* marker for no null bitmap */
2706 nbytes
+= ARR_OVERHEAD_NONULLS(ndim
);
2708 result
= (ArrayType
*) palloc(nbytes
);
2709 SET_VARSIZE(result
, nbytes
);
2710 result
->ndim
= ndim
;
2711 result
->dataoffset
= dataoffset
;
2712 result
->elemtype
= retType
;
2713 memcpy(ARR_DIMS(result
), ARR_DIMS(v
), 2 * ndim
* sizeof(int));
2716 * Note: do not risk trying to pfree the results of the called function
2718 CopyArrayEls(result
,
2719 values
, nulls
, nitems
,
2720 typlen
, typbyval
, typalign
,
2726 PG_RETURN_ARRAYTYPE_P(result
);
2730 * construct_array --- simple method for constructing an array object
2732 * elems: array of Datum items to become the array contents
2733 * (NULL element values are not supported).
2734 * nelems: number of items
2735 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
2737 * A palloc'd 1-D array object is constructed and returned. Note that
2738 * elem values will be copied into the object even if pass-by-ref type.
2740 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
2741 * from the system catalogs, given the elmtype. However, the caller is
2742 * in a better position to cache this info across multiple uses, or even
2743 * to hard-wire values if the element type is hard-wired.
2746 construct_array(Datum
*elems
, int nelems
,
2748 int elmlen
, bool elmbyval
, char elmalign
)
2756 return construct_md_array(elems
, NULL
, 1, dims
, lbs
,
2757 elmtype
, elmlen
, elmbyval
, elmalign
);
2761 * construct_md_array --- simple method for constructing an array object
2762 * with arbitrary dimensions and possible NULLs
2764 * elems: array of Datum items to become the array contents
2765 * nulls: array of is-null flags (can be NULL if no nulls)
2766 * ndims: number of dimensions
2767 * dims: integer array with size of each dimension
2768 * lbs: integer array with lower bound of each dimension
2769 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
2771 * A palloc'd ndims-D array object is constructed and returned. Note that
2772 * elem values will be copied into the object even if pass-by-ref type.
2774 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
2775 * from the system catalogs, given the elmtype. However, the caller is
2776 * in a better position to cache this info across multiple uses, or even
2777 * to hard-wire values if the element type is hard-wired.
2780 construct_md_array(Datum
*elems
,
2785 Oid elmtype
, int elmlen
, bool elmbyval
, char elmalign
)
2794 if (ndims
< 0) /* we do allow zero-dimension arrays */
2796 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
2797 errmsg("invalid number of dimensions: %d", ndims
)));
2800 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
2801 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
2804 /* fast track for empty array */
2806 return construct_empty_array(elmtype
);
2808 nelems
= ArrayGetNItems(ndims
, dims
);
2810 /* compute required space */
2813 for (i
= 0; i
< nelems
; i
++)
2815 if (nulls
&& nulls
[i
])
2820 /* make sure data is not toasted */
2822 elems
[i
] = PointerGetDatum(PG_DETOAST_DATUM(elems
[i
]));
2823 nbytes
= att_addlength_datum(nbytes
, elmlen
, elems
[i
]);
2824 nbytes
= att_align_nominal(nbytes
, elmalign
);
2825 /* check for overflow of total request */
2826 if (!AllocSizeIsValid(nbytes
))
2828 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
2829 errmsg("array size exceeds the maximum allowed (%d)",
2830 (int) MaxAllocSize
)));
2833 /* Allocate and initialize result array */
2836 dataoffset
= ARR_OVERHEAD_WITHNULLS(ndims
, nelems
);
2837 nbytes
+= dataoffset
;
2841 dataoffset
= 0; /* marker for no null bitmap */
2842 nbytes
+= ARR_OVERHEAD_NONULLS(ndims
);
2844 result
= (ArrayType
*) palloc(nbytes
);
2845 SET_VARSIZE(result
, nbytes
);
2846 result
->ndim
= ndims
;
2847 result
->dataoffset
= dataoffset
;
2848 result
->elemtype
= elmtype
;
2849 memcpy(ARR_DIMS(result
), dims
, ndims
* sizeof(int));
2850 memcpy(ARR_LBOUND(result
), lbs
, ndims
* sizeof(int));
2852 CopyArrayEls(result
,
2853 elems
, nulls
, nelems
,
2854 elmlen
, elmbyval
, elmalign
,
2861 * construct_empty_array --- make a zero-dimensional array of given type
2864 construct_empty_array(Oid elmtype
)
2868 result
= (ArrayType
*) palloc(sizeof(ArrayType
));
2869 SET_VARSIZE(result
, sizeof(ArrayType
));
2871 result
->dataoffset
= 0;
2872 result
->elemtype
= elmtype
;
2877 * deconstruct_array --- simple method for extracting data from an array
2879 * array: array object to examine (must not be NULL)
2880 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
2881 * elemsp: return value, set to point to palloc'd array of Datum values
2882 * nullsp: return value, set to point to palloc'd array of isnull markers
2883 * nelemsp: return value, set to number of extracted values
2885 * The caller may pass nullsp == NULL if it does not support NULLs in the
2886 * array. Note that this produces a very uninformative error message,
2887 * so do it only in cases where a NULL is really not expected.
2889 * If array elements are pass-by-ref data type, the returned Datums will
2890 * be pointers into the array object.
2892 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
2893 * from the system catalogs, given the elmtype. However, in most current
2894 * uses the type is hard-wired into the caller and so we can save a lookup
2895 * cycle by hard-wiring the type info as well.
2898 deconstruct_array(ArrayType
*array
,
2900 int elmlen
, bool elmbyval
, char elmalign
,
2901 Datum
**elemsp
, bool **nullsp
, int *nelemsp
)
2911 Assert(ARR_ELEMTYPE(array
) == elmtype
);
2913 nelems
= ArrayGetNItems(ARR_NDIM(array
), ARR_DIMS(array
));
2914 *elemsp
= elems
= (Datum
*) palloc(nelems
* sizeof(Datum
));
2916 *nullsp
= nulls
= (bool *) palloc(nelems
* sizeof(bool));
2921 p
= ARR_DATA_PTR(array
);
2922 bitmap
= ARR_NULLBITMAP(array
);
2925 for (i
= 0; i
< nelems
; i
++)
2927 /* Get source element, checking for NULL */
2928 if (bitmap
&& (*bitmap
& bitmask
) == 0)
2930 elems
[i
] = (Datum
) 0;
2935 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED
),
2936 errmsg("null array element not allowed in this context")));
2940 elems
[i
] = fetch_att(p
, elmbyval
, elmlen
);
2943 p
= att_addlength_pointer(p
, elmlen
, p
);
2944 p
= (char *) att_align_nominal(p
, elmalign
);
2947 /* advance bitmap pointer if any */
2951 if (bitmask
== 0x100)
2963 * compares two arrays for equality
2965 * returns true if the arrays are equal, false otherwise.
2967 * Note: we do not use array_cmp here, since equality may be meaningful in
2968 * datatypes that don't have a total ordering (and hence no btree support).
2971 array_eq(PG_FUNCTION_ARGS
)
2973 ArrayType
*array1
= PG_GETARG_ARRAYTYPE_P(0);
2974 ArrayType
*array2
= PG_GETARG_ARRAYTYPE_P(1);
2975 int ndims1
= ARR_NDIM(array1
);
2976 int ndims2
= ARR_NDIM(array2
);
2977 int *dims1
= ARR_DIMS(array1
);
2978 int *dims2
= ARR_DIMS(array2
);
2979 Oid element_type
= ARR_ELEMTYPE(array1
);
2982 TypeCacheEntry
*typentry
;
2992 FunctionCallInfoData locfcinfo
;
2994 if (element_type
!= ARR_ELEMTYPE(array2
))
2996 (errcode(ERRCODE_DATATYPE_MISMATCH
),
2997 errmsg("cannot compare arrays of different element types")));
2999 /* fast path if the arrays do not have the same dimensionality */
3000 if (ndims1
!= ndims2
||
3001 memcmp(dims1
, dims2
, 2 * ndims1
* sizeof(int)) != 0)
3006 * We arrange to look up the equality function only once per series of
3007 * calls, assuming the element type doesn't change underneath us. The
3008 * typcache is used so that we have no memory leakage when being used
3009 * as an index support function.
3011 typentry
= (TypeCacheEntry
*) fcinfo
->flinfo
->fn_extra
;
3012 if (typentry
== NULL
||
3013 typentry
->type_id
!= element_type
)
3015 typentry
= lookup_type_cache(element_type
,
3016 TYPECACHE_EQ_OPR_FINFO
);
3017 if (!OidIsValid(typentry
->eq_opr_finfo
.fn_oid
))
3019 (errcode(ERRCODE_UNDEFINED_FUNCTION
),
3020 errmsg("could not identify an equality operator for type %s",
3021 format_type_be(element_type
))));
3022 fcinfo
->flinfo
->fn_extra
= (void *) typentry
;
3024 typlen
= typentry
->typlen
;
3025 typbyval
= typentry
->typbyval
;
3026 typalign
= typentry
->typalign
;
3029 * apply the operator to each pair of array elements.
3031 InitFunctionCallInfoData(locfcinfo
, &typentry
->eq_opr_finfo
, 2,
3034 /* Loop over source data */
3035 nitems
= ArrayGetNItems(ndims1
, dims1
);
3036 ptr1
= ARR_DATA_PTR(array1
);
3037 ptr2
= ARR_DATA_PTR(array2
);
3038 bitmap1
= ARR_NULLBITMAP(array1
);
3039 bitmap2
= ARR_NULLBITMAP(array2
);
3040 bitmask
= 1; /* use same bitmask for both arrays */
3042 for (i
= 0; i
< nitems
; i
++)
3050 /* Get elements, checking for NULL */
3051 if (bitmap1
&& (*bitmap1
& bitmask
) == 0)
3059 elt1
= fetch_att(ptr1
, typbyval
, typlen
);
3060 ptr1
= att_addlength_pointer(ptr1
, typlen
, ptr1
);
3061 ptr1
= (char *) att_align_nominal(ptr1
, typalign
);
3064 if (bitmap2
&& (*bitmap2
& bitmask
) == 0)
3072 elt2
= fetch_att(ptr2
, typbyval
, typlen
);
3073 ptr2
= att_addlength_pointer(ptr2
, typlen
, ptr2
);
3074 ptr2
= (char *) att_align_nominal(ptr2
, typalign
);
3077 /* advance bitmap pointers if any */
3079 if (bitmask
== 0x100)
3089 * We consider two NULLs equal; NULL and not-NULL are unequal.
3091 if (isnull1
&& isnull2
)
3093 if (isnull1
|| isnull2
)
3100 * Apply the operator to the element pair
3102 locfcinfo
.arg
[0] = elt1
;
3103 locfcinfo
.arg
[1] = elt2
;
3104 locfcinfo
.argnull
[0] = false;
3105 locfcinfo
.argnull
[1] = false;
3106 locfcinfo
.isnull
= false;
3107 oprresult
= DatumGetBool(FunctionCallInvoke(&locfcinfo
));
3116 /* Avoid leaking memory when handed toasted input. */
3117 PG_FREE_IF_COPY(array1
, 0);
3118 PG_FREE_IF_COPY(array2
, 1);
3120 PG_RETURN_BOOL(result
);
3124 /*-----------------------------------------------------------------------------
3125 * array-array bool operators:
3126 * Given two arrays, iterate comparison operators
3127 * over the array. Uses logic similar to text comparison
3128 * functions, except element-by-element instead of
3129 * character-by-character.
3130 *----------------------------------------------------------------------------
3134 array_ne(PG_FUNCTION_ARGS
)
3136 PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo
)));
3140 array_lt(PG_FUNCTION_ARGS
)
3142 PG_RETURN_BOOL(array_cmp(fcinfo
) < 0);
3146 array_gt(PG_FUNCTION_ARGS
)
3148 PG_RETURN_BOOL(array_cmp(fcinfo
) > 0);
3152 array_le(PG_FUNCTION_ARGS
)
3154 PG_RETURN_BOOL(array_cmp(fcinfo
) <= 0);
3158 array_ge(PG_FUNCTION_ARGS
)
3160 PG_RETURN_BOOL(array_cmp(fcinfo
) >= 0);
3164 btarraycmp(PG_FUNCTION_ARGS
)
3166 PG_RETURN_INT32(array_cmp(fcinfo
));
3171 * Internal comparison function for arrays.
3173 * Returns -1, 0 or 1
3176 array_cmp(FunctionCallInfo fcinfo
)
3178 ArrayType
*array1
= PG_GETARG_ARRAYTYPE_P(0);
3179 ArrayType
*array2
= PG_GETARG_ARRAYTYPE_P(1);
3180 int ndims1
= ARR_NDIM(array1
);
3181 int ndims2
= ARR_NDIM(array2
);
3182 int *dims1
= ARR_DIMS(array1
);
3183 int *dims2
= ARR_DIMS(array2
);
3184 int nitems1
= ArrayGetNItems(ndims1
, dims1
);
3185 int nitems2
= ArrayGetNItems(ndims2
, dims2
);
3186 Oid element_type
= ARR_ELEMTYPE(array1
);
3188 TypeCacheEntry
*typentry
;
3199 FunctionCallInfoData locfcinfo
;
3201 if (element_type
!= ARR_ELEMTYPE(array2
))
3203 (errcode(ERRCODE_DATATYPE_MISMATCH
),
3204 errmsg("cannot compare arrays of different element types")));
3207 * We arrange to look up the comparison function only once per series of
3208 * calls, assuming the element type doesn't change underneath us. The
3209 * typcache is used so that we have no memory leakage when being used as
3210 * an index support function.
3212 typentry
= (TypeCacheEntry
*) fcinfo
->flinfo
->fn_extra
;
3213 if (typentry
== NULL
||
3214 typentry
->type_id
!= element_type
)
3216 typentry
= lookup_type_cache(element_type
,
3217 TYPECACHE_CMP_PROC_FINFO
);
3218 if (!OidIsValid(typentry
->cmp_proc_finfo
.fn_oid
))
3220 (errcode(ERRCODE_UNDEFINED_FUNCTION
),
3221 errmsg("could not identify a comparison function for type %s",
3222 format_type_be(element_type
))));
3223 fcinfo
->flinfo
->fn_extra
= (void *) typentry
;
3225 typlen
= typentry
->typlen
;
3226 typbyval
= typentry
->typbyval
;
3227 typalign
= typentry
->typalign
;
3230 * apply the operator to each pair of array elements.
3232 InitFunctionCallInfoData(locfcinfo
, &typentry
->cmp_proc_finfo
, 2,
3235 /* Loop over source data */
3236 min_nitems
= Min(nitems1
, nitems2
);
3237 ptr1
= ARR_DATA_PTR(array1
);
3238 ptr2
= ARR_DATA_PTR(array2
);
3239 bitmap1
= ARR_NULLBITMAP(array1
);
3240 bitmap2
= ARR_NULLBITMAP(array2
);
3241 bitmask
= 1; /* use same bitmask for both arrays */
3243 for (i
= 0; i
< min_nitems
; i
++)
3251 /* Get elements, checking for NULL */
3252 if (bitmap1
&& (*bitmap1
& bitmask
) == 0)
3260 elt1
= fetch_att(ptr1
, typbyval
, typlen
);
3261 ptr1
= att_addlength_pointer(ptr1
, typlen
, ptr1
);
3262 ptr1
= (char *) att_align_nominal(ptr1
, typalign
);
3265 if (bitmap2
&& (*bitmap2
& bitmask
) == 0)
3273 elt2
= fetch_att(ptr2
, typbyval
, typlen
);
3274 ptr2
= att_addlength_pointer(ptr2
, typlen
, ptr2
);
3275 ptr2
= (char *) att_align_nominal(ptr2
, typalign
);
3278 /* advance bitmap pointers if any */
3280 if (bitmask
== 0x100)
3290 * We consider two NULLs equal; NULL > not-NULL.
3292 if (isnull1
&& isnull2
)
3296 /* arg1 is greater than arg2 */
3302 /* arg1 is less than arg2 */
3307 /* Compare the pair of elements */
3308 locfcinfo
.arg
[0] = elt1
;
3309 locfcinfo
.arg
[1] = elt2
;
3310 locfcinfo
.argnull
[0] = false;
3311 locfcinfo
.argnull
[1] = false;
3312 locfcinfo
.isnull
= false;
3313 cmpresult
= DatumGetInt32(FunctionCallInvoke(&locfcinfo
));
3316 continue; /* equal */
3320 /* arg1 is less than arg2 */
3326 /* arg1 is greater than arg2 */
3333 * If arrays contain same data (up to end of shorter one), apply
3334 * additional rules to sort by dimensionality. The relative significance
3335 * of the different bits of information is historical; mainly we just care
3336 * that we don't say "equal" for arrays of different dimensionality.
3340 if (nitems1
!= nitems2
)
3341 result
= (nitems1
< nitems2
) ? -1 : 1;
3342 else if (ndims1
!= ndims2
)
3343 result
= (ndims1
< ndims2
) ? -1 : 1;
3346 /* this relies on LB array immediately following DIMS array */
3347 for (i
= 0; i
< ndims1
* 2; i
++)
3349 if (dims1
[i
] != dims2
[i
])
3351 result
= (dims1
[i
] < dims2
[i
]) ? -1 : 1;
3358 /* Avoid leaking memory when handed toasted input. */
3359 PG_FREE_IF_COPY(array1
, 0);
3360 PG_FREE_IF_COPY(array2
, 1);
3366 /*-----------------------------------------------------------------------------
3367 * array overlap/containment comparisons
3368 * These use the same methods of comparing array elements as array_eq.
3369 * We consider only the elements of the arrays, ignoring dimensionality.
3370 *----------------------------------------------------------------------------
3374 * array_contain_compare :
3375 * compares two arrays for overlap/containment
3377 * When matchall is true, return true if all members of array1 are in array2.
3378 * When matchall is false, return true if any members of array1 are in array2.
3381 array_contain_compare(ArrayType
*array1
, ArrayType
*array2
, bool matchall
,
3384 bool result
= matchall
;
3385 Oid element_type
= ARR_ELEMTYPE(array1
);
3386 TypeCacheEntry
*typentry
;
3399 FunctionCallInfoData locfcinfo
;
3401 if (element_type
!= ARR_ELEMTYPE(array2
))
3403 (errcode(ERRCODE_DATATYPE_MISMATCH
),
3404 errmsg("cannot compare arrays of different element types")));
3407 * We arrange to look up the equality function only once per series of
3408 * calls, assuming the element type doesn't change underneath us. The
3409 * typcache is used so that we have no memory leakage when being used as
3410 * an index support function.
3412 typentry
= (TypeCacheEntry
*) *fn_extra
;
3413 if (typentry
== NULL
||
3414 typentry
->type_id
!= element_type
)
3416 typentry
= lookup_type_cache(element_type
,
3417 TYPECACHE_EQ_OPR_FINFO
);
3418 if (!OidIsValid(typentry
->eq_opr_finfo
.fn_oid
))
3420 (errcode(ERRCODE_UNDEFINED_FUNCTION
),
3421 errmsg("could not identify an equality operator for type %s",
3422 format_type_be(element_type
))));
3423 *fn_extra
= (void *) typentry
;
3425 typlen
= typentry
->typlen
;
3426 typbyval
= typentry
->typbyval
;
3427 typalign
= typentry
->typalign
;
3430 * Since we probably will need to scan array2 multiple times, it's
3431 * worthwhile to use deconstruct_array on it. We scan array1 the hard way
3432 * however, since we very likely won't need to look at all of it.
3434 deconstruct_array(array2
, element_type
, typlen
, typbyval
, typalign
,
3435 &values2
, &nulls2
, &nelems2
);
3438 * Apply the comparison operator to each pair of array elements.
3440 InitFunctionCallInfoData(locfcinfo
, &typentry
->eq_opr_finfo
, 2,
3443 /* Loop over source data */
3444 nelems1
= ArrayGetNItems(ARR_NDIM(array1
), ARR_DIMS(array1
));
3445 ptr1
= ARR_DATA_PTR(array1
);
3446 bitmap1
= ARR_NULLBITMAP(array1
);
3449 for (i
= 0; i
< nelems1
; i
++)
3454 /* Get element, checking for NULL */
3455 if (bitmap1
&& (*bitmap1
& bitmask
) == 0)
3463 elt1
= fetch_att(ptr1
, typbyval
, typlen
);
3464 ptr1
= att_addlength_pointer(ptr1
, typlen
, ptr1
);
3465 ptr1
= (char *) att_align_nominal(ptr1
, typalign
);
3468 /* advance bitmap pointer if any */
3470 if (bitmask
== 0x100)
3478 * We assume that the comparison operator is strict, so a NULL can't
3479 * match anything. XXX this diverges from the "NULL=NULL" behavior of
3480 * array_eq, should we act like that?
3492 for (j
= 0; j
< nelems2
; j
++)
3494 Datum elt2
= values2
[j
];
3495 bool isnull2
= nulls2
[j
];
3499 continue; /* can't match */
3502 * Apply the operator to the element pair
3504 locfcinfo
.arg
[0] = elt1
;
3505 locfcinfo
.arg
[1] = elt2
;
3506 locfcinfo
.argnull
[0] = false;
3507 locfcinfo
.argnull
[1] = false;
3508 locfcinfo
.isnull
= false;
3509 oprresult
= DatumGetBool(FunctionCallInvoke(&locfcinfo
));
3516 /* found a match for elt1 */
3525 /* no match for elt1 */
3541 arrayoverlap(PG_FUNCTION_ARGS
)
3543 ArrayType
*array1
= PG_GETARG_ARRAYTYPE_P(0);
3544 ArrayType
*array2
= PG_GETARG_ARRAYTYPE_P(1);
3547 result
= array_contain_compare(array1
, array2
, false,
3548 &fcinfo
->flinfo
->fn_extra
);
3550 /* Avoid leaking memory when handed toasted input. */
3551 PG_FREE_IF_COPY(array1
, 0);
3552 PG_FREE_IF_COPY(array2
, 1);
3554 PG_RETURN_BOOL(result
);
3558 arraycontains(PG_FUNCTION_ARGS
)
3560 ArrayType
*array1
= PG_GETARG_ARRAYTYPE_P(0);
3561 ArrayType
*array2
= PG_GETARG_ARRAYTYPE_P(1);
3564 result
= array_contain_compare(array2
, array1
, true,
3565 &fcinfo
->flinfo
->fn_extra
);
3567 /* Avoid leaking memory when handed toasted input. */
3568 PG_FREE_IF_COPY(array1
, 0);
3569 PG_FREE_IF_COPY(array2
, 1);
3571 PG_RETURN_BOOL(result
);
3575 arraycontained(PG_FUNCTION_ARGS
)
3577 ArrayType
*array1
= PG_GETARG_ARRAYTYPE_P(0);
3578 ArrayType
*array2
= PG_GETARG_ARRAYTYPE_P(1);
3581 result
= array_contain_compare(array1
, array2
, true,
3582 &fcinfo
->flinfo
->fn_extra
);
3584 /* Avoid leaking memory when handed toasted input. */
3585 PG_FREE_IF_COPY(array1
, 0);
3586 PG_FREE_IF_COPY(array2
, 1);
3588 PG_RETURN_BOOL(result
);
3592 /***************************************************************************/
3593 /******************| Support Routines |*****************/
3594 /***************************************************************************/
3597 * Check whether a specific array element is NULL
3599 * nullbitmap: pointer to array's null bitmap (NULL if none)
3600 * offset: 0-based linear element number of array element
3603 array_get_isnull(const bits8
*nullbitmap
, int offset
)
3605 if (nullbitmap
== NULL
)
3606 return false; /* assume not null */
3607 if (nullbitmap
[offset
/ 8] & (1 << (offset
% 8)))
3608 return false; /* not null */
3613 * Set a specific array element's null-bitmap entry
3615 * nullbitmap: pointer to array's null bitmap (mustn't be NULL)
3616 * offset: 0-based linear element number of array element
3617 * isNull: null status to set
3620 array_set_isnull(bits8
*nullbitmap
, int offset
, bool isNull
)
3624 nullbitmap
+= offset
/ 8;
3625 bitmask
= 1 << (offset
% 8);
3627 *nullbitmap
&= ~bitmask
;
3629 *nullbitmap
|= bitmask
;
3633 * Fetch array element at pointer, converted correctly to a Datum
3635 * Caller must have handled case of NULL element
3638 ArrayCast(char *value
, bool byval
, int len
)
3640 return fetch_att(value
, byval
, len
);
3644 * Copy datum to *dest and return total space used (including align padding)
3646 * Caller must have handled case of NULL element
3649 ArrayCastAndSet(Datum src
,
3660 store_att_byval(dest
, src
, typlen
);
3662 memmove(dest
, DatumGetPointer(src
), typlen
);
3663 inc
= att_align_nominal(typlen
, typalign
);
3668 inc
= att_addlength_datum(0, typlen
, src
);
3669 memmove(dest
, DatumGetPointer(src
), inc
);
3670 inc
= att_align_nominal(inc
, typalign
);
3677 * Advance ptr over nitems array elements
3679 * ptr: starting location in array
3680 * offset: 0-based linear element number of first element (the one at *ptr)
3681 * nullbitmap: start of array's null bitmap, or NULL if none
3682 * nitems: number of array elements to advance over (>= 0)
3683 * typlen, typbyval, typalign: storage parameters of array element datatype
3685 * It is caller's responsibility to ensure that nitems is within range
3688 array_seek(char *ptr
, int offset
, bits8
*nullbitmap
, int nitems
,
3689 int typlen
, bool typbyval
, char typalign
)
3694 /* easy if fixed-size elements and no NULLs */
3695 if (typlen
> 0 && !nullbitmap
)
3696 return ptr
+ nitems
* ((Size
) att_align_nominal(typlen
, typalign
));
3698 /* seems worth having separate loops for NULL and no-NULLs cases */
3701 nullbitmap
+= offset
/ 8;
3702 bitmask
= 1 << (offset
% 8);
3704 for (i
= 0; i
< nitems
; i
++)
3706 if (*nullbitmap
& bitmask
)
3708 ptr
= att_addlength_pointer(ptr
, typlen
, ptr
);
3709 ptr
= (char *) att_align_nominal(ptr
, typalign
);
3712 if (bitmask
== 0x100)
3721 for (i
= 0; i
< nitems
; i
++)
3723 ptr
= att_addlength_pointer(ptr
, typlen
, ptr
);
3724 ptr
= (char *) att_align_nominal(ptr
, typalign
);
3731 * Compute total size of the nitems array elements starting at *ptr
3733 * Parameters same as for array_seek
3736 array_nelems_size(char *ptr
, int offset
, bits8
*nullbitmap
, int nitems
,
3737 int typlen
, bool typbyval
, char typalign
)
3739 return array_seek(ptr
, offset
, nullbitmap
, nitems
,
3740 typlen
, typbyval
, typalign
) - ptr
;
3744 * Copy nitems array elements from srcptr to destptr
3746 * destptr: starting destination location (must be enough room!)
3747 * nitems: number of array elements to copy (>= 0)
3748 * srcptr: starting location in source array
3749 * offset: 0-based linear element number of first element (the one at *srcptr)
3750 * nullbitmap: start of source array's null bitmap, or NULL if none
3751 * typlen, typbyval, typalign: storage parameters of array element datatype
3753 * Returns number of bytes copied
3755 * NB: this does not take care of setting up the destination's null bitmap!
3758 array_copy(char *destptr
, int nitems
,
3759 char *srcptr
, int offset
, bits8
*nullbitmap
,
3760 int typlen
, bool typbyval
, char typalign
)
3764 numbytes
= array_nelems_size(srcptr
, offset
, nullbitmap
, nitems
,
3765 typlen
, typbyval
, typalign
);
3766 memcpy(destptr
, srcptr
, numbytes
);
3771 * Copy nitems null-bitmap bits from source to destination
3773 * destbitmap: start of destination array's null bitmap (mustn't be NULL)
3774 * destoffset: 0-based linear element number of first dest element
3775 * srcbitmap: start of source array's null bitmap, or NULL if none
3776 * srcoffset: 0-based linear element number of first source element
3777 * nitems: number of bits to copy (>= 0)
3779 * If srcbitmap is NULL then we assume the source is all-non-NULL and
3780 * fill 1's into the destination bitmap. Note that only the specified
3781 * bits in the destination map are changed, not any before or after.
3783 * Note: this could certainly be optimized using standard bitblt methods.
3784 * However, it's not clear that the typical Postgres array has enough elements
3785 * to make it worth worrying too much. For the moment, KISS.
3788 array_bitmap_copy(bits8
*destbitmap
, int destoffset
,
3789 const bits8
*srcbitmap
, int srcoffset
,
3799 return; /* don't risk fetch off end of memory */
3800 destbitmap
+= destoffset
/ 8;
3801 destbitmask
= 1 << (destoffset
% 8);
3802 destbitval
= *destbitmap
;
3805 srcbitmap
+= srcoffset
/ 8;
3806 srcbitmask
= 1 << (srcoffset
% 8);
3807 srcbitval
= *srcbitmap
;
3808 while (nitems
-- > 0)
3810 if (srcbitval
& srcbitmask
)
3811 destbitval
|= destbitmask
;
3813 destbitval
&= ~destbitmask
;
3815 if (destbitmask
== 0x100)
3817 *destbitmap
++ = destbitval
;
3820 destbitval
= *destbitmap
;
3823 if (srcbitmask
== 0x100)
3828 srcbitval
= *srcbitmap
;
3831 if (destbitmask
!= 1)
3832 *destbitmap
= destbitval
;
3836 while (nitems
-- > 0)
3838 destbitval
|= destbitmask
;
3840 if (destbitmask
== 0x100)
3842 *destbitmap
++ = destbitval
;
3845 destbitval
= *destbitmap
;
3848 if (destbitmask
!= 1)
3849 *destbitmap
= destbitval
;
3854 * Compute space needed for a slice of an array
3856 * We assume the caller has verified that the slice coordinates are valid.
3859 array_slice_size(char *arraydataptr
, bits8
*arraynullsptr
,
3860 int ndim
, int *dim
, int *lb
,
3862 int typlen
, bool typbyval
, char typalign
)
3875 mda_get_range(ndim
, span
, st
, endp
);
3877 /* Pretty easy for fixed element length without nulls ... */
3878 if (typlen
> 0 && !arraynullsptr
)
3879 return ArrayGetNItems(ndim
, span
) * att_align_nominal(typlen
, typalign
);
3881 /* Else gotta do it the hard way */
3882 src_offset
= ArrayGetOffset(ndim
, dim
, lb
, st
);
3883 ptr
= array_seek(arraydataptr
, 0, arraynullsptr
, src_offset
,
3884 typlen
, typbyval
, typalign
);
3885 mda_get_prod(ndim
, dim
, prod
);
3886 mda_get_offset_values(ndim
, dist
, prod
, span
);
3887 for (i
= 0; i
< ndim
; i
++)
3894 ptr
= array_seek(ptr
, src_offset
, arraynullsptr
, dist
[j
],
3895 typlen
, typbyval
, typalign
);
3896 src_offset
+= dist
[j
];
3898 if (!array_get_isnull(arraynullsptr
, src_offset
))
3900 inc
= att_addlength_pointer(0, typlen
, ptr
);
3901 inc
= att_align_nominal(inc
, typalign
);
3906 } while ((j
= mda_next_tuple(ndim
, indx
, span
)) != -1);
3911 * Extract a slice of an array into consecutive elements in the destination
3914 * We assume the caller has verified that the slice coordinates are valid,
3915 * allocated enough storage for the result, and initialized the header
3919 array_extract_slice(ArrayType
*newarray
,
3924 bits8
*arraynullsptr
,
3931 char *destdataptr
= ARR_DATA_PTR(newarray
);
3932 bits8
*destnullsptr
= ARR_NULLBITMAP(newarray
);
3944 src_offset
= ArrayGetOffset(ndim
, dim
, lb
, st
);
3945 srcdataptr
= array_seek(arraydataptr
, 0, arraynullsptr
, src_offset
,
3946 typlen
, typbyval
, typalign
);
3947 mda_get_prod(ndim
, dim
, prod
);
3948 mda_get_range(ndim
, span
, st
, endp
);
3949 mda_get_offset_values(ndim
, dist
, prod
, span
);
3950 for (i
= 0; i
< ndim
; i
++)
3958 /* skip unwanted elements */
3959 srcdataptr
= array_seek(srcdataptr
, src_offset
, arraynullsptr
,
3961 typlen
, typbyval
, typalign
);
3962 src_offset
+= dist
[j
];
3964 inc
= array_copy(destdataptr
, 1,
3965 srcdataptr
, src_offset
, arraynullsptr
,
3966 typlen
, typbyval
, typalign
);
3968 array_bitmap_copy(destnullsptr
, dest_offset
,
3969 arraynullsptr
, src_offset
,
3975 } while ((j
= mda_next_tuple(ndim
, indx
, span
)) != -1);
3979 * Insert a slice into an array.
3981 * ndim/dim[]/lb[] are dimensions of the original array. A new array with
3982 * those same dimensions is to be constructed. destArray must already
3983 * have been allocated and its header initialized.
3985 * st[]/endp[] identify the slice to be replaced. Elements within the slice
3986 * volume are taken from consecutive elements of the srcArray; elements
3987 * outside it are copied from origArray.
3989 * We assume the caller has verified that the slice coordinates are valid.
3992 array_insert_slice(ArrayType
*destArray
,
3993 ArrayType
*origArray
,
3994 ArrayType
*srcArray
,
4004 char *destPtr
= ARR_DATA_PTR(destArray
);
4005 char *origPtr
= ARR_DATA_PTR(origArray
);
4006 char *srcPtr
= ARR_DATA_PTR(srcArray
);
4007 bits8
*destBitmap
= ARR_NULLBITMAP(destArray
);
4008 bits8
*origBitmap
= ARR_NULLBITMAP(origArray
);
4009 bits8
*srcBitmap
= ARR_NULLBITMAP(srcArray
);
4010 int orignitems
= ArrayGetNItems(ARR_NDIM(origArray
),
4011 ARR_DIMS(origArray
));
4023 dest_offset
= ArrayGetOffset(ndim
, dim
, lb
, st
);
4024 /* copy items before the slice start */
4025 inc
= array_copy(destPtr
, dest_offset
,
4026 origPtr
, 0, origBitmap
,
4027 typlen
, typbyval
, typalign
);
4031 array_bitmap_copy(destBitmap
, 0, origBitmap
, 0, dest_offset
);
4032 orig_offset
= dest_offset
;
4033 mda_get_prod(ndim
, dim
, prod
);
4034 mda_get_range(ndim
, span
, st
, endp
);
4035 mda_get_offset_values(ndim
, dist
, prod
, span
);
4036 for (i
= 0; i
< ndim
; i
++)
4042 /* Copy/advance over elements between here and next part of slice */
4045 inc
= array_copy(destPtr
, dist
[j
],
4046 origPtr
, orig_offset
, origBitmap
,
4047 typlen
, typbyval
, typalign
);
4051 array_bitmap_copy(destBitmap
, dest_offset
,
4052 origBitmap
, orig_offset
,
4054 dest_offset
+= dist
[j
];
4055 orig_offset
+= dist
[j
];
4057 /* Copy new element at this slice position */
4058 inc
= array_copy(destPtr
, 1,
4059 srcPtr
, src_offset
, srcBitmap
,
4060 typlen
, typbyval
, typalign
);
4062 array_bitmap_copy(destBitmap
, dest_offset
,
4063 srcBitmap
, src_offset
,
4069 /* Advance over old element at this slice position */
4070 origPtr
= array_seek(origPtr
, orig_offset
, origBitmap
, 1,
4071 typlen
, typbyval
, typalign
);
4073 } while ((j
= mda_next_tuple(ndim
, indx
, span
)) != -1);
4075 /* don't miss any data at the end */
4076 array_copy(destPtr
, orignitems
- orig_offset
,
4077 origPtr
, orig_offset
, origBitmap
,
4078 typlen
, typbyval
, typalign
);
4080 array_bitmap_copy(destBitmap
, dest_offset
,
4081 origBitmap
, orig_offset
,
4082 orignitems
- orig_offset
);
4086 * accumArrayResult - accumulate one (more) Datum for an array result
4088 * astate is working state (NULL on first call)
4089 * rcontext is where to keep working state
4092 accumArrayResult(ArrayBuildState
*astate
,
4093 Datum dvalue
, bool disnull
,
4095 MemoryContext rcontext
)
4097 MemoryContext arr_context
,
4102 /* First time through --- initialize */
4104 /* Make a temporary context to hold all the junk */
4105 arr_context
= AllocSetContextCreate(rcontext
,
4107 ALLOCSET_DEFAULT_MINSIZE
,
4108 ALLOCSET_DEFAULT_INITSIZE
,
4109 ALLOCSET_DEFAULT_MAXSIZE
);
4110 oldcontext
= MemoryContextSwitchTo(arr_context
);
4111 astate
= (ArrayBuildState
*) palloc(sizeof(ArrayBuildState
));
4112 astate
->mcontext
= arr_context
;
4113 astate
->alen
= 64; /* arbitrary starting array size */
4114 astate
->dvalues
= (Datum
*) palloc(astate
->alen
* sizeof(Datum
));
4115 astate
->dnulls
= (bool *) palloc(astate
->alen
* sizeof(bool));
4117 astate
->element_type
= element_type
;
4118 get_typlenbyvalalign(element_type
,
4125 oldcontext
= MemoryContextSwitchTo(astate
->mcontext
);
4126 Assert(astate
->element_type
== element_type
);
4127 /* enlarge dvalues[]/dnulls[] if needed */
4128 if (astate
->nelems
>= astate
->alen
)
4131 astate
->dvalues
= (Datum
*)
4132 repalloc(astate
->dvalues
, astate
->alen
* sizeof(Datum
));
4133 astate
->dnulls
= (bool *)
4134 repalloc(astate
->dnulls
, astate
->alen
* sizeof(bool));
4138 /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
4139 if (!disnull
&& !astate
->typbyval
)
4140 dvalue
= datumCopy(dvalue
, astate
->typbyval
, astate
->typlen
);
4142 astate
->dvalues
[astate
->nelems
] = dvalue
;
4143 astate
->dnulls
[astate
->nelems
] = disnull
;
4146 MemoryContextSwitchTo(oldcontext
);
4152 * makeArrayResult - produce 1-D final result of accumArrayResult
4154 * astate is working state (not NULL)
4155 * rcontext is where to construct result
4158 makeArrayResult(ArrayBuildState
*astate
,
4159 MemoryContext rcontext
)
4164 dims
[0] = astate
->nelems
;
4167 return makeMdArrayResult(astate
, 1, dims
, lbs
, rcontext
);
4171 * makeMdArrayResult - produce multi-D final result of accumArrayResult
4173 * beware: no check that specified dimensions match the number of values
4176 * astate is working state (not NULL)
4177 * rcontext is where to construct result
4180 makeMdArrayResult(ArrayBuildState
*astate
,
4184 MemoryContext rcontext
)
4187 MemoryContext oldcontext
;
4189 /* Build the final array result in rcontext */
4190 oldcontext
= MemoryContextSwitchTo(rcontext
);
4192 result
= construct_md_array(astate
->dvalues
,
4197 astate
->element_type
,
4202 MemoryContextSwitchTo(oldcontext
);
4204 /* Clean up all the junk */
4205 MemoryContextDelete(astate
->mcontext
);
4207 return PointerGetDatum(result
);
4211 array_larger(PG_FUNCTION_ARGS
)
4217 v1
= PG_GETARG_ARRAYTYPE_P(0);
4218 v2
= PG_GETARG_ARRAYTYPE_P(1);
4220 result
= ((array_cmp(fcinfo
) > 0) ? v1
: v2
);
4222 PG_RETURN_ARRAYTYPE_P(result
);
4226 array_smaller(PG_FUNCTION_ARGS
)
4232 v1
= PG_GETARG_ARRAYTYPE_P(0);
4233 v2
= PG_GETARG_ARRAYTYPE_P(1);
4235 result
= ((array_cmp(fcinfo
) < 0) ? v1
: v2
);
4237 PG_RETURN_ARRAYTYPE_P(result
);
4241 typedef struct generate_subscripts_fctx
4246 } generate_subscripts_fctx
;
4249 * generate_subscripts(array anyarray, dim int [, reverse bool])
4250 * Returns all subscripts of the array for any dimension
4253 generate_subscripts(PG_FUNCTION_ARGS
)
4255 FuncCallContext
*funcctx
;
4256 MemoryContext oldcontext
;
4257 generate_subscripts_fctx
*fctx
;
4259 /* stuff done only on the first call of the function */
4260 if (SRF_IS_FIRSTCALL())
4262 ArrayType
*v
= PG_GETARG_ARRAYTYPE_P(0);
4263 int reqdim
= PG_GETARG_INT32(1);
4267 /* create a function context for cross-call persistence */
4268 funcctx
= SRF_FIRSTCALL_INIT();
4270 /* Sanity check: does it look like an array at all? */
4271 if (ARR_NDIM(v
) <= 0 || ARR_NDIM(v
) > MAXDIM
)
4272 SRF_RETURN_DONE(funcctx
);
4274 /* Sanity check: was the requested dim valid */
4275 if (reqdim
<= 0 || reqdim
> ARR_NDIM(v
))
4276 SRF_RETURN_DONE(funcctx
);
4279 * switch to memory context appropriate for multiple function calls
4281 oldcontext
= MemoryContextSwitchTo(funcctx
->multi_call_memory_ctx
);
4282 fctx
= (generate_subscripts_fctx
*) palloc(sizeof(generate_subscripts_fctx
));
4287 fctx
->lower
= lb
[reqdim
- 1];
4288 fctx
->upper
= dimv
[reqdim
- 1] + lb
[reqdim
- 1] - 1;
4289 fctx
->reverse
= (PG_NARGS() < 3) ? false : PG_GETARG_BOOL(2);
4291 funcctx
->user_fctx
= fctx
;
4293 MemoryContextSwitchTo(oldcontext
);
4296 funcctx
= SRF_PERCALL_SETUP();
4298 fctx
= funcctx
->user_fctx
;
4300 if (fctx
->lower
<= fctx
->upper
)
4303 SRF_RETURN_NEXT(funcctx
, Int32GetDatum(fctx
->lower
++));
4305 SRF_RETURN_NEXT(funcctx
, Int32GetDatum(fctx
->upper
--));
4308 /* done when there are no more elements left */
4309 SRF_RETURN_DONE(funcctx
);
4313 * generate_subscripts_nodir
4314 * Implements the 2-argument version of generate_subscripts
4317 generate_subscripts_nodir(PG_FUNCTION_ARGS
)
4319 /* just call the other one -- it can handle both cases */
4320 return generate_subscripts(fcinfo
);
4324 * array_fill_with_lower_bounds
4325 * Create and fill array with defined lower bounds.
4328 array_fill_with_lower_bounds(PG_FUNCTION_ARGS
)
4337 if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
4339 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED
),
4340 errmsg("dimension array or low bound array cannot be NULL")));
4342 dims
= PG_GETARG_ARRAYTYPE_P(1);
4343 lbs
= PG_GETARG_ARRAYTYPE_P(2);
4345 if (!PG_ARGISNULL(0))
4347 value
= PG_GETARG_DATUM(0);
4356 elmtype
= get_fn_expr_argtype(fcinfo
->flinfo
, 0);
4357 if (!OidIsValid(elmtype
))
4358 elog(ERROR
, "could not determine data type of input");
4360 result
= array_fill_internal(dims
, lbs
, value
, isnull
, elmtype
, fcinfo
);
4361 PG_RETURN_ARRAYTYPE_P(result
);
4366 * Create and fill array with default lower bounds.
4369 array_fill(PG_FUNCTION_ARGS
)
4377 if (PG_ARGISNULL(1))
4379 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED
),
4380 errmsg("dimension array or low bound array cannot be NULL")));
4382 dims
= PG_GETARG_ARRAYTYPE_P(1);
4384 if (!PG_ARGISNULL(0))
4386 value
= PG_GETARG_DATUM(0);
4395 elmtype
= get_fn_expr_argtype(fcinfo
->flinfo
, 0);
4396 if (!OidIsValid(elmtype
))
4397 elog(ERROR
, "could not determine data type of input");
4399 result
= array_fill_internal(dims
, NULL
, value
, isnull
, elmtype
, fcinfo
);
4400 PG_RETURN_ARRAYTYPE_P(result
);
4404 create_array_envelope(int ndims
, int *dimv
, int *lbsv
, int nbytes
,
4405 Oid elmtype
, int dataoffset
)
4409 result
= (ArrayType
*) palloc0(nbytes
);
4410 SET_VARSIZE(result
, nbytes
);
4411 result
->ndim
= ndims
;
4412 result
->dataoffset
= dataoffset
;
4413 result
->elemtype
= elmtype
;
4414 memcpy(ARR_DIMS(result
), dimv
, ndims
* sizeof(int));
4415 memcpy(ARR_LBOUND(result
), lbsv
, ndims
* sizeof(int));
4421 array_fill_internal(ArrayType
*dims
, ArrayType
*lbs
,
4422 Datum value
, bool isnull
, Oid elmtype
,
4423 FunctionCallInfo fcinfo
)
4434 ArrayMetaState
*my_extra
;
4439 if (ARR_NDIM(dims
) != 1)
4441 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
4442 errmsg("wrong number of array subscripts"),
4443 errdetail("Dimension array must be one dimensional.")));
4445 if (ARR_LBOUND(dims
)[0] != 1)
4447 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
4448 errmsg("wrong range of array_subscripts"),
4449 errdetail("Lower bound of dimension array must be one.")));
4451 if (ARR_HASNULL(dims
))
4453 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED
),
4454 errmsg("dimension values cannot be null")));
4456 dimv
= (int *) ARR_DATA_PTR(dims
);
4457 ndims
= ARR_DIMS(dims
)[0];
4459 if (ndims
< 0) /* we do allow zero-dimension arrays */
4461 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
4462 errmsg("invalid number of dimensions: %d", ndims
)));
4465 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
4466 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
4471 if (ARR_NDIM(lbs
) != 1)
4473 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
4474 errmsg("wrong number of array subscripts"),
4475 errdetail("Dimension array must be one dimensional.")));
4477 if (ARR_LBOUND(lbs
)[0] != 1)
4479 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
4480 errmsg("wrong range of array_subscripts"),
4481 errdetail("Lower bound of dimension array must be one.")));
4483 if (ARR_HASNULL(lbs
))
4485 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED
),
4486 errmsg("dimension values cannot be null")));
4488 if (ARR_DIMS(lbs
)[0] != ndims
)
4490 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
4491 errmsg("wrong number of array_subscripts"),
4492 errdetail("Low bound array has different size than dimensions array.")));
4494 lbsv
= (int *) ARR_DATA_PTR(lbs
);
4500 for (i
= 0; i
< MAXDIM
; i
++)
4506 /* fast track for empty array */
4508 return construct_empty_array(elmtype
);
4510 nitems
= ArrayGetNItems(ndims
, dimv
);
4513 * We arrange to look up info about element type only once per series of
4514 * calls, assuming the element type doesn't change underneath us.
4516 my_extra
= (ArrayMetaState
*) fcinfo
->flinfo
->fn_extra
;
4517 if (my_extra
== NULL
)
4519 fcinfo
->flinfo
->fn_extra
= MemoryContextAlloc(fcinfo
->flinfo
->fn_mcxt
,
4520 sizeof(ArrayMetaState
));
4521 my_extra
= (ArrayMetaState
*) fcinfo
->flinfo
->fn_extra
;
4522 my_extra
->element_type
= InvalidOid
;
4525 if (my_extra
->element_type
!= elmtype
)
4527 /* Get info about element type */
4528 get_typlenbyvalalign(elmtype
,
4530 &my_extra
->typbyval
,
4531 &my_extra
->typalign
);
4532 my_extra
->element_type
= elmtype
;
4535 elmlen
= my_extra
->typlen
;
4536 elmbyval
= my_extra
->typbyval
;
4537 elmalign
= my_extra
->typalign
;
4539 /* compute required space */
4547 /* make sure data is not toasted */
4549 value
= PointerGetDatum(PG_DETOAST_DATUM(value
));
4551 nbytes
= att_addlength_datum(0, elmlen
, value
);
4552 nbytes
= att_align_nominal(nbytes
, elmalign
);
4555 totbytes
= nbytes
* nitems
;
4557 /* check for overflow of multiplication or total request */
4558 if (totbytes
/ nbytes
!= nitems
||
4559 !AllocSizeIsValid(totbytes
))
4561 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
4562 errmsg("array size exceeds the maximum allowed (%d)",
4563 (int) MaxAllocSize
)));
4566 * This addition can't overflow, but it might cause us to go past
4567 * MaxAllocSize. We leave it to palloc to complain in that case.
4569 totbytes
+= ARR_OVERHEAD_NONULLS(ndims
);
4571 result
= create_array_envelope(ndims
, dimv
, lbsv
, totbytes
,
4574 p
= ARR_DATA_PTR(result
);
4575 for (i
= 0; i
< nitems
; i
++)
4576 p
+= ArrayCastAndSet(value
, elmlen
, elmbyval
, elmalign
, p
);
4583 dataoffset
= ARR_OVERHEAD_WITHNULLS(ndims
, nitems
);
4584 nbytes
= dataoffset
;
4586 result
= create_array_envelope(ndims
, dimv
, lbsv
, nbytes
,
4587 elmtype
, dataoffset
);
4589 /* create_array_envelope already zeroed the bitmap, so we're done */