1
/*-------------------------------------------------------------------------
4
* Support functions for arrays.
6
* Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
7
* Portions Copyright (c) 1994, Regents of the University of California
11
* src/backend/utils/adt/arrayfuncs.c
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
/* Working state for array_iterate() */
54
typedef struct ArrayIteratorData
56
/* basic info about the array, set up during array_create_iterator() */
57
ArrayType *arr; /* array we're iterating through */
58
bits8 *nullbitmap; /* its null bitmap, if any */
59
int nitems; /* total number of elements in array */
60
int16 typlen; /* element type's length */
61
bool typbyval; /* element type's byval property */
62
char typalign; /* element type's align property */
64
/* information about the requested slice size */
65
int slice_ndim; /* slice dimension, or 0 if not slicing */
66
int slice_len; /* number of elements per slice */
67
int *slice_dims; /* slice dims array */
68
int *slice_lbound; /* slice lbound array */
69
Datum *slice_values; /* workspace of length slice_len */
70
bool *slice_nulls; /* workspace of length slice_len */
72
/* current position information, updated on each iteration */
73
char *data_ptr; /* our current position in the array */
74
int current_item; /* the item # we're at in the array */
77
static bool array_isspace(char ch);
78
static int ArrayCount(const char *str, int *dim, char typdelim);
79
static void ReadArrayStr(char *arrayStr, const char *origStr,
80
int nitems, int ndim, int *dim,
81
FmgrInfo *inputproc, Oid typioparam, int32 typmod,
83
int typlen, bool typbyval, char typalign,
84
Datum *values, bool *nulls,
85
bool *hasnulls, int32 *nbytes);
86
static void ReadArrayBinary(StringInfo buf, int nitems,
87
FmgrInfo *receiveproc, Oid typioparam, int32 typmod,
88
int typlen, bool typbyval, char typalign,
89
Datum *values, bool *nulls,
90
bool *hasnulls, int32 *nbytes);
91
static void CopyArrayEls(ArrayType *array,
92
Datum *values, bool *nulls, int nitems,
93
int typlen, bool typbyval, char typalign,
95
static bool array_get_isnull(const bits8 *nullbitmap, int offset);
96
static void array_set_isnull(bits8 *nullbitmap, int offset, bool isNull);
97
static Datum ArrayCast(char *value, bool byval, int len);
98
static int ArrayCastAndSet(Datum src,
99
int typlen, bool typbyval, char typalign,
101
static char *array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
102
int typlen, bool typbyval, char typalign);
103
static int array_nelems_size(char *ptr, int offset, bits8 *nullbitmap,
104
int nitems, int typlen, bool typbyval, char typalign);
105
static int array_copy(char *destptr, int nitems,
106
char *srcptr, int offset, bits8 *nullbitmap,
107
int typlen, bool typbyval, char typalign);
108
static int array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
109
int ndim, int *dim, int *lb,
111
int typlen, bool typbyval, char typalign);
112
static void array_extract_slice(ArrayType *newarray,
113
int ndim, int *dim, int *lb,
114
char *arraydataptr, bits8 *arraynullsptr,
116
int typlen, bool typbyval, char typalign);
117
static void array_insert_slice(ArrayType *destArray, ArrayType *origArray,
119
int ndim, int *dim, int *lb,
121
int typlen, bool typbyval, char typalign);
122
static int array_cmp(FunctionCallInfo fcinfo);
123
static ArrayType *create_array_envelope(int ndims, int *dimv, int *lbv, int nbytes,
124
Oid elmtype, int dataoffset);
125
static ArrayType *array_fill_internal(ArrayType *dims, ArrayType *lbs,
126
Datum value, bool isnull, Oid elmtype,
127
FunctionCallInfo fcinfo);
132
* converts an array from the external format in "string" to
133
* its internal format.
136
* the internal representation of the input array
139
array_in(PG_FUNCTION_ARGS)
141
char *string = PG_GETARG_CSTRING(0); /* external form */
142
Oid element_type = PG_GETARG_OID(1); /* type of an array
144
int32 typmod = PG_GETARG_INT32(2); /* typmod for array elements */
163
ArrayMetaState *my_extra;
166
* We arrange to look up info about element type, including its input
167
* conversion proc, only once per series of calls, assuming the element
168
* type doesn't change underneath us.
170
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
171
if (my_extra == NULL)
173
fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
174
sizeof(ArrayMetaState));
175
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
176
my_extra->element_type = ~element_type;
179
if (my_extra->element_type != element_type)
182
* Get info about element type, including its input conversion proc
184
get_type_io_data(element_type, IOFunc_input,
185
&my_extra->typlen, &my_extra->typbyval,
186
&my_extra->typalign, &my_extra->typdelim,
187
&my_extra->typioparam, &my_extra->typiofunc);
188
fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
189
fcinfo->flinfo->fn_mcxt);
190
my_extra->element_type = element_type;
192
typlen = my_extra->typlen;
193
typbyval = my_extra->typbyval;
194
typalign = my_extra->typalign;
195
typdelim = my_extra->typdelim;
196
typioparam = my_extra->typioparam;
198
/* Make a modifiable copy of the input */
199
string_save = pstrdup(string);
202
* If the input string starts with dimension info, read and use that.
203
* Otherwise, we require the input to be in curly-brace style, and we
204
* prescan the input to determine dimensions.
206
* Dimension info takes the form of one or more [n] or [m:n] items. The
207
* outer loop iterates once per dimension item.
217
* Note: we currently allow whitespace between, but not within,
220
while (array_isspace(*p))
223
break; /* no more dimension items */
227
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
228
errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
231
for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++);
232
if (q == p) /* no digits? */
234
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
235
errmsg("missing dimension value")));
241
lBound[ndim] = atoi(p);
243
for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++);
244
if (q == p) /* no digits? */
246
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
247
errmsg("missing dimension value")));
256
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
257
errmsg("missing \"]\" in array dimensions")));
262
if (ub < lBound[ndim])
264
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
265
errmsg("upper bound cannot be less than lower bound")));
267
dim[ndim] = ub - lBound[ndim] + 1;
273
/* No array dimensions, so intuit dimensions from brace structure */
276
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
277
errmsg("array value must start with \"{\" or dimension information")));
278
ndim = ArrayCount(p, dim, typdelim);
279
for (i = 0; i < ndim; i++)
287
/* If array dimensions are given, expect '=' operator */
288
if (strncmp(p, ASSGN, strlen(ASSGN)) != 0)
290
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
291
errmsg("missing assignment operator")));
293
while (array_isspace(*p))
297
* intuit dimensions from brace structure -- it better match what we
302
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
303
errmsg("array value must start with \"{\" or dimension information")));
304
ndim_braces = ArrayCount(p, dim_braces, typdelim);
305
if (ndim_braces != ndim)
307
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
308
errmsg("array dimensions incompatible with array literal")));
309
for (i = 0; i < ndim; ++i)
311
if (dim[i] != dim_braces[i])
313
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
314
errmsg("array dimensions incompatible with array literal")));
319
printf("array_in- ndim %d (", ndim);
320
for (i = 0; i < ndim; i++)
322
printf(" %d", dim[i]);
324
printf(") for %s\n", string);
327
/* This checks for overflow of the array dimensions */
328
nitems = ArrayGetNItems(ndim, dim);
331
PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
333
dataPtr = (Datum *) palloc(nitems * sizeof(Datum));
334
nullsPtr = (bool *) palloc(nitems * sizeof(bool));
335
ReadArrayStr(p, string,
337
&my_extra->proc, typioparam, typmod,
339
typlen, typbyval, typalign,
344
dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
345
nbytes += dataoffset;
349
dataoffset = 0; /* marker for no null bitmap */
350
nbytes += ARR_OVERHEAD_NONULLS(ndim);
352
retval = (ArrayType *) palloc0(nbytes);
353
SET_VARSIZE(retval, nbytes);
355
retval->dataoffset = dataoffset;
358
* This comes from the array's pg_type.typelem (which points to the base
359
* data type's pg_type.oid) and stores system oids in user tables. This
360
* oid must be preserved by binary upgrades.
362
retval->elemtype = element_type;
363
memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
364
memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
367
dataPtr, nullsPtr, nitems,
368
typlen, typbyval, typalign,
375
PG_RETURN_ARRAYTYPE_P(retval);
379
* array_isspace() --- a non-locale-dependent isspace()
381
* We used to use isspace() for parsing array values, but that has
382
* undesirable results: an array value might be silently interpreted
383
* differently depending on the locale setting. Now we just hard-wire
384
* the traditional ASCII definition of isspace().
387
array_isspace(char ch)
401
* Determines the dimensions for an array string.
403
* Returns number of dimensions as function result. The axis lengths are
404
* returned in dim[], which must be of size MAXDIM.
407
ArrayCount(const char *str, int *dim, char typdelim)
415
bool in_quotes = false;
416
bool eoArray = false;
417
bool empty_array = true;
419
ArrayParseState parse_state = ARRAY_NO_LEVEL;
421
for (i = 0; i < MAXDIM; ++i)
423
temp[i] = dim[i] = 0;
424
nelems_last[i] = nelems[i] = 1;
430
bool itemdone = false;
434
if (parse_state == ARRAY_ELEM_STARTED ||
435
parse_state == ARRAY_QUOTED_ELEM_STARTED)
441
/* Signal a premature end of the string */
443
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
444
errmsg("malformed array literal: \"%s\"", str)));
449
* An escape must be after a level start, after an element
450
* start, or after an element delimiter. In any case we
451
* now must be past an element start.
453
if (parse_state != ARRAY_LEVEL_STARTED &&
454
parse_state != ARRAY_ELEM_STARTED &&
455
parse_state != ARRAY_QUOTED_ELEM_STARTED &&
456
parse_state != ARRAY_ELEM_DELIMITED)
458
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
459
errmsg("malformed array literal: \"%s\"", str)));
460
if (parse_state != ARRAY_QUOTED_ELEM_STARTED)
461
parse_state = ARRAY_ELEM_STARTED;
462
/* skip the escaped character */
467
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
468
errmsg("malformed array literal: \"%s\"", str)));
473
* A quote must be after a level start, after a quoted
474
* element start, or after an element delimiter. In any
475
* case we now must be past an element start.
477
if (parse_state != ARRAY_LEVEL_STARTED &&
478
parse_state != ARRAY_QUOTED_ELEM_STARTED &&
479
parse_state != ARRAY_ELEM_DELIMITED)
481
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
482
errmsg("malformed array literal: \"%s\"", str)));
483
in_quotes = !in_quotes;
485
parse_state = ARRAY_QUOTED_ELEM_STARTED;
487
parse_state = ARRAY_QUOTED_ELEM_COMPLETED;
493
* A left brace can occur if no nesting has occurred
494
* yet, after a level start, or after a level
497
if (parse_state != ARRAY_NO_LEVEL &&
498
parse_state != ARRAY_LEVEL_STARTED &&
499
parse_state != ARRAY_LEVEL_DELIMITED)
501
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
502
errmsg("malformed array literal: \"%s\"", str)));
503
parse_state = ARRAY_LEVEL_STARTED;
504
if (nest_level >= MAXDIM)
506
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
507
errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
508
nest_level + 1, MAXDIM)));
509
temp[nest_level] = 0;
511
if (ndim < nest_level)
519
* A right brace can occur after an element start, an
520
* element completion, a quoted element completion, or
521
* a level completion.
523
if (parse_state != ARRAY_ELEM_STARTED &&
524
parse_state != ARRAY_ELEM_COMPLETED &&
525
parse_state != ARRAY_QUOTED_ELEM_COMPLETED &&
526
parse_state != ARRAY_LEVEL_COMPLETED &&
527
!(nest_level == 1 && parse_state == ARRAY_LEVEL_STARTED))
529
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
530
errmsg("malformed array literal: \"%s\"", str)));
531
parse_state = ARRAY_LEVEL_COMPLETED;
534
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
535
errmsg("malformed array literal: \"%s\"", str)));
538
if ((nelems_last[nest_level] != 1) &&
539
(nelems[nest_level] != nelems_last[nest_level]))
541
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
542
errmsg("multidimensional arrays must have "
543
"array expressions with matching "
545
nelems_last[nest_level] = nelems[nest_level];
546
nelems[nest_level] = 1;
548
eoArray = itemdone = true;
552
* We don't set itemdone here; see comments in
555
temp[nest_level - 1]++;
562
if (*ptr == typdelim)
565
* Delimiters can occur after an element start, an
566
* element completion, a quoted element
567
* completion, or a level completion.
569
if (parse_state != ARRAY_ELEM_STARTED &&
570
parse_state != ARRAY_ELEM_COMPLETED &&
571
parse_state != ARRAY_QUOTED_ELEM_COMPLETED &&
572
parse_state != ARRAY_LEVEL_COMPLETED)
574
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
575
errmsg("malformed array literal: \"%s\"", str)));
576
if (parse_state == ARRAY_LEVEL_COMPLETED)
577
parse_state = ARRAY_LEVEL_DELIMITED;
579
parse_state = ARRAY_ELEM_DELIMITED;
581
nelems[nest_level - 1]++;
583
else if (!array_isspace(*ptr))
586
* Other non-space characters must be after a
587
* level start, after an element start, or after
588
* an element delimiter. In any case we now must
589
* be past an element start.
591
if (parse_state != ARRAY_LEVEL_STARTED &&
592
parse_state != ARRAY_ELEM_STARTED &&
593
parse_state != ARRAY_ELEM_DELIMITED)
595
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
596
errmsg("malformed array literal: \"%s\"", str)));
597
parse_state = ARRAY_ELEM_STARTED;
609
/* only whitespace is allowed after the closing brace */
612
if (!array_isspace(*ptr++))
614
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
615
errmsg("malformed array literal: \"%s\"", str)));
618
/* special case for an empty array */
622
for (i = 0; i < ndim; ++i)
630
* parses the array string pointed to by "arrayStr" and converts the values
631
* to internal format. Unspecified elements are initialized to nulls.
632
* The array dimensions must already have been determined.
635
* arrayStr: the string to parse.
636
* CAUTION: the contents of "arrayStr" will be modified!
637
* origStr: the unmodified input string, used only in error messages.
638
* nitems: total number of array elements, as already determined.
639
* ndim: number of array dimensions
640
* dim[]: array axis lengths
641
* inputproc: type-specific input procedure for element datatype.
642
* typioparam, typmod: auxiliary values to pass to inputproc.
643
* typdelim: the value delimiter (type-specific).
644
* typlen, typbyval, typalign: storage parameters of element datatype.
647
* values[]: filled with converted data values.
648
* nulls[]: filled with is-null markers.
649
* *hasnulls: set TRUE iff there are any null elements.
650
* *nbytes: set to total size of data area needed (including alignment
651
* padding but not including array header overhead).
653
* Note that values[] and nulls[] are allocated by the caller, and must have
657
ReadArrayStr(char *arrayStr,
677
bool in_quotes = false;
678
bool eoArray = false;
684
mda_get_prod(ndim, dim, prod);
685
MemSet(indx, 0, sizeof(indx));
687
/* Initialize is-null markers to true */
688
memset(nulls, true, nitems * sizeof(bool));
691
* We have to remove " and \ characters to create a clean item value to
692
* pass to the datatype input routine. We overwrite each item value
693
* in-place within arrayStr to do this. srcptr is the current scan point,
694
* and dstptr is where we are copying to.
696
* We also want to suppress leading and trailing unquoted whitespace. We
697
* use the leadingspace flag to suppress leading space. Trailing space is
698
* tracked by using dstendptr to point to the last significant output
701
* The error checking in this routine is mostly pro-forma, since we expect
702
* that ArrayCount() already validated the string.
707
bool itemdone = false;
708
bool leadingspace = true;
709
bool hasquoting = false;
715
itemstart = dstptr = dstendptr = srcptr;
722
/* Signal a premature end of the string */
724
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
725
errmsg("malformed array literal: \"%s\"",
729
/* Skip backslash, copy next character as-is. */
733
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
734
errmsg("malformed array literal: \"%s\"",
736
*dstptr++ = *srcptr++;
737
/* Treat the escaped character as non-whitespace */
738
leadingspace = false;
740
hasquoting = true; /* can't be a NULL marker */
743
in_quotes = !in_quotes;
745
leadingspace = false;
749
* Advance dstendptr when we exit in_quotes; this
750
* saves having to do it in all the other in_quotes
755
hasquoting = true; /* can't be a NULL marker */
761
if (nest_level >= ndim)
763
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
764
errmsg("malformed array literal: \"%s\"",
767
indx[nest_level - 1] = 0;
771
*dstptr++ = *srcptr++;
778
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
779
errmsg("malformed array literal: \"%s\"",
782
i = ArrayGetOffset0(ndim, indx, prod);
783
indx[nest_level - 1] = 0;
786
eoArray = itemdone = true;
788
indx[nest_level - 1]++;
792
*dstptr++ = *srcptr++;
796
*dstptr++ = *srcptr++;
797
else if (*srcptr == typdelim)
800
i = ArrayGetOffset0(ndim, indx, prod);
805
else if (array_isspace(*srcptr))
808
* If leading space, drop it immediately. Else, copy
809
* but don't advance dstendptr.
814
*dstptr++ = *srcptr++;
818
*dstptr++ = *srcptr++;
819
leadingspace = false;
826
Assert(dstptr < srcptr);
829
if (i < 0 || i >= nitems)
831
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
832
errmsg("malformed array literal: \"%s\"",
835
if (Array_nulls && !hasquoting &&
836
pg_strcasecmp(itemstart, "NULL") == 0)
838
/* it's a NULL item */
839
values[i] = InputFunctionCall(inputproc, NULL,
845
values[i] = InputFunctionCall(inputproc, itemstart,
852
* Check for nulls, compute total data space needed
856
for (i = 0; i < nitems; i++)
862
/* let's just make sure data is not toasted */
864
values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
865
totbytes = att_addlength_datum(totbytes, typlen, values[i]);
866
totbytes = att_align_nominal(totbytes, typalign);
867
/* check for overflow of total request */
868
if (!AllocSizeIsValid(totbytes))
870
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
871
errmsg("array size exceeds the maximum allowed (%d)",
872
(int) MaxAllocSize)));
881
* Copy data into an array object from a temporary array of Datums.
883
* array: array object (with header fields already filled in)
884
* values: array of Datums to be copied
885
* nulls: array of is-null flags (can be NULL if no nulls)
886
* nitems: number of Datums to be copied
887
* typbyval, typlen, typalign: info about element datatype
888
* freedata: if TRUE and element type is pass-by-ref, pfree data values
889
* referenced by Datums after copying them.
891
* If the input data is of varlena type, the caller must have ensured that
892
* the values are not toasted. (Doing it here doesn't work since the
893
* caller has already allocated space for the array...)
896
CopyArrayEls(ArrayType *array,
905
char *p = ARR_DATA_PTR(array);
906
bits8 *bitmap = ARR_NULLBITMAP(array);
914
for (i = 0; i < nitems; i++)
916
if (nulls && nulls[i])
918
if (!bitmap) /* shouldn't happen */
919
elog(ERROR, "null array element where not supported");
920
/* bitmap bit stays 0 */
925
p += ArrayCastAndSet(values[i], typlen, typbyval, typalign, p);
927
pfree(DatumGetPointer(values[i]));
932
if (bitmask == 0x100)
941
if (bitmap && bitmask != 1)
947
* takes the internal representation of an array and returns a string
948
* containing the array in its external format.
951
array_out(PG_FUNCTION_ARGS)
953
ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
954
Oid element_type = ARR_ELEMTYPE(v);
963
dims_str[(MAXDIM * 33) + 2];
966
* 33 per dim since we assume 15 digits per number + ':' +'[]'
968
* +2 allows for assignment operator + trailing null
983
ArrayMetaState *my_extra;
986
* We arrange to look up info about element type, including its output
987
* conversion proc, only once per series of calls, assuming the element
988
* type doesn't change underneath us.
990
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
991
if (my_extra == NULL)
993
fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
994
sizeof(ArrayMetaState));
995
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
996
my_extra->element_type = ~element_type;
999
if (my_extra->element_type != element_type)
1002
* Get info about element type, including its output conversion proc
1004
get_type_io_data(element_type, IOFunc_output,
1005
&my_extra->typlen, &my_extra->typbyval,
1006
&my_extra->typalign, &my_extra->typdelim,
1007
&my_extra->typioparam, &my_extra->typiofunc);
1008
fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1009
fcinfo->flinfo->fn_mcxt);
1010
my_extra->element_type = element_type;
1012
typlen = my_extra->typlen;
1013
typbyval = my_extra->typbyval;
1014
typalign = my_extra->typalign;
1015
typdelim = my_extra->typdelim;
1020
nitems = ArrayGetNItems(ndim, dims);
1024
retval = pstrdup("{}");
1025
PG_RETURN_CSTRING(retval);
1029
* we will need to add explicit dimensions if any dimension has a lower
1030
* bound other than one
1032
for (i = 0; i < ndim; i++)
1042
* Convert all values to string form, count total space needed (including
1043
* any overhead such as escaping backslashes), and detect whether each
1044
* item needs double quotes.
1046
values = (char **) palloc(nitems * sizeof(char *));
1047
needquotes = (bool *) palloc(nitems * sizeof(bool));
1048
overall_length = 1; /* don't forget to count \0 at end. */
1050
p = ARR_DATA_PTR(v);
1051
bitmap = ARR_NULLBITMAP(v);
1054
for (i = 0; i < nitems; i++)
1058
/* Get source element, checking for NULL */
1059
if (bitmap && (*bitmap & bitmask) == 0)
1061
values[i] = pstrdup("NULL");
1062
overall_length += 4;
1069
itemvalue = fetch_att(p, typbyval, typlen);
1070
values[i] = OutputFunctionCall(&my_extra->proc, itemvalue);
1071
p = att_addlength_pointer(p, typlen, p);
1072
p = (char *) att_align_nominal(p, typalign);
1074
/* count data plus backslashes; detect chars needing quotes */
1075
if (values[i][0] == '\0')
1076
needquote = true; /* force quotes for empty string */
1077
else if (pg_strcasecmp(values[i], "NULL") == 0)
1078
needquote = true; /* force quotes for literal NULL */
1082
for (tmp = values[i]; *tmp != '\0'; tmp++)
1086
overall_length += 1;
1087
if (ch == '"' || ch == '\\')
1090
overall_length += 1;
1092
else if (ch == '{' || ch == '}' || ch == typdelim ||
1098
needquotes[i] = needquote;
1100
/* Count the pair of double quotes, if needed */
1102
overall_length += 2;
1104
overall_length += 1;
1106
/* advance bitmap pointer if any */
1110
if (bitmask == 0x100)
1119
* count total number of curly braces in output string
1121
for (i = j = 0, k = 1; i < ndim; i++)
1122
k *= dims[i], j += k;
1126
/* add explicit dimensions if required */
1129
char *ptr = dims_str;
1131
for (i = 0; i < ndim; i++)
1133
sprintf(ptr, "[%d:%d]", lb[i], lb[i] + dims[i] - 1);
1140
retval = (char *) palloc(strlen(dims_str) + overall_length + 2 * j);
1143
#define APPENDSTR(str) (strcpy(p, (str)), p += strlen(p))
1144
#define APPENDCHAR(ch) (*p++ = (ch), *p = '\0')
1147
APPENDSTR(dims_str);
1149
for (i = 0; i < ndim; i++)
1155
for (i = j; i < ndim - 1; i++)
1161
for (tmp = values[k]; *tmp; tmp++)
1165
if (ch == '"' || ch == '\\')
1173
APPENDSTR(values[k]);
1176
for (i = ndim - 1; i >= 0; i--)
1178
indx[i] = (indx[i] + 1) % dims[i];
1181
APPENDCHAR(typdelim);
1196
PG_RETURN_CSTRING(retval);
1201
* converts an array from the external binary format to
1202
* its internal format.
1205
* the internal representation of the input array
1208
array_recv(PG_FUNCTION_ARGS)
1210
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
1211
Oid spec_element_type = PG_GETARG_OID(1); /* type of an array
1213
int32 typmod = PG_GETARG_INT32(2); /* typmod for array elements */
1231
ArrayMetaState *my_extra;
1233
/* Get the array header information */
1234
ndim = pq_getmsgint(buf, 4);
1235
if (ndim < 0) /* we do allow zero-dimension arrays */
1237
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1238
errmsg("invalid number of dimensions: %d", ndim)));
1241
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1242
errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
1245
flags = pq_getmsgint(buf, 4);
1246
if (flags != 0 && flags != 1)
1248
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1249
errmsg("invalid array flags")));
1251
element_type = pq_getmsgint(buf, sizeof(Oid));
1252
if (element_type != spec_element_type)
1254
/* XXX Can we allow taking the input element type in any cases? */
1256
(errcode(ERRCODE_DATATYPE_MISMATCH),
1257
errmsg("wrong element type")));
1260
for (i = 0; i < ndim; i++)
1262
dim[i] = pq_getmsgint(buf, 4);
1263
lBound[i] = pq_getmsgint(buf, 4);
1266
* Check overflow of upper bound. (ArrayNItems() below checks that
1271
int ub = lBound[i] + dim[i] - 1;
1275
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
1276
errmsg("integer out of range")));
1280
/* This checks for overflow of array dimensions */
1281
nitems = ArrayGetNItems(ndim, dim);
1284
* We arrange to look up info about element type, including its receive
1285
* conversion proc, only once per series of calls, assuming the element
1286
* type doesn't change underneath us.
1288
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1289
if (my_extra == NULL)
1291
fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1292
sizeof(ArrayMetaState));
1293
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1294
my_extra->element_type = ~element_type;
1297
if (my_extra->element_type != element_type)
1299
/* Get info about element type, including its receive proc */
1300
get_type_io_data(element_type, IOFunc_receive,
1301
&my_extra->typlen, &my_extra->typbyval,
1302
&my_extra->typalign, &my_extra->typdelim,
1303
&my_extra->typioparam, &my_extra->typiofunc);
1304
if (!OidIsValid(my_extra->typiofunc))
1306
(errcode(ERRCODE_UNDEFINED_FUNCTION),
1307
errmsg("no binary input function available for type %s",
1308
format_type_be(element_type))));
1309
fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1310
fcinfo->flinfo->fn_mcxt);
1311
my_extra->element_type = element_type;
1316
/* Return empty array ... but not till we've validated element_type */
1317
PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
1320
typlen = my_extra->typlen;
1321
typbyval = my_extra->typbyval;
1322
typalign = my_extra->typalign;
1323
typioparam = my_extra->typioparam;
1325
dataPtr = (Datum *) palloc(nitems * sizeof(Datum));
1326
nullsPtr = (bool *) palloc(nitems * sizeof(bool));
1327
ReadArrayBinary(buf, nitems,
1328
&my_extra->proc, typioparam, typmod,
1329
typlen, typbyval, typalign,
1331
&hasnulls, &nbytes);
1334
dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
1335
nbytes += dataoffset;
1339
dataoffset = 0; /* marker for no null bitmap */
1340
nbytes += ARR_OVERHEAD_NONULLS(ndim);
1342
retval = (ArrayType *) palloc0(nbytes);
1343
SET_VARSIZE(retval, nbytes);
1344
retval->ndim = ndim;
1345
retval->dataoffset = dataoffset;
1346
retval->elemtype = element_type;
1347
memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
1348
memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
1350
CopyArrayEls(retval,
1351
dataPtr, nullsPtr, nitems,
1352
typlen, typbyval, typalign,
1358
PG_RETURN_ARRAYTYPE_P(retval);
1363
* collect the data elements of an array being read in binary style.
1366
* buf: the data buffer to read from.
1367
* nitems: total number of array elements (already read).
1368
* receiveproc: type-specific receive procedure for element datatype.
1369
* typioparam, typmod: auxiliary values to pass to receiveproc.
1370
* typlen, typbyval, typalign: storage parameters of element datatype.
1373
* values[]: filled with converted data values.
1374
* nulls[]: filled with is-null markers.
1375
* *hasnulls: set TRUE iff there are any null elements.
1376
* *nbytes: set to total size of data area needed (including alignment
1377
* padding but not including array header overhead).
1379
* Note that values[] and nulls[] are allocated by the caller, and must have
1383
ReadArrayBinary(StringInfo buf,
1385
FmgrInfo *receiveproc,
1400
for (i = 0; i < nitems; i++)
1403
StringInfoData elem_buf;
1406
/* Get and check the item length */
1407
itemlen = pq_getmsgint(buf, 4);
1408
if (itemlen < -1 || itemlen > (buf->len - buf->cursor))
1410
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1411
errmsg("insufficient data left in message")));
1415
/* -1 length means NULL */
1416
values[i] = ReceiveFunctionCall(receiveproc, NULL,
1417
typioparam, typmod);
1423
* Rather than copying data around, we just set up a phony StringInfo
1424
* pointing to the correct portion of the input buffer. We assume we
1425
* can scribble on the input buffer so as to maintain the convention
1426
* that StringInfos have a trailing null.
1428
elem_buf.data = &buf->data[buf->cursor];
1429
elem_buf.maxlen = itemlen + 1;
1430
elem_buf.len = itemlen;
1431
elem_buf.cursor = 0;
1433
buf->cursor += itemlen;
1435
csave = buf->data[buf->cursor];
1436
buf->data[buf->cursor] = '\0';
1438
/* Now call the element's receiveproc */
1439
values[i] = ReceiveFunctionCall(receiveproc, &elem_buf,
1440
typioparam, typmod);
1443
/* Trouble if it didn't eat the whole buffer */
1444
if (elem_buf.cursor != itemlen)
1446
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1447
errmsg("improper binary format in array element %d",
1450
buf->data[buf->cursor] = csave;
1454
* Check for nulls, compute total data space needed
1458
for (i = 0; i < nitems; i++)
1464
/* let's just make sure data is not toasted */
1466
values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
1467
totbytes = att_addlength_datum(totbytes, typlen, values[i]);
1468
totbytes = att_align_nominal(totbytes, typalign);
1469
/* check for overflow of total request */
1470
if (!AllocSizeIsValid(totbytes))
1472
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1473
errmsg("array size exceeds the maximum allowed (%d)",
1474
(int) MaxAllocSize)));
1477
*hasnulls = hasnull;
1484
* takes the internal representation of an array and returns a bytea
1485
* containing the array in its external binary format.
1488
array_send(PG_FUNCTION_ARGS)
1490
ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1491
Oid element_type = ARR_ELEMTYPE(v);
1503
ArrayMetaState *my_extra;
1506
* We arrange to look up info about element type, including its send
1507
* conversion proc, only once per series of calls, assuming the element
1508
* type doesn't change underneath us.
1510
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1511
if (my_extra == NULL)
1513
fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1514
sizeof(ArrayMetaState));
1515
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1516
my_extra->element_type = ~element_type;
1519
if (my_extra->element_type != element_type)
1521
/* Get info about element type, including its send proc */
1522
get_type_io_data(element_type, IOFunc_send,
1523
&my_extra->typlen, &my_extra->typbyval,
1524
&my_extra->typalign, &my_extra->typdelim,
1525
&my_extra->typioparam, &my_extra->typiofunc);
1526
if (!OidIsValid(my_extra->typiofunc))
1528
(errcode(ERRCODE_UNDEFINED_FUNCTION),
1529
errmsg("no binary output function available for type %s",
1530
format_type_be(element_type))));
1531
fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1532
fcinfo->flinfo->fn_mcxt);
1533
my_extra->element_type = element_type;
1535
typlen = my_extra->typlen;
1536
typbyval = my_extra->typbyval;
1537
typalign = my_extra->typalign;
1541
nitems = ArrayGetNItems(ndim, dim);
1543
pq_begintypsend(&buf);
1545
/* Send the array header information */
1546
pq_sendint(&buf, ndim, 4);
1547
pq_sendint(&buf, ARR_HASNULL(v) ? 1 : 0, 4);
1548
pq_sendint(&buf, element_type, sizeof(Oid));
1549
for (i = 0; i < ndim; i++)
1551
pq_sendint(&buf, ARR_DIMS(v)[i], 4);
1552
pq_sendint(&buf, ARR_LBOUND(v)[i], 4);
1555
/* Send the array elements using the element's own sendproc */
1556
p = ARR_DATA_PTR(v);
1557
bitmap = ARR_NULLBITMAP(v);
1560
for (i = 0; i < nitems; i++)
1562
/* Get source element, checking for NULL */
1563
if (bitmap && (*bitmap & bitmask) == 0)
1565
/* -1 length means a NULL */
1566
pq_sendint(&buf, -1, 4);
1573
itemvalue = fetch_att(p, typbyval, typlen);
1574
outputbytes = SendFunctionCall(&my_extra->proc, itemvalue);
1575
pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
1576
pq_sendbytes(&buf, VARDATA(outputbytes),
1577
VARSIZE(outputbytes) - VARHDRSZ);
1580
p = att_addlength_pointer(p, typlen, p);
1581
p = (char *) att_align_nominal(p, typalign);
1584
/* advance bitmap pointer if any */
1588
if (bitmask == 0x100)
1596
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
1601
* returns the number of dimensions of the array pointed to by "v"
1604
array_ndims(PG_FUNCTION_ARGS)
1606
ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1608
/* Sanity check: does it look like an array at all? */
1609
if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1612
PG_RETURN_INT32(ARR_NDIM(v));
1617
* returns the dimensions of the array pointed to by "v", as a "text"
1620
array_dims(PG_FUNCTION_ARGS)
1622
ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1629
* 33 since we assume 15 digits per number + ':' +'[]'
1631
* +1 for trailing null
1633
char buf[MAXDIM * 33 + 1];
1635
/* Sanity check: does it look like an array at all? */
1636
if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1643
for (i = 0; i < ARR_NDIM(v); i++)
1645
sprintf(p, "[%d:%d]", lb[i], dimv[i] + lb[i] - 1);
1649
PG_RETURN_TEXT_P(cstring_to_text(buf));
1654
* returns the lower dimension, of the DIM requested, for
1655
* the array pointed to by "v", as an int4
1658
array_lower(PG_FUNCTION_ARGS)
1660
ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1661
int reqdim = PG_GETARG_INT32(1);
1665
/* Sanity check: does it look like an array at all? */
1666
if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1669
/* Sanity check: was the requested dim valid */
1670
if (reqdim <= 0 || reqdim > ARR_NDIM(v))
1674
result = lb[reqdim - 1];
1676
PG_RETURN_INT32(result);
1681
* returns the upper dimension, of the DIM requested, for
1682
* the array pointed to by "v", as an int4
1685
array_upper(PG_FUNCTION_ARGS)
1687
ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1688
int reqdim = PG_GETARG_INT32(1);
1693
/* Sanity check: does it look like an array at all? */
1694
if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1697
/* Sanity check: was the requested dim valid */
1698
if (reqdim <= 0 || reqdim > ARR_NDIM(v))
1704
result = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
1706
PG_RETURN_INT32(result);
1711
* returns the length, of the dimension requested, for
1712
* the array pointed to by "v", as an int4
1715
array_length(PG_FUNCTION_ARGS)
1717
ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1718
int reqdim = PG_GETARG_INT32(1);
1722
/* Sanity check: does it look like an array at all? */
1723
if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1726
/* Sanity check: was the requested dim valid */
1727
if (reqdim <= 0 || reqdim > ARR_NDIM(v))
1732
result = dimv[reqdim - 1];
1734
PG_RETURN_INT32(result);
1739
* This routine takes an array pointer and a subscript array and returns
1740
* the referenced item as a Datum. Note that for a pass-by-reference
1741
* datatype, the returned Datum is a pointer into the array object.
1743
* This handles both ordinary varlena arrays and fixed-length arrays.
1746
* array: the array object (mustn't be NULL)
1747
* nSubscripts: number of subscripts supplied
1748
* indx[]: the subscript values
1749
* arraytyplen: pg_type.typlen for the array type
1750
* elmlen: pg_type.typlen for the array's element type
1751
* elmbyval: pg_type.typbyval for the array's element type
1752
* elmalign: pg_type.typalign for the array's element type
1755
* The return value is the element Datum.
1756
* *isNull is set to indicate whether the element is NULL.
1759
array_ref(ArrayType *array,
1777
bits8 *arraynullsptr;
1779
if (arraytyplen > 0)
1782
* fixed-length arrays -- these are assumed to be 1-d, 0-based
1785
fixedDim[0] = arraytyplen / elmlen;
1789
arraydataptr = (char *) array;
1790
arraynullsptr = NULL;
1794
/* detoast input array if necessary */
1795
array = DatumGetArrayTypeP(PointerGetDatum(array));
1797
ndim = ARR_NDIM(array);
1798
dim = ARR_DIMS(array);
1799
lb = ARR_LBOUND(array);
1800
arraydataptr = ARR_DATA_PTR(array);
1801
arraynullsptr = ARR_NULLBITMAP(array);
1805
* Return NULL for invalid subscript
1807
if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
1812
for (i = 0; i < ndim; i++)
1814
if (indx[i] < lb[i] || indx[i] >= (dim[i] + lb[i]))
1822
* Calculate the element number
1824
offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
1827
* Check for NULL array element
1829
if (array_get_isnull(arraynullsptr, offset))
1836
* OK, get the element
1839
retptr = array_seek(arraydataptr, 0, arraynullsptr, offset,
1840
elmlen, elmbyval, elmalign);
1841
return ArrayCast(retptr, elmbyval, elmlen);
1846
* This routine takes an array and a range of indices (upperIndex and
1847
* lowerIndx), creates a new array structure for the referred elements
1848
* and returns a pointer to it.
1850
* This handles both ordinary varlena arrays and fixed-length arrays.
1853
* array: the array object (mustn't be NULL)
1854
* nSubscripts: number of subscripts supplied (must be same for upper/lower)
1855
* upperIndx[]: the upper subscript values
1856
* lowerIndx[]: the lower subscript values
1857
* arraytyplen: pg_type.typlen for the array type
1858
* elmlen: pg_type.typlen for the array's element type
1859
* elmbyval: pg_type.typbyval for the array's element type
1860
* elmalign: pg_type.typalign for the array's element type
1863
* The return value is the new array Datum (it's never NULL)
1865
* NOTE: we assume it is OK to scribble on the provided subscript arrays
1866
* lowerIndx[] and upperIndx[]. These are generally just temporaries.
1869
array_get_slice(ArrayType *array,
1878
ArrayType *newarray;
1888
bits8 *arraynullsptr;
1893
if (arraytyplen > 0)
1896
* fixed-length arrays -- currently, cannot slice these because parser
1897
* labels output as being of the fixed-length array type! Code below
1898
* shows how we could support it if the parser were changed to label
1899
* output as a suitable varlena array type.
1902
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1903
errmsg("slices of fixed-length arrays not implemented")));
1906
* fixed-length arrays -- these are assumed to be 1-d, 0-based
1908
* XXX where would we get the correct ELEMTYPE from?
1911
fixedDim[0] = arraytyplen / elmlen;
1915
elemtype = InvalidOid; /* XXX */
1916
arraydataptr = (char *) array;
1917
arraynullsptr = NULL;
1921
/* detoast input array if necessary */
1922
array = DatumGetArrayTypeP(PointerGetDatum(array));
1924
ndim = ARR_NDIM(array);
1925
dim = ARR_DIMS(array);
1926
lb = ARR_LBOUND(array);
1927
elemtype = ARR_ELEMTYPE(array);
1928
arraydataptr = ARR_DATA_PTR(array);
1929
arraynullsptr = ARR_NULLBITMAP(array);
1933
* Check provided subscripts. A slice exceeding the current array limits
1934
* is silently truncated to the array limits. If we end up with an empty
1935
* slice, return an empty array.
1937
if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
1938
return construct_empty_array(elemtype);
1940
for (i = 0; i < nSubscripts; i++)
1942
if (lowerIndx[i] < lb[i])
1943
lowerIndx[i] = lb[i];
1944
if (upperIndx[i] >= (dim[i] + lb[i]))
1945
upperIndx[i] = dim[i] + lb[i] - 1;
1946
if (lowerIndx[i] > upperIndx[i])
1947
return construct_empty_array(elemtype);
1949
/* fill any missing subscript positions with full array range */
1950
for (; i < ndim; i++)
1952
lowerIndx[i] = lb[i];
1953
upperIndx[i] = dim[i] + lb[i] - 1;
1954
if (lowerIndx[i] > upperIndx[i])
1955
return construct_empty_array(elemtype);
1958
mda_get_range(ndim, span, lowerIndx, upperIndx);
1960
bytes = array_slice_size(arraydataptr, arraynullsptr,
1962
lowerIndx, upperIndx,
1963
elmlen, elmbyval, elmalign);
1966
* Currently, we put a null bitmap in the result if the source has one;
1967
* could be smarter ...
1971
dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, ArrayGetNItems(ndim, span));
1972
bytes += dataoffset;
1976
dataoffset = 0; /* marker for no null bitmap */
1977
bytes += ARR_OVERHEAD_NONULLS(ndim);
1980
newarray = (ArrayType *) palloc0(bytes);
1981
SET_VARSIZE(newarray, bytes);
1982
newarray->ndim = ndim;
1983
newarray->dataoffset = dataoffset;
1984
newarray->elemtype = elemtype;
1985
memcpy(ARR_DIMS(newarray), span, ndim * sizeof(int));
1988
* Lower bounds of the new array are set to 1. Formerly (before 7.3) we
1989
* copied the given lowerIndx values ... but that seems confusing.
1991
newlb = ARR_LBOUND(newarray);
1992
for (i = 0; i < ndim; i++)
1995
array_extract_slice(newarray,
1997
arraydataptr, arraynullsptr,
1998
lowerIndx, upperIndx,
1999
elmlen, elmbyval, elmalign);
2006
* This routine sets the value of an array element (specified by
2007
* a subscript array) to a new value specified by "dataValue".
2009
* This handles both ordinary varlena arrays and fixed-length arrays.
2012
* array: the initial array object (mustn't be NULL)
2013
* nSubscripts: number of subscripts supplied
2014
* indx[]: the subscript values
2015
* dataValue: the datum to be inserted at the given position
2016
* isNull: whether dataValue is NULL
2017
* arraytyplen: pg_type.typlen for the array type
2018
* elmlen: pg_type.typlen for the array's element type
2019
* elmbyval: pg_type.typbyval for the array's element type
2020
* elmalign: pg_type.typalign for the array's element type
2023
* A new array is returned, just like the old except for the one
2024
* modified entry. The original array object is not changed.
2026
* For one-dimensional arrays only, we allow the array to be extended
2027
* by assigning to a position outside the existing subscript range; any
2028
* positions between the existing elements and the new one are set to NULLs.
2029
* (XXX TODO: allow a corresponding behavior for multidimensional arrays)
2031
* NOTE: For assignments, we throw an error for invalid subscripts etc,
2032
* rather than returning a NULL as the fetch operations do.
2035
array_set(ArrayType *array,
2045
ArrayType *newarray;
2053
bits8 *oldnullbitmap;
2067
if (arraytyplen > 0)
2070
* fixed-length arrays -- these are assumed to be 1-d, 0-based. We
2071
* cannot extend them, either.
2073
if (nSubscripts != 1)
2075
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2076
errmsg("wrong number of array subscripts")));
2078
if (indx[0] < 0 || indx[0] * elmlen >= arraytyplen)
2080
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2081
errmsg("array subscript out of range")));
2085
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
2086
errmsg("cannot assign null value to an element of a fixed-length array")));
2088
newarray = (ArrayType *) palloc(arraytyplen);
2089
memcpy(newarray, array, arraytyplen);
2090
elt_ptr = (char *) newarray + indx[0] * elmlen;
2091
ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign, elt_ptr);
2095
if (nSubscripts <= 0 || nSubscripts > MAXDIM)
2097
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2098
errmsg("wrong number of array subscripts")));
2100
/* make sure item to be inserted is not toasted */
2101
if (elmlen == -1 && !isNull)
2102
dataValue = PointerGetDatum(PG_DETOAST_DATUM(dataValue));
2104
/* detoast input array if necessary */
2105
array = DatumGetArrayTypeP(PointerGetDatum(array));
2107
ndim = ARR_NDIM(array);
2110
* if number of dims is zero, i.e. an empty array, create an array with
2111
* nSubscripts dimensions, and set the lower bounds to the supplied
2116
Oid elmtype = ARR_ELEMTYPE(array);
2118
for (i = 0; i < nSubscripts; i++)
2124
return construct_md_array(&dataValue, &isNull, nSubscripts,
2126
elmlen, elmbyval, elmalign);
2129
if (ndim != nSubscripts)
2131
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2132
errmsg("wrong number of array subscripts")));
2134
/* copy dim/lb since we may modify them */
2135
memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
2136
memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
2138
newhasnulls = (ARR_HASNULL(array) || isNull);
2139
addedbefore = addedafter = 0;
2146
if (indx[0] < lb[0])
2148
addedbefore = lb[0] - indx[0];
2149
dim[0] += addedbefore;
2151
if (addedbefore > 1)
2152
newhasnulls = true; /* will insert nulls */
2154
if (indx[0] >= (dim[0] + lb[0]))
2156
addedafter = indx[0] - (dim[0] + lb[0]) + 1;
2157
dim[0] += addedafter;
2159
newhasnulls = true; /* will insert nulls */
2165
* XXX currently we do not support extending multi-dimensional arrays
2168
for (i = 0; i < ndim; i++)
2170
if (indx[i] < lb[i] ||
2171
indx[i] >= (dim[i] + lb[i]))
2173
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2174
errmsg("array subscript out of range")));
2179
* Compute sizes of items and areas to copy
2181
newnitems = ArrayGetNItems(ndim, dim);
2183
overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, newnitems);
2185
overheadlen = ARR_OVERHEAD_NONULLS(ndim);
2186
oldnitems = ArrayGetNItems(ndim, ARR_DIMS(array));
2187
oldnullbitmap = ARR_NULLBITMAP(array);
2188
oldoverheadlen = ARR_DATA_OFFSET(array);
2189
olddatasize = ARR_SIZE(array) - oldoverheadlen;
2195
lenafter = olddatasize;
2197
else if (addedafter)
2200
lenbefore = olddatasize;
2206
offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
2207
elt_ptr = array_seek(ARR_DATA_PTR(array), 0, oldnullbitmap, offset,
2208
elmlen, elmbyval, elmalign);
2209
lenbefore = (int) (elt_ptr - ARR_DATA_PTR(array));
2210
if (array_get_isnull(oldnullbitmap, offset))
2214
olditemlen = att_addlength_pointer(0, elmlen, elt_ptr);
2215
olditemlen = att_align_nominal(olditemlen, elmalign);
2217
lenafter = (int) (olddatasize - lenbefore - olditemlen);
2224
newitemlen = att_addlength_datum(0, elmlen, dataValue);
2225
newitemlen = att_align_nominal(newitemlen, elmalign);
2228
newsize = overheadlen + lenbefore + newitemlen + lenafter;
2231
* OK, create the new array and fill in header/dimensions
2233
newarray = (ArrayType *) palloc0(newsize);
2234
SET_VARSIZE(newarray, newsize);
2235
newarray->ndim = ndim;
2236
newarray->dataoffset = newhasnulls ? overheadlen : 0;
2237
newarray->elemtype = ARR_ELEMTYPE(array);
2238
memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
2239
memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
2244
memcpy((char *) newarray + overheadlen,
2245
(char *) array + oldoverheadlen,
2248
ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign,
2249
(char *) newarray + overheadlen + lenbefore);
2250
memcpy((char *) newarray + overheadlen + lenbefore + newitemlen,
2251
(char *) array + oldoverheadlen + lenbefore + olditemlen,
2255
* Fill in nulls bitmap if needed
2257
* Note: it's possible we just replaced the last NULL with a non-NULL, and
2258
* could get rid of the bitmap. Seems not worth testing for though.
2262
bits8 *newnullbitmap = ARR_NULLBITMAP(newarray);
2264
/* Zero the bitmap to take care of marking inserted positions null */
2265
MemSet(newnullbitmap, 0, (newnitems + 7) / 8);
2266
/* Fix the inserted value */
2268
array_set_isnull(newnullbitmap, newnitems - 1, isNull);
2270
array_set_isnull(newnullbitmap, offset, isNull);
2271
/* Fix the copied range(s) */
2273
array_bitmap_copy(newnullbitmap, addedbefore,
2278
array_bitmap_copy(newnullbitmap, 0,
2281
if (addedafter == 0)
2282
array_bitmap_copy(newnullbitmap, offset + 1,
2283
oldnullbitmap, offset + 1,
2284
oldnitems - offset - 1);
2293
* This routine sets the value of a range of array locations (specified
2294
* by upper and lower subscript values) to new values passed as
2297
* This handles both ordinary varlena arrays and fixed-length arrays.
2300
* array: the initial array object (mustn't be NULL)
2301
* nSubscripts: number of subscripts supplied (must be same for upper/lower)
2302
* upperIndx[]: the upper subscript values
2303
* lowerIndx[]: the lower subscript values
2304
* srcArray: the source for the inserted values
2305
* isNull: indicates whether srcArray is NULL
2306
* arraytyplen: pg_type.typlen for the array type
2307
* elmlen: pg_type.typlen for the array's element type
2308
* elmbyval: pg_type.typbyval for the array's element type
2309
* elmalign: pg_type.typalign for the array's element type
2312
* A new array is returned, just like the old except for the
2313
* modified range. The original array object is not changed.
2315
* For one-dimensional arrays only, we allow the array to be extended
2316
* by assigning to positions outside the existing subscript range; any
2317
* positions between the existing elements and the new ones are set to NULLs.
2318
* (XXX TODO: allow a corresponding behavior for multidimensional arrays)
2320
* NOTE: we assume it is OK to scribble on the provided index arrays
2321
* lowerIndx[] and upperIndx[]. These are generally just temporaries.
2323
* NOTE: For assignments, we throw an error for silly subscripts etc,
2324
* rather than returning a NULL or empty array as the fetch operations do.
2327
array_set_slice(ArrayType *array,
2331
ArrayType *srcArray,
2338
ArrayType *newarray;
2361
/* Currently, assignment from a NULL source array is a no-op */
2365
if (arraytyplen > 0)
2368
* fixed-length arrays -- not got round to doing this...
2371
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2372
errmsg("updates on slices of fixed-length arrays not implemented")));
2375
/* detoast arrays if necessary */
2376
array = DatumGetArrayTypeP(PointerGetDatum(array));
2377
srcArray = DatumGetArrayTypeP(PointerGetDatum(srcArray));
2379
/* note: we assume srcArray contains no toasted elements */
2381
ndim = ARR_NDIM(array);
2384
* if number of dims is zero, i.e. an empty array, create an array with
2385
* nSubscripts dimensions, and set the upper and lower bounds to the
2386
* supplied subscripts
2393
Oid elmtype = ARR_ELEMTYPE(array);
2395
deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
2396
&dvalues, &dnulls, &nelems);
2398
for (i = 0; i < nSubscripts; i++)
2400
dim[i] = 1 + upperIndx[i] - lowerIndx[i];
2401
lb[i] = lowerIndx[i];
2404
/* complain if too few source items; we ignore extras, however */
2405
if (nelems < ArrayGetNItems(nSubscripts, dim))
2407
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2408
errmsg("source array too small")));
2410
return construct_md_array(dvalues, dnulls, nSubscripts,
2412
elmlen, elmbyval, elmalign);
2415
if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
2417
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2418
errmsg("wrong number of array subscripts")));
2420
/* copy dim/lb since we may modify them */
2421
memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
2422
memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
2424
newhasnulls = (ARR_HASNULL(array) || ARR_HASNULL(srcArray));
2425
addedbefore = addedafter = 0;
2432
Assert(nSubscripts == 1);
2433
if (lowerIndx[0] > upperIndx[0])
2435
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2436
errmsg("upper bound cannot be less than lower bound")));
2437
if (lowerIndx[0] < lb[0])
2439
if (upperIndx[0] < lb[0] - 1)
2440
newhasnulls = true; /* will insert nulls */
2441
addedbefore = lb[0] - lowerIndx[0];
2442
dim[0] += addedbefore;
2443
lb[0] = lowerIndx[0];
2445
if (upperIndx[0] >= (dim[0] + lb[0]))
2447
if (lowerIndx[0] > (dim[0] + lb[0]))
2448
newhasnulls = true; /* will insert nulls */
2449
addedafter = upperIndx[0] - (dim[0] + lb[0]) + 1;
2450
dim[0] += addedafter;
2456
* XXX currently we do not support extending multi-dimensional arrays
2459
for (i = 0; i < nSubscripts; i++)
2461
if (lowerIndx[i] > upperIndx[i])
2463
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2464
errmsg("upper bound cannot be less than lower bound")));
2465
if (lowerIndx[i] < lb[i] ||
2466
upperIndx[i] >= (dim[i] + lb[i]))
2468
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2469
errmsg("array subscript out of range")));
2471
/* fill any missing subscript positions with full array range */
2472
for (; i < ndim; i++)
2474
lowerIndx[i] = lb[i];
2475
upperIndx[i] = dim[i] + lb[i] - 1;
2476
if (lowerIndx[i] > upperIndx[i])
2478
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2479
errmsg("upper bound cannot be less than lower bound")));
2483
/* Do this mainly to check for overflow */
2484
nitems = ArrayGetNItems(ndim, dim);
2487
* Make sure source array has enough entries. Note we ignore the shape of
2488
* the source array and just read entries serially.
2490
mda_get_range(ndim, span, lowerIndx, upperIndx);
2491
nsrcitems = ArrayGetNItems(ndim, span);
2492
if (nsrcitems > ArrayGetNItems(ARR_NDIM(srcArray), ARR_DIMS(srcArray)))
2494
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2495
errmsg("source array too small")));
2498
* Compute space occupied by new entries, space occupied by replaced
2499
* entries, and required space for new array.
2502
overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
2504
overheadlen = ARR_OVERHEAD_NONULLS(ndim);
2505
newitemsize = array_nelems_size(ARR_DATA_PTR(srcArray), 0,
2506
ARR_NULLBITMAP(srcArray), nsrcitems,
2507
elmlen, elmbyval, elmalign);
2508
oldoverheadlen = ARR_DATA_OFFSET(array);
2509
olddatasize = ARR_SIZE(array) - oldoverheadlen;
2513
* here we do not need to cope with extension of the array; it would
2514
* be a lot more complicated if we had to do so...
2516
olditemsize = array_slice_size(ARR_DATA_PTR(array),
2517
ARR_NULLBITMAP(array),
2519
lowerIndx, upperIndx,
2520
elmlen, elmbyval, elmalign);
2521
lenbefore = lenafter = 0; /* keep compiler quiet */
2522
itemsbefore = itemsafter = nolditems = 0;
2527
* here we must allow for possibility of slice larger than orig array
2528
* and/or not adjacent to orig array subscripts
2530
int oldlb = ARR_LBOUND(array)[0];
2531
int oldub = oldlb + ARR_DIMS(array)[0] - 1;
2532
int slicelb = Max(oldlb, lowerIndx[0]);
2533
int sliceub = Min(oldub, upperIndx[0]);
2534
char *oldarraydata = ARR_DATA_PTR(array);
2535
bits8 *oldarraybitmap = ARR_NULLBITMAP(array);
2537
/* count/size of old array entries that will go before the slice */
2538
itemsbefore = Min(slicelb, oldub + 1) - oldlb;
2539
lenbefore = array_nelems_size(oldarraydata, 0, oldarraybitmap,
2541
elmlen, elmbyval, elmalign);
2542
/* count/size of old array entries that will be replaced by slice */
2543
if (slicelb > sliceub)
2550
nolditems = sliceub - slicelb + 1;
2551
olditemsize = array_nelems_size(oldarraydata + lenbefore,
2552
itemsbefore, oldarraybitmap,
2554
elmlen, elmbyval, elmalign);
2556
/* count/size of old array entries that will go after the slice */
2557
itemsafter = oldub + 1 - Max(sliceub + 1, oldlb);
2558
lenafter = olddatasize - lenbefore - olditemsize;
2561
newsize = overheadlen + olddatasize - olditemsize + newitemsize;
2563
newarray = (ArrayType *) palloc0(newsize);
2564
SET_VARSIZE(newarray, newsize);
2565
newarray->ndim = ndim;
2566
newarray->dataoffset = newhasnulls ? overheadlen : 0;
2567
newarray->elemtype = ARR_ELEMTYPE(array);
2568
memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
2569
memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
2574
* here we do not need to cope with extension of the array; it would
2575
* be a lot more complicated if we had to do so...
2577
array_insert_slice(newarray, array, srcArray,
2579
lowerIndx, upperIndx,
2580
elmlen, elmbyval, elmalign);
2585
memcpy((char *) newarray + overheadlen,
2586
(char *) array + oldoverheadlen,
2588
memcpy((char *) newarray + overheadlen + lenbefore,
2589
ARR_DATA_PTR(srcArray),
2591
memcpy((char *) newarray + overheadlen + lenbefore + newitemsize,
2592
(char *) array + oldoverheadlen + lenbefore + olditemsize,
2594
/* fill in nulls bitmap if needed */
2597
bits8 *newnullbitmap = ARR_NULLBITMAP(newarray);
2598
bits8 *oldnullbitmap = ARR_NULLBITMAP(array);
2600
/* Zero the bitmap to handle marking inserted positions null */
2601
MemSet(newnullbitmap, 0, (nitems + 7) / 8);
2602
array_bitmap_copy(newnullbitmap, addedbefore,
2605
array_bitmap_copy(newnullbitmap, lowerIndx[0] - lb[0],
2606
ARR_NULLBITMAP(srcArray), 0,
2608
array_bitmap_copy(newnullbitmap, addedbefore + itemsbefore + nolditems,
2609
oldnullbitmap, itemsbefore + nolditems,
2620
* Map an array through an arbitrary function. Return a new array with
2621
* same dimensions and each source element transformed by fn(). Each
2622
* source element is passed as the first argument to fn(); additional
2623
* arguments to be passed to fn() can be specified by the caller.
2624
* The output array can have a different element type than the input.
2627
* * fcinfo: a function-call data structure pre-constructed by the caller
2628
* to be ready to call the desired function, with everything except the
2629
* first argument position filled in. In particular, flinfo identifies
2630
* the function fn(), and if nargs > 1 then argument positions after the
2631
* first must be preset to the additional values to be passed. The
2632
* first argument position initially holds the input array value.
2633
* * inpType: OID of element type of input array. This must be the same as,
2634
* or binary-compatible with, the first argument type of fn().
2635
* * retType: OID of element type of output array. This must be the same as,
2636
* or binary-compatible with, the result type of fn().
2637
* * amstate: workspace for array_map. Must be zeroed by caller before
2638
* first call, and not touched after that.
2640
* It is legitimate to pass a freshly-zeroed ArrayMapState on each call,
2641
* but better performance can be had if the state can be preserved across
2642
* a series of calls.
2644
* NB: caller must assure that input array is not NULL. NULL elements in
2645
* the array are OK however.
2648
array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType,
2649
ArrayMapState *amstate)
2672
ArrayMetaState *inp_extra;
2673
ArrayMetaState *ret_extra;
2675
/* Get input array */
2676
if (fcinfo->nargs < 1)
2677
elog(ERROR, "invalid nargs: %d", fcinfo->nargs);
2678
if (PG_ARGISNULL(0))
2679
elog(ERROR, "null input array");
2680
v = PG_GETARG_ARRAYTYPE_P(0);
2682
Assert(ARR_ELEMTYPE(v) == inpType);
2686
nitems = ArrayGetNItems(ndim, dim);
2688
/* Check for empty array */
2691
/* Return empty array */
2692
PG_RETURN_ARRAYTYPE_P(construct_empty_array(retType));
2696
* We arrange to look up info about input and return element types only
2697
* once per series of calls, assuming the element type doesn't change
2700
inp_extra = &amstate->inp_extra;
2701
ret_extra = &amstate->ret_extra;
2703
if (inp_extra->element_type != inpType)
2705
get_typlenbyvalalign(inpType,
2707
&inp_extra->typbyval,
2708
&inp_extra->typalign);
2709
inp_extra->element_type = inpType;
2711
inp_typlen = inp_extra->typlen;
2712
inp_typbyval = inp_extra->typbyval;
2713
inp_typalign = inp_extra->typalign;
2715
if (ret_extra->element_type != retType)
2717
get_typlenbyvalalign(retType,
2719
&ret_extra->typbyval,
2720
&ret_extra->typalign);
2721
ret_extra->element_type = retType;
2723
typlen = ret_extra->typlen;
2724
typbyval = ret_extra->typbyval;
2725
typalign = ret_extra->typalign;
2727
/* Allocate temporary arrays for new values */
2728
values = (Datum *) palloc(nitems * sizeof(Datum));
2729
nulls = (bool *) palloc(nitems * sizeof(bool));
2731
/* Loop over source data */
2732
s = ARR_DATA_PTR(v);
2733
bitmap = ARR_NULLBITMAP(v);
2737
for (i = 0; i < nitems; i++)
2741
/* Get source element, checking for NULL */
2742
if (bitmap && (*bitmap & bitmask) == 0)
2744
fcinfo->argnull[0] = true;
2748
elt = fetch_att(s, inp_typbyval, inp_typlen);
2749
s = att_addlength_datum(s, inp_typlen, elt);
2750
s = (char *) att_align_nominal(s, inp_typalign);
2751
fcinfo->arg[0] = elt;
2752
fcinfo->argnull[0] = false;
2756
* Apply the given function to source elt and extra args.
2758
if (fcinfo->flinfo->fn_strict)
2762
for (j = 0; j < fcinfo->nargs; j++)
2764
if (fcinfo->argnull[j])
2774
fcinfo->isnull = false;
2775
values[i] = FunctionCallInvoke(fcinfo);
2778
fcinfo->isnull = true;
2780
nulls[i] = fcinfo->isnull;
2785
/* Ensure data is not toasted */
2787
values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
2788
/* Update total result size */
2789
nbytes = att_addlength_datum(nbytes, typlen, values[i]);
2790
nbytes = att_align_nominal(nbytes, typalign);
2791
/* check for overflow of total request */
2792
if (!AllocSizeIsValid(nbytes))
2794
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2795
errmsg("array size exceeds the maximum allowed (%d)",
2796
(int) MaxAllocSize)));
2799
/* advance bitmap pointer if any */
2803
if (bitmask == 0x100)
2811
/* Allocate and initialize the result array */
2814
dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
2815
nbytes += dataoffset;
2819
dataoffset = 0; /* marker for no null bitmap */
2820
nbytes += ARR_OVERHEAD_NONULLS(ndim);
2822
result = (ArrayType *) palloc0(nbytes);
2823
SET_VARSIZE(result, nbytes);
2824
result->ndim = ndim;
2825
result->dataoffset = dataoffset;
2826
result->elemtype = retType;
2827
memcpy(ARR_DIMS(result), ARR_DIMS(v), 2 * ndim * sizeof(int));
2830
* Note: do not risk trying to pfree the results of the called function
2832
CopyArrayEls(result,
2833
values, nulls, nitems,
2834
typlen, typbyval, typalign,
2840
PG_RETURN_ARRAYTYPE_P(result);
2844
* construct_array --- simple method for constructing an array object
2846
* elems: array of Datum items to become the array contents
2847
* (NULL element values are not supported).
2848
* nelems: number of items
2849
* elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
2851
* A palloc'd 1-D array object is constructed and returned. Note that
2852
* elem values will be copied into the object even if pass-by-ref type.
2854
* NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
2855
* from the system catalogs, given the elmtype. However, the caller is
2856
* in a better position to cache this info across multiple uses, or even
2857
* to hard-wire values if the element type is hard-wired.
2860
construct_array(Datum *elems, int nelems,
2862
int elmlen, bool elmbyval, char elmalign)
2870
return construct_md_array(elems, NULL, 1, dims, lbs,
2871
elmtype, elmlen, elmbyval, elmalign);
2875
* construct_md_array --- simple method for constructing an array object
2876
* with arbitrary dimensions and possible NULLs
2878
* elems: array of Datum items to become the array contents
2879
* nulls: array of is-null flags (can be NULL if no nulls)
2880
* ndims: number of dimensions
2881
* dims: integer array with size of each dimension
2882
* lbs: integer array with lower bound of each dimension
2883
* elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
2885
* A palloc'd ndims-D array object is constructed and returned. Note that
2886
* elem values will be copied into the object even if pass-by-ref type.
2888
* NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
2889
* from the system catalogs, given the elmtype. However, the caller is
2890
* in a better position to cache this info across multiple uses, or even
2891
* to hard-wire values if the element type is hard-wired.
2894
construct_md_array(Datum *elems,
2899
Oid elmtype, int elmlen, bool elmbyval, char elmalign)
2908
if (ndims < 0) /* we do allow zero-dimension arrays */
2910
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2911
errmsg("invalid number of dimensions: %d", ndims)));
2914
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2915
errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
2918
/* fast track for empty array */
2920
return construct_empty_array(elmtype);
2922
nelems = ArrayGetNItems(ndims, dims);
2924
/* compute required space */
2927
for (i = 0; i < nelems; i++)
2929
if (nulls && nulls[i])
2934
/* make sure data is not toasted */
2936
elems[i] = PointerGetDatum(PG_DETOAST_DATUM(elems[i]));
2937
nbytes = att_addlength_datum(nbytes, elmlen, elems[i]);
2938
nbytes = att_align_nominal(nbytes, elmalign);
2939
/* check for overflow of total request */
2940
if (!AllocSizeIsValid(nbytes))
2942
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2943
errmsg("array size exceeds the maximum allowed (%d)",
2944
(int) MaxAllocSize)));
2947
/* Allocate and initialize result array */
2950
dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nelems);
2951
nbytes += dataoffset;
2955
dataoffset = 0; /* marker for no null bitmap */
2956
nbytes += ARR_OVERHEAD_NONULLS(ndims);
2958
result = (ArrayType *) palloc0(nbytes);
2959
SET_VARSIZE(result, nbytes);
2960
result->ndim = ndims;
2961
result->dataoffset = dataoffset;
2962
result->elemtype = elmtype;
2963
memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
2964
memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
2966
CopyArrayEls(result,
2967
elems, nulls, nelems,
2968
elmlen, elmbyval, elmalign,
2975
* construct_empty_array --- make a zero-dimensional array of given type
2978
construct_empty_array(Oid elmtype)
2982
result = (ArrayType *) palloc0(sizeof(ArrayType));
2983
SET_VARSIZE(result, sizeof(ArrayType));
2985
result->dataoffset = 0;
2986
result->elemtype = elmtype;
2991
* deconstruct_array --- simple method for extracting data from an array
2993
* array: array object to examine (must not be NULL)
2994
* elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
2995
* elemsp: return value, set to point to palloc'd array of Datum values
2996
* nullsp: return value, set to point to palloc'd array of isnull markers
2997
* nelemsp: return value, set to number of extracted values
2999
* The caller may pass nullsp == NULL if it does not support NULLs in the
3000
* array. Note that this produces a very uninformative error message,
3001
* so do it only in cases where a NULL is really not expected.
3003
* If array elements are pass-by-ref data type, the returned Datums will
3004
* be pointers into the array object.
3006
* NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
3007
* from the system catalogs, given the elmtype. However, in most current
3008
* uses the type is hard-wired into the caller and so we can save a lookup
3009
* cycle by hard-wiring the type info as well.
3012
deconstruct_array(ArrayType *array,
3014
int elmlen, bool elmbyval, char elmalign,
3015
Datum **elemsp, bool **nullsp, int *nelemsp)
3025
Assert(ARR_ELEMTYPE(array) == elmtype);
3027
nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
3028
*elemsp = elems = (Datum *) palloc(nelems * sizeof(Datum));
3030
*nullsp = nulls = (bool *) palloc0(nelems * sizeof(bool));
3035
p = ARR_DATA_PTR(array);
3036
bitmap = ARR_NULLBITMAP(array);
3039
for (i = 0; i < nelems; i++)
3041
/* Get source element, checking for NULL */
3042
if (bitmap && (*bitmap & bitmask) == 0)
3044
elems[i] = (Datum) 0;
3049
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
3050
errmsg("null array element not allowed in this context")));
3054
elems[i] = fetch_att(p, elmbyval, elmlen);
3055
p = att_addlength_pointer(p, elmlen, p);
3056
p = (char *) att_align_nominal(p, elmalign);
3059
/* advance bitmap pointer if any */
3063
if (bitmask == 0x100)
3073
* array_contains_nulls --- detect whether an array has any null elements
3075
* This gives an accurate answer, whereas testing ARR_HASNULL only tells
3076
* if the array *might* contain a null.
3079
array_contains_nulls(ArrayType *array)
3085
/* Easy answer if there's no null bitmap */
3086
if (!ARR_HASNULL(array))
3089
nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
3091
bitmap = ARR_NULLBITMAP(array);
3093
/* check whole bytes of the bitmap byte-at-a-time */
3096
if (*bitmap != 0xFF)
3102
/* check last partial byte */
3106
if ((*bitmap & bitmask) == 0)
3118
* compares two arrays for equality
3120
* returns true if the arrays are equal, false otherwise.
3122
* Note: we do not use array_cmp here, since equality may be meaningful in
3123
* datatypes that don't have a total ordering (and hence no btree support).
3126
array_eq(PG_FUNCTION_ARGS)
3128
ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
3129
ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
3130
Oid collation = PG_GET_COLLATION();
3131
int ndims1 = ARR_NDIM(array1);
3132
int ndims2 = ARR_NDIM(array2);
3133
int *dims1 = ARR_DIMS(array1);
3134
int *dims2 = ARR_DIMS(array2);
3135
Oid element_type = ARR_ELEMTYPE(array1);
3138
TypeCacheEntry *typentry;
3148
FunctionCallInfoData locfcinfo;
3150
if (element_type != ARR_ELEMTYPE(array2))
3152
(errcode(ERRCODE_DATATYPE_MISMATCH),
3153
errmsg("cannot compare arrays of different element types")));
3155
/* fast path if the arrays do not have the same dimensionality */
3156
if (ndims1 != ndims2 ||
3157
memcmp(dims1, dims2, 2 * ndims1 * sizeof(int)) != 0)
3162
* We arrange to look up the equality function only once per series of
3163
* calls, assuming the element type doesn't change underneath us. The
3164
* typcache is used so that we have no memory leakage when being used
3165
* as an index support function.
3167
typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3168
if (typentry == NULL ||
3169
typentry->type_id != element_type)
3171
typentry = lookup_type_cache(element_type,
3172
TYPECACHE_EQ_OPR_FINFO);
3173
if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
3175
(errcode(ERRCODE_UNDEFINED_FUNCTION),
3176
errmsg("could not identify an equality operator for type %s",
3177
format_type_be(element_type))));
3178
fcinfo->flinfo->fn_extra = (void *) typentry;
3180
typlen = typentry->typlen;
3181
typbyval = typentry->typbyval;
3182
typalign = typentry->typalign;
3185
* apply the operator to each pair of array elements.
3187
InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
3188
collation, NULL, NULL);
3190
/* Loop over source data */
3191
nitems = ArrayGetNItems(ndims1, dims1);
3192
ptr1 = ARR_DATA_PTR(array1);
3193
ptr2 = ARR_DATA_PTR(array2);
3194
bitmap1 = ARR_NULLBITMAP(array1);
3195
bitmap2 = ARR_NULLBITMAP(array2);
3196
bitmask = 1; /* use same bitmask for both arrays */
3198
for (i = 0; i < nitems; i++)
3206
/* Get elements, checking for NULL */
3207
if (bitmap1 && (*bitmap1 & bitmask) == 0)
3215
elt1 = fetch_att(ptr1, typbyval, typlen);
3216
ptr1 = att_addlength_pointer(ptr1, typlen, ptr1);
3217
ptr1 = (char *) att_align_nominal(ptr1, typalign);
3220
if (bitmap2 && (*bitmap2 & bitmask) == 0)
3228
elt2 = fetch_att(ptr2, typbyval, typlen);
3229
ptr2 = att_addlength_pointer(ptr2, typlen, ptr2);
3230
ptr2 = (char *) att_align_nominal(ptr2, typalign);
3233
/* advance bitmap pointers if any */
3235
if (bitmask == 0x100)
3245
* We consider two NULLs equal; NULL and not-NULL are unequal.
3247
if (isnull1 && isnull2)
3249
if (isnull1 || isnull2)
3256
* Apply the operator to the element pair
3258
locfcinfo.arg[0] = elt1;
3259
locfcinfo.arg[1] = elt2;
3260
locfcinfo.argnull[0] = false;
3261
locfcinfo.argnull[1] = false;
3262
locfcinfo.isnull = false;
3263
oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
3272
/* Avoid leaking memory when handed toasted input. */
3273
PG_FREE_IF_COPY(array1, 0);
3274
PG_FREE_IF_COPY(array2, 1);
3276
PG_RETURN_BOOL(result);
3280
/*-----------------------------------------------------------------------------
3281
* array-array bool operators:
3282
* Given two arrays, iterate comparison operators
3283
* over the array. Uses logic similar to text comparison
3284
* functions, except element-by-element instead of
3285
* character-by-character.
3286
*----------------------------------------------------------------------------
3290
array_ne(PG_FUNCTION_ARGS)
3292
PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo)));
3296
array_lt(PG_FUNCTION_ARGS)
3298
PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
3302
array_gt(PG_FUNCTION_ARGS)
3304
PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
3308
array_le(PG_FUNCTION_ARGS)
3310
PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
3314
array_ge(PG_FUNCTION_ARGS)
3316
PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
3320
btarraycmp(PG_FUNCTION_ARGS)
3322
PG_RETURN_INT32(array_cmp(fcinfo));
3327
* Internal comparison function for arrays.
3329
* Returns -1, 0 or 1
3332
array_cmp(FunctionCallInfo fcinfo)
3334
ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
3335
ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
3336
Oid collation = PG_GET_COLLATION();
3337
int ndims1 = ARR_NDIM(array1);
3338
int ndims2 = ARR_NDIM(array2);
3339
int *dims1 = ARR_DIMS(array1);
3340
int *dims2 = ARR_DIMS(array2);
3341
int nitems1 = ArrayGetNItems(ndims1, dims1);
3342
int nitems2 = ArrayGetNItems(ndims2, dims2);
3343
Oid element_type = ARR_ELEMTYPE(array1);
3345
TypeCacheEntry *typentry;
3356
FunctionCallInfoData locfcinfo;
3358
if (element_type != ARR_ELEMTYPE(array2))
3360
(errcode(ERRCODE_DATATYPE_MISMATCH),
3361
errmsg("cannot compare arrays of different element types")));
3364
* We arrange to look up the comparison function only once per series of
3365
* calls, assuming the element type doesn't change underneath us. The
3366
* typcache is used so that we have no memory leakage when being used as
3367
* an index support function.
3369
typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3370
if (typentry == NULL ||
3371
typentry->type_id != element_type)
3373
typentry = lookup_type_cache(element_type,
3374
TYPECACHE_CMP_PROC_FINFO);
3375
if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
3377
(errcode(ERRCODE_UNDEFINED_FUNCTION),
3378
errmsg("could not identify a comparison function for type %s",
3379
format_type_be(element_type))));
3380
fcinfo->flinfo->fn_extra = (void *) typentry;
3382
typlen = typentry->typlen;
3383
typbyval = typentry->typbyval;
3384
typalign = typentry->typalign;
3387
* apply the operator to each pair of array elements.
3389
InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2,
3390
collation, NULL, NULL);
3392
/* Loop over source data */
3393
min_nitems = Min(nitems1, nitems2);
3394
ptr1 = ARR_DATA_PTR(array1);
3395
ptr2 = ARR_DATA_PTR(array2);
3396
bitmap1 = ARR_NULLBITMAP(array1);
3397
bitmap2 = ARR_NULLBITMAP(array2);
3398
bitmask = 1; /* use same bitmask for both arrays */
3400
for (i = 0; i < min_nitems; i++)
3408
/* Get elements, checking for NULL */
3409
if (bitmap1 && (*bitmap1 & bitmask) == 0)
3417
elt1 = fetch_att(ptr1, typbyval, typlen);
3418
ptr1 = att_addlength_pointer(ptr1, typlen, ptr1);
3419
ptr1 = (char *) att_align_nominal(ptr1, typalign);
3422
if (bitmap2 && (*bitmap2 & bitmask) == 0)
3430
elt2 = fetch_att(ptr2, typbyval, typlen);
3431
ptr2 = att_addlength_pointer(ptr2, typlen, ptr2);
3432
ptr2 = (char *) att_align_nominal(ptr2, typalign);
3435
/* advance bitmap pointers if any */
3437
if (bitmask == 0x100)
3447
* We consider two NULLs equal; NULL > not-NULL.
3449
if (isnull1 && isnull2)
3453
/* arg1 is greater than arg2 */
3459
/* arg1 is less than arg2 */
3464
/* Compare the pair of elements */
3465
locfcinfo.arg[0] = elt1;
3466
locfcinfo.arg[1] = elt2;
3467
locfcinfo.argnull[0] = false;
3468
locfcinfo.argnull[1] = false;
3469
locfcinfo.isnull = false;
3470
cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
3473
continue; /* equal */
3477
/* arg1 is less than arg2 */
3483
/* arg1 is greater than arg2 */
3490
* If arrays contain same data (up to end of shorter one), apply
3491
* additional rules to sort by dimensionality. The relative significance
3492
* of the different bits of information is historical; mainly we just care
3493
* that we don't say "equal" for arrays of different dimensionality.
3497
if (nitems1 != nitems2)
3498
result = (nitems1 < nitems2) ? -1 : 1;
3499
else if (ndims1 != ndims2)
3500
result = (ndims1 < ndims2) ? -1 : 1;
3503
/* this relies on LB array immediately following DIMS array */
3504
for (i = 0; i < ndims1 * 2; i++)
3506
if (dims1[i] != dims2[i])
3508
result = (dims1[i] < dims2[i]) ? -1 : 1;
3515
/* Avoid leaking memory when handed toasted input. */
3516
PG_FREE_IF_COPY(array1, 0);
3517
PG_FREE_IF_COPY(array2, 1);
3523
/*-----------------------------------------------------------------------------
3525
* Hash the elements and combine the results.
3526
*----------------------------------------------------------------------------
3530
hash_array(PG_FUNCTION_ARGS)
3532
ArrayType *array = PG_GETARG_ARRAYTYPE_P(0);
3533
int ndims = ARR_NDIM(array);
3534
int *dims = ARR_DIMS(array);
3535
Oid element_type = ARR_ELEMTYPE(array);
3538
TypeCacheEntry *typentry;
3546
FunctionCallInfoData locfcinfo;
3549
* We arrange to look up the hash function only once per series of calls,
3550
* assuming the element type doesn't change underneath us. The typcache
3551
* is used so that we have no memory leakage when being used as an index
3554
typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3555
if (typentry == NULL ||
3556
typentry->type_id != element_type)
3558
typentry = lookup_type_cache(element_type,
3559
TYPECACHE_HASH_PROC_FINFO);
3560
if (!OidIsValid(typentry->hash_proc_finfo.fn_oid))
3562
(errcode(ERRCODE_UNDEFINED_FUNCTION),
3563
errmsg("could not identify a hash function for type %s",
3564
format_type_be(element_type))));
3565
fcinfo->flinfo->fn_extra = (void *) typentry;
3567
typlen = typentry->typlen;
3568
typbyval = typentry->typbyval;
3569
typalign = typentry->typalign;
3572
* apply the hash function to each array element.
3574
InitFunctionCallInfoData(locfcinfo, &typentry->hash_proc_finfo, 1,
3575
InvalidOid, NULL, NULL);
3577
/* Loop over source data */
3578
nitems = ArrayGetNItems(ndims, dims);
3579
ptr = ARR_DATA_PTR(array);
3580
bitmap = ARR_NULLBITMAP(array);
3583
for (i = 0; i < nitems; i++)
3587
/* Get element, checking for NULL */
3588
if (bitmap && (*bitmap & bitmask) == 0)
3590
/* Treat nulls as having hashvalue 0 */
3597
elt = fetch_att(ptr, typbyval, typlen);
3598
ptr = att_addlength_pointer(ptr, typlen, ptr);
3599
ptr = (char *) att_align_nominal(ptr, typalign);
3601
/* Apply the hash function */
3602
locfcinfo.arg[0] = elt;
3603
locfcinfo.argnull[0] = false;
3604
locfcinfo.isnull = false;
3605
elthash = DatumGetUInt32(FunctionCallInvoke(&locfcinfo));
3608
/* advance bitmap pointer if any */
3612
if (bitmask == 0x100)
3620
* Combine hash values of successive elements by rotating the previous
3621
* value left 1 bit, then XOR'ing in the new element's hash value.
3623
result = (result << 1) | (result >> 31);
3627
/* Avoid leaking memory when handed toasted input. */
3628
PG_FREE_IF_COPY(array, 0);
3630
PG_RETURN_UINT32(result);
3634
/*-----------------------------------------------------------------------------
3635
* array overlap/containment comparisons
3636
* These use the same methods of comparing array elements as array_eq.
3637
* We consider only the elements of the arrays, ignoring dimensionality.
3638
*----------------------------------------------------------------------------
3642
* array_contain_compare :
3643
* compares two arrays for overlap/containment
3645
* When matchall is true, return true if all members of array1 are in array2.
3646
* When matchall is false, return true if any members of array1 are in array2.
3649
array_contain_compare(ArrayType *array1, ArrayType *array2, Oid collation,
3650
bool matchall, void **fn_extra)
3652
bool result = matchall;
3653
Oid element_type = ARR_ELEMTYPE(array1);
3654
TypeCacheEntry *typentry;
3667
FunctionCallInfoData locfcinfo;
3669
if (element_type != ARR_ELEMTYPE(array2))
3671
(errcode(ERRCODE_DATATYPE_MISMATCH),
3672
errmsg("cannot compare arrays of different element types")));
3675
* We arrange to look up the equality function only once per series of
3676
* calls, assuming the element type doesn't change underneath us. The
3677
* typcache is used so that we have no memory leakage when being used as
3678
* an index support function.
3680
typentry = (TypeCacheEntry *) *fn_extra;
3681
if (typentry == NULL ||
3682
typentry->type_id != element_type)
3684
typentry = lookup_type_cache(element_type,
3685
TYPECACHE_EQ_OPR_FINFO);
3686
if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
3688
(errcode(ERRCODE_UNDEFINED_FUNCTION),
3689
errmsg("could not identify an equality operator for type %s",
3690
format_type_be(element_type))));
3691
*fn_extra = (void *) typentry;
3693
typlen = typentry->typlen;
3694
typbyval = typentry->typbyval;
3695
typalign = typentry->typalign;
3698
* Since we probably will need to scan array2 multiple times, it's
3699
* worthwhile to use deconstruct_array on it. We scan array1 the hard way
3700
* however, since we very likely won't need to look at all of it.
3702
deconstruct_array(array2, element_type, typlen, typbyval, typalign,
3703
&values2, &nulls2, &nelems2);
3706
* Apply the comparison operator to each pair of array elements.
3708
InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
3709
collation, NULL, NULL);
3711
/* Loop over source data */
3712
nelems1 = ArrayGetNItems(ARR_NDIM(array1), ARR_DIMS(array1));
3713
ptr1 = ARR_DATA_PTR(array1);
3714
bitmap1 = ARR_NULLBITMAP(array1);
3717
for (i = 0; i < nelems1; i++)
3722
/* Get element, checking for NULL */
3723
if (bitmap1 && (*bitmap1 & bitmask) == 0)
3731
elt1 = fetch_att(ptr1, typbyval, typlen);
3732
ptr1 = att_addlength_pointer(ptr1, typlen, ptr1);
3733
ptr1 = (char *) att_align_nominal(ptr1, typalign);
3736
/* advance bitmap pointer if any */
3738
if (bitmask == 0x100)
3746
* We assume that the comparison operator is strict, so a NULL can't
3747
* match anything. XXX this diverges from the "NULL=NULL" behavior of
3748
* array_eq, should we act like that?
3760
for (j = 0; j < nelems2; j++)
3762
Datum elt2 = values2[j];
3763
bool isnull2 = nulls2[j];
3767
continue; /* can't match */
3770
* Apply the operator to the element pair
3772
locfcinfo.arg[0] = elt1;
3773
locfcinfo.arg[1] = elt2;
3774
locfcinfo.argnull[0] = false;
3775
locfcinfo.argnull[1] = false;
3776
locfcinfo.isnull = false;
3777
oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
3784
/* found a match for elt1 */
3793
/* no match for elt1 */
3809
arrayoverlap(PG_FUNCTION_ARGS)
3811
ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
3812
ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
3813
Oid collation = PG_GET_COLLATION();
3816
result = array_contain_compare(array1, array2, collation, false,
3817
&fcinfo->flinfo->fn_extra);
3819
/* Avoid leaking memory when handed toasted input. */
3820
PG_FREE_IF_COPY(array1, 0);
3821
PG_FREE_IF_COPY(array2, 1);
3823
PG_RETURN_BOOL(result);
3827
arraycontains(PG_FUNCTION_ARGS)
3829
ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
3830
ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
3831
Oid collation = PG_GET_COLLATION();
3834
result = array_contain_compare(array2, array1, collation, true,
3835
&fcinfo->flinfo->fn_extra);
3837
/* Avoid leaking memory when handed toasted input. */
3838
PG_FREE_IF_COPY(array1, 0);
3839
PG_FREE_IF_COPY(array2, 1);
3841
PG_RETURN_BOOL(result);
3845
arraycontained(PG_FUNCTION_ARGS)
3847
ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
3848
ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
3849
Oid collation = PG_GET_COLLATION();
3852
result = array_contain_compare(array1, array2, collation, true,
3853
&fcinfo->flinfo->fn_extra);
3855
/* Avoid leaking memory when handed toasted input. */
3856
PG_FREE_IF_COPY(array1, 0);
3857
PG_FREE_IF_COPY(array2, 1);
3859
PG_RETURN_BOOL(result);
3863
/*-----------------------------------------------------------------------------
3864
* Array iteration functions
3865
* These functions are used to iterate efficiently through arrays
3866
*-----------------------------------------------------------------------------
3870
* array_create_iterator --- set up to iterate through an array
3872
* If slice_ndim is zero, we will iterate element-by-element; the returned
3873
* datums are of the array's element type.
3875
* If slice_ndim is 1..ARR_NDIM(arr), we will iterate by slices: the
3876
* returned datums are of the same array type as 'arr', but of size
3877
* equal to the rightmost N dimensions of 'arr'.
3879
* The passed-in array must remain valid for the lifetime of the iterator.
3882
array_create_iterator(ArrayType *arr, int slice_ndim)
3884
ArrayIterator iterator = palloc0(sizeof(ArrayIteratorData));
3887
* Sanity-check inputs --- caller should have got this right already
3889
Assert(PointerIsValid(arr));
3890
if (slice_ndim < 0 || slice_ndim > ARR_NDIM(arr))
3891
elog(ERROR, "invalid arguments to array_create_iterator");
3894
* Remember basic info about the array and its element type
3896
iterator->arr = arr;
3897
iterator->nullbitmap = ARR_NULLBITMAP(arr);
3898
iterator->nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
3899
get_typlenbyvalalign(ARR_ELEMTYPE(arr),
3901
&iterator->typbyval,
3902
&iterator->typalign);
3905
* Remember the slicing parameters.
3907
iterator->slice_ndim = slice_ndim;
3912
* Get pointers into the array's dims and lbound arrays to represent
3913
* the dims/lbound arrays of a slice. These are the same as the
3914
* rightmost N dimensions of the array.
3916
iterator->slice_dims = ARR_DIMS(arr) + ARR_NDIM(arr) - slice_ndim;
3917
iterator->slice_lbound = ARR_LBOUND(arr) + ARR_NDIM(arr) - slice_ndim;
3920
* Compute number of elements in a slice.
3922
iterator->slice_len = ArrayGetNItems(slice_ndim,
3923
iterator->slice_dims);
3926
* Create workspace for building sub-arrays.
3928
iterator->slice_values = (Datum *)
3929
palloc(iterator->slice_len * sizeof(Datum));
3930
iterator->slice_nulls = (bool *)
3931
palloc(iterator->slice_len * sizeof(bool));
3935
* Initialize our data pointer and linear element number. These will
3936
* advance through the array during array_iterate().
3938
iterator->data_ptr = ARR_DATA_PTR(arr);
3939
iterator->current_item = 0;
3945
* Iterate through the array referenced by 'iterator'.
3947
* As long as there is another element (or slice), return it into
3948
* *value / *isnull, and return true. Return false when no more data.
3951
array_iterate(ArrayIterator iterator, Datum *value, bool *isnull)
3953
/* Done if we have reached the end of the array */
3954
if (iterator->current_item >= iterator->nitems)
3957
if (iterator->slice_ndim == 0)
3960
* Scalar case: return one element.
3962
if (array_get_isnull(iterator->nullbitmap, iterator->current_item++))
3969
/* non-NULL, so fetch the individual Datum to return */
3970
char *p = iterator->data_ptr;
3973
*value = fetch_att(p, iterator->typbyval, iterator->typlen);
3975
/* Move our data pointer forward to the next element */
3976
p = att_addlength_pointer(p, iterator->typlen, p);
3977
p = (char *) att_align_nominal(p, iterator->typalign);
3978
iterator->data_ptr = p;
3984
* Slice case: build and return an array of the requested size.
3987
Datum *values = iterator->slice_values;
3988
bool *nulls = iterator->slice_nulls;
3989
char *p = iterator->data_ptr;
3992
for (i = 0; i < iterator->slice_len; i++)
3994
if (array_get_isnull(iterator->nullbitmap,
3995
iterator->current_item++))
3998
values[i] = (Datum) 0;
4003
values[i] = fetch_att(p, iterator->typbyval, iterator->typlen);
4005
/* Move our data pointer forward to the next element */
4006
p = att_addlength_pointer(p, iterator->typlen, p);
4007
p = (char *) att_align_nominal(p, iterator->typalign);
4011
iterator->data_ptr = p;
4013
result = construct_md_array(values,
4015
iterator->slice_ndim,
4016
iterator->slice_dims,
4017
iterator->slice_lbound,
4018
ARR_ELEMTYPE(iterator->arr),
4021
iterator->typalign);
4024
*value = PointerGetDatum(result);
4031
* Release an ArrayIterator data structure
4034
array_free_iterator(ArrayIterator iterator)
4036
if (iterator->slice_ndim > 0)
4038
pfree(iterator->slice_values);
4039
pfree(iterator->slice_nulls);
4045
/***************************************************************************/
4046
/******************| Support Routines |*****************/
4047
/***************************************************************************/
4050
* Check whether a specific array element is NULL
4052
* nullbitmap: pointer to array's null bitmap (NULL if none)
4053
* offset: 0-based linear element number of array element
4056
array_get_isnull(const bits8 *nullbitmap, int offset)
4058
if (nullbitmap == NULL)
4059
return false; /* assume not null */
4060
if (nullbitmap[offset / 8] & (1 << (offset % 8)))
4061
return false; /* not null */
4066
* Set a specific array element's null-bitmap entry
4068
* nullbitmap: pointer to array's null bitmap (mustn't be NULL)
4069
* offset: 0-based linear element number of array element
4070
* isNull: null status to set
4073
array_set_isnull(bits8 *nullbitmap, int offset, bool isNull)
4077
nullbitmap += offset / 8;
4078
bitmask = 1 << (offset % 8);
4080
*nullbitmap &= ~bitmask;
4082
*nullbitmap |= bitmask;
4086
* Fetch array element at pointer, converted correctly to a Datum
4088
* Caller must have handled case of NULL element
4091
ArrayCast(char *value, bool byval, int len)
4093
return fetch_att(value, byval, len);
4097
* Copy datum to *dest and return total space used (including align padding)
4099
* Caller must have handled case of NULL element
4102
ArrayCastAndSet(Datum src,
4113
store_att_byval(dest, src, typlen);
4115
memmove(dest, DatumGetPointer(src), typlen);
4116
inc = att_align_nominal(typlen, typalign);
4121
inc = att_addlength_datum(0, typlen, src);
4122
memmove(dest, DatumGetPointer(src), inc);
4123
inc = att_align_nominal(inc, typalign);
4130
* Advance ptr over nitems array elements
4132
* ptr: starting location in array
4133
* offset: 0-based linear element number of first element (the one at *ptr)
4134
* nullbitmap: start of array's null bitmap, or NULL if none
4135
* nitems: number of array elements to advance over (>= 0)
4136
* typlen, typbyval, typalign: storage parameters of array element datatype
4138
* It is caller's responsibility to ensure that nitems is within range
4141
array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
4142
int typlen, bool typbyval, char typalign)
4147
/* easy if fixed-size elements and no NULLs */
4148
if (typlen > 0 && !nullbitmap)
4149
return ptr + nitems * ((Size) att_align_nominal(typlen, typalign));
4151
/* seems worth having separate loops for NULL and no-NULLs cases */
4154
nullbitmap += offset / 8;
4155
bitmask = 1 << (offset % 8);
4157
for (i = 0; i < nitems; i++)
4159
if (*nullbitmap & bitmask)
4161
ptr = att_addlength_pointer(ptr, typlen, ptr);
4162
ptr = (char *) att_align_nominal(ptr, typalign);
4165
if (bitmask == 0x100)
4174
for (i = 0; i < nitems; i++)
4176
ptr = att_addlength_pointer(ptr, typlen, ptr);
4177
ptr = (char *) att_align_nominal(ptr, typalign);
4184
* Compute total size of the nitems array elements starting at *ptr
4186
* Parameters same as for array_seek
4189
array_nelems_size(char *ptr, int offset, bits8 *nullbitmap, int nitems,
4190
int typlen, bool typbyval, char typalign)
4192
return array_seek(ptr, offset, nullbitmap, nitems,
4193
typlen, typbyval, typalign) - ptr;
4197
* Copy nitems array elements from srcptr to destptr
4199
* destptr: starting destination location (must be enough room!)
4200
* nitems: number of array elements to copy (>= 0)
4201
* srcptr: starting location in source array
4202
* offset: 0-based linear element number of first element (the one at *srcptr)
4203
* nullbitmap: start of source array's null bitmap, or NULL if none
4204
* typlen, typbyval, typalign: storage parameters of array element datatype
4206
* Returns number of bytes copied
4208
* NB: this does not take care of setting up the destination's null bitmap!
4211
array_copy(char *destptr, int nitems,
4212
char *srcptr, int offset, bits8 *nullbitmap,
4213
int typlen, bool typbyval, char typalign)
4217
numbytes = array_nelems_size(srcptr, offset, nullbitmap, nitems,
4218
typlen, typbyval, typalign);
4219
memcpy(destptr, srcptr, numbytes);
4224
* Copy nitems null-bitmap bits from source to destination
4226
* destbitmap: start of destination array's null bitmap (mustn't be NULL)
4227
* destoffset: 0-based linear element number of first dest element
4228
* srcbitmap: start of source array's null bitmap, or NULL if none
4229
* srcoffset: 0-based linear element number of first source element
4230
* nitems: number of bits to copy (>= 0)
4232
* If srcbitmap is NULL then we assume the source is all-non-NULL and
4233
* fill 1's into the destination bitmap. Note that only the specified
4234
* bits in the destination map are changed, not any before or after.
4236
* Note: this could certainly be optimized using standard bitblt methods.
4237
* However, it's not clear that the typical Postgres array has enough elements
4238
* to make it worth worrying too much. For the moment, KISS.
4241
array_bitmap_copy(bits8 *destbitmap, int destoffset,
4242
const bits8 *srcbitmap, int srcoffset,
4252
return; /* don't risk fetch off end of memory */
4253
destbitmap += destoffset / 8;
4254
destbitmask = 1 << (destoffset % 8);
4255
destbitval = *destbitmap;
4258
srcbitmap += srcoffset / 8;
4259
srcbitmask = 1 << (srcoffset % 8);
4260
srcbitval = *srcbitmap;
4261
while (nitems-- > 0)
4263
if (srcbitval & srcbitmask)
4264
destbitval |= destbitmask;
4266
destbitval &= ~destbitmask;
4268
if (destbitmask == 0x100)
4270
*destbitmap++ = destbitval;
4273
destbitval = *destbitmap;
4276
if (srcbitmask == 0x100)
4281
srcbitval = *srcbitmap;
4284
if (destbitmask != 1)
4285
*destbitmap = destbitval;
4289
while (nitems-- > 0)
4291
destbitval |= destbitmask;
4293
if (destbitmask == 0x100)
4295
*destbitmap++ = destbitval;
4298
destbitval = *destbitmap;
4301
if (destbitmask != 1)
4302
*destbitmap = destbitval;
4307
* Compute space needed for a slice of an array
4309
* We assume the caller has verified that the slice coordinates are valid.
4312
array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
4313
int ndim, int *dim, int *lb,
4315
int typlen, bool typbyval, char typalign)
4328
mda_get_range(ndim, span, st, endp);
4330
/* Pretty easy for fixed element length without nulls ... */
4331
if (typlen > 0 && !arraynullsptr)
4332
return ArrayGetNItems(ndim, span) * att_align_nominal(typlen, typalign);
4334
/* Else gotta do it the hard way */
4335
src_offset = ArrayGetOffset(ndim, dim, lb, st);
4336
ptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
4337
typlen, typbyval, typalign);
4338
mda_get_prod(ndim, dim, prod);
4339
mda_get_offset_values(ndim, dist, prod, span);
4340
for (i = 0; i < ndim; i++)
4347
ptr = array_seek(ptr, src_offset, arraynullsptr, dist[j],
4348
typlen, typbyval, typalign);
4349
src_offset += dist[j];
4351
if (!array_get_isnull(arraynullsptr, src_offset))
4353
inc = att_addlength_pointer(0, typlen, ptr);
4354
inc = att_align_nominal(inc, typalign);
4359
} while ((j = mda_next_tuple(ndim, indx, span)) != -1);
4364
* Extract a slice of an array into consecutive elements in the destination
4367
* We assume the caller has verified that the slice coordinates are valid,
4368
* allocated enough storage for the result, and initialized the header
4372
array_extract_slice(ArrayType *newarray,
4377
bits8 *arraynullsptr,
4384
char *destdataptr = ARR_DATA_PTR(newarray);
4385
bits8 *destnullsptr = ARR_NULLBITMAP(newarray);
4397
src_offset = ArrayGetOffset(ndim, dim, lb, st);
4398
srcdataptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
4399
typlen, typbyval, typalign);
4400
mda_get_prod(ndim, dim, prod);
4401
mda_get_range(ndim, span, st, endp);
4402
mda_get_offset_values(ndim, dist, prod, span);
4403
for (i = 0; i < ndim; i++)
4411
/* skip unwanted elements */
4412
srcdataptr = array_seek(srcdataptr, src_offset, arraynullsptr,
4414
typlen, typbyval, typalign);
4415
src_offset += dist[j];
4417
inc = array_copy(destdataptr, 1,
4418
srcdataptr, src_offset, arraynullsptr,
4419
typlen, typbyval, typalign);
4421
array_bitmap_copy(destnullsptr, dest_offset,
4422
arraynullsptr, src_offset,
4428
} while ((j = mda_next_tuple(ndim, indx, span)) != -1);
4432
* Insert a slice into an array.
4434
* ndim/dim[]/lb[] are dimensions of the original array. A new array with
4435
* those same dimensions is to be constructed. destArray must already
4436
* have been allocated and its header initialized.
4438
* st[]/endp[] identify the slice to be replaced. Elements within the slice
4439
* volume are taken from consecutive elements of the srcArray; elements
4440
* outside it are copied from origArray.
4442
* We assume the caller has verified that the slice coordinates are valid.
4445
array_insert_slice(ArrayType *destArray,
4446
ArrayType *origArray,
4447
ArrayType *srcArray,
4457
char *destPtr = ARR_DATA_PTR(destArray);
4458
char *origPtr = ARR_DATA_PTR(origArray);
4459
char *srcPtr = ARR_DATA_PTR(srcArray);
4460
bits8 *destBitmap = ARR_NULLBITMAP(destArray);
4461
bits8 *origBitmap = ARR_NULLBITMAP(origArray);
4462
bits8 *srcBitmap = ARR_NULLBITMAP(srcArray);
4463
int orignitems = ArrayGetNItems(ARR_NDIM(origArray),
4464
ARR_DIMS(origArray));
4476
dest_offset = ArrayGetOffset(ndim, dim, lb, st);
4477
/* copy items before the slice start */
4478
inc = array_copy(destPtr, dest_offset,
4479
origPtr, 0, origBitmap,
4480
typlen, typbyval, typalign);
4484
array_bitmap_copy(destBitmap, 0, origBitmap, 0, dest_offset);
4485
orig_offset = dest_offset;
4486
mda_get_prod(ndim, dim, prod);
4487
mda_get_range(ndim, span, st, endp);
4488
mda_get_offset_values(ndim, dist, prod, span);
4489
for (i = 0; i < ndim; i++)
4495
/* Copy/advance over elements between here and next part of slice */
4498
inc = array_copy(destPtr, dist[j],
4499
origPtr, orig_offset, origBitmap,
4500
typlen, typbyval, typalign);
4504
array_bitmap_copy(destBitmap, dest_offset,
4505
origBitmap, orig_offset,
4507
dest_offset += dist[j];
4508
orig_offset += dist[j];
4510
/* Copy new element at this slice position */
4511
inc = array_copy(destPtr, 1,
4512
srcPtr, src_offset, srcBitmap,
4513
typlen, typbyval, typalign);
4515
array_bitmap_copy(destBitmap, dest_offset,
4516
srcBitmap, src_offset,
4522
/* Advance over old element at this slice position */
4523
origPtr = array_seek(origPtr, orig_offset, origBitmap, 1,
4524
typlen, typbyval, typalign);
4526
} while ((j = mda_next_tuple(ndim, indx, span)) != -1);
4528
/* don't miss any data at the end */
4529
array_copy(destPtr, orignitems - orig_offset,
4530
origPtr, orig_offset, origBitmap,
4531
typlen, typbyval, typalign);
4533
array_bitmap_copy(destBitmap, dest_offset,
4534
origBitmap, orig_offset,
4535
orignitems - orig_offset);
4539
* accumArrayResult - accumulate one (more) Datum for an array result
4541
* astate is working state (NULL on first call)
4542
* rcontext is where to keep working state
4545
accumArrayResult(ArrayBuildState *astate,
4546
Datum dvalue, bool disnull,
4548
MemoryContext rcontext)
4550
MemoryContext arr_context,
4555
/* First time through --- initialize */
4557
/* Make a temporary context to hold all the junk */
4558
arr_context = AllocSetContextCreate(rcontext,
4560
ALLOCSET_DEFAULT_MINSIZE,
4561
ALLOCSET_DEFAULT_INITSIZE,
4562
ALLOCSET_DEFAULT_MAXSIZE);
4563
oldcontext = MemoryContextSwitchTo(arr_context);
4564
astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
4565
astate->mcontext = arr_context;
4566
astate->alen = 64; /* arbitrary starting array size */
4567
astate->dvalues = (Datum *) palloc(astate->alen * sizeof(Datum));
4568
astate->dnulls = (bool *) palloc(astate->alen * sizeof(bool));
4570
astate->element_type = element_type;
4571
get_typlenbyvalalign(element_type,
4578
oldcontext = MemoryContextSwitchTo(astate->mcontext);
4579
Assert(astate->element_type == element_type);
4580
/* enlarge dvalues[]/dnulls[] if needed */
4581
if (astate->nelems >= astate->alen)
4584
astate->dvalues = (Datum *)
4585
repalloc(astate->dvalues, astate->alen * sizeof(Datum));
4586
astate->dnulls = (bool *)
4587
repalloc(astate->dnulls, astate->alen * sizeof(bool));
4592
* Ensure pass-by-ref stuff is copied into mcontext; and detoast it too if
4593
* it's varlena. (You might think that detoasting is not needed here
4594
* because construct_md_array can detoast the array elements later.
4595
* However, we must not let construct_md_array modify the ArrayBuildState
4596
* because that would mean array_agg_finalfn damages its input, which is
4597
* verboten. Also, this way frequently saves one copying step.)
4599
if (!disnull && !astate->typbyval)
4601
if (astate->typlen == -1)
4602
dvalue = PointerGetDatum(PG_DETOAST_DATUM_COPY(dvalue));
4604
dvalue = datumCopy(dvalue, astate->typbyval, astate->typlen);
4607
astate->dvalues[astate->nelems] = dvalue;
4608
astate->dnulls[astate->nelems] = disnull;
4611
MemoryContextSwitchTo(oldcontext);
4617
* makeArrayResult - produce 1-D final result of accumArrayResult
4619
* astate is working state (not NULL)
4620
* rcontext is where to construct result
4623
makeArrayResult(ArrayBuildState *astate,
4624
MemoryContext rcontext)
4629
dims[0] = astate->nelems;
4632
return makeMdArrayResult(astate, 1, dims, lbs, rcontext, true);
4636
* makeMdArrayResult - produce multi-D final result of accumArrayResult
4638
* beware: no check that specified dimensions match the number of values
4641
* astate is working state (not NULL)
4642
* rcontext is where to construct result
4643
* release is true if okay to release working state
4646
makeMdArrayResult(ArrayBuildState *astate,
4650
MemoryContext rcontext,
4654
MemoryContext oldcontext;
4656
/* Build the final array result in rcontext */
4657
oldcontext = MemoryContextSwitchTo(rcontext);
4659
result = construct_md_array(astate->dvalues,
4664
astate->element_type,
4669
MemoryContextSwitchTo(oldcontext);
4671
/* Clean up all the junk */
4673
MemoryContextDelete(astate->mcontext);
4675
return PointerGetDatum(result);
4679
array_larger(PG_FUNCTION_ARGS)
4685
v1 = PG_GETARG_ARRAYTYPE_P(0);
4686
v2 = PG_GETARG_ARRAYTYPE_P(1);
4688
result = ((array_cmp(fcinfo) > 0) ? v1 : v2);
4690
PG_RETURN_ARRAYTYPE_P(result);
4694
array_smaller(PG_FUNCTION_ARGS)
4700
v1 = PG_GETARG_ARRAYTYPE_P(0);
4701
v2 = PG_GETARG_ARRAYTYPE_P(1);
4703
result = ((array_cmp(fcinfo) < 0) ? v1 : v2);
4705
PG_RETURN_ARRAYTYPE_P(result);
4709
typedef struct generate_subscripts_fctx
4714
} generate_subscripts_fctx;
4717
* generate_subscripts(array anyarray, dim int [, reverse bool])
4718
* Returns all subscripts of the array for any dimension
4721
generate_subscripts(PG_FUNCTION_ARGS)
4723
FuncCallContext *funcctx;
4724
MemoryContext oldcontext;
4725
generate_subscripts_fctx *fctx;
4727
/* stuff done only on the first call of the function */
4728
if (SRF_IS_FIRSTCALL())
4730
ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
4731
int reqdim = PG_GETARG_INT32(1);
4735
/* create a function context for cross-call persistence */
4736
funcctx = SRF_FIRSTCALL_INIT();
4738
/* Sanity check: does it look like an array at all? */
4739
if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
4740
SRF_RETURN_DONE(funcctx);
4742
/* Sanity check: was the requested dim valid */
4743
if (reqdim <= 0 || reqdim > ARR_NDIM(v))
4744
SRF_RETURN_DONE(funcctx);
4747
* switch to memory context appropriate for multiple function calls
4749
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
4750
fctx = (generate_subscripts_fctx *) palloc(sizeof(generate_subscripts_fctx));
4755
fctx->lower = lb[reqdim - 1];
4756
fctx->upper = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
4757
fctx->reverse = (PG_NARGS() < 3) ? false : PG_GETARG_BOOL(2);
4759
funcctx->user_fctx = fctx;
4761
MemoryContextSwitchTo(oldcontext);
4764
funcctx = SRF_PERCALL_SETUP();
4766
fctx = funcctx->user_fctx;
4768
if (fctx->lower <= fctx->upper)
4771
SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->lower++));
4773
SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->upper--));
4776
/* done when there are no more elements left */
4777
SRF_RETURN_DONE(funcctx);
4781
* generate_subscripts_nodir
4782
* Implements the 2-argument version of generate_subscripts
4785
generate_subscripts_nodir(PG_FUNCTION_ARGS)
4787
/* just call the other one -- it can handle both cases */
4788
return generate_subscripts(fcinfo);
4792
* array_fill_with_lower_bounds
4793
* Create and fill array with defined lower bounds.
4796
array_fill_with_lower_bounds(PG_FUNCTION_ARGS)
4805
if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
4807
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
4808
errmsg("dimension array or low bound array cannot be NULL")));
4810
dims = PG_GETARG_ARRAYTYPE_P(1);
4811
lbs = PG_GETARG_ARRAYTYPE_P(2);
4813
if (!PG_ARGISNULL(0))
4815
value = PG_GETARG_DATUM(0);
4824
elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
4825
if (!OidIsValid(elmtype))
4826
elog(ERROR, "could not determine data type of input");
4828
result = array_fill_internal(dims, lbs, value, isnull, elmtype, fcinfo);
4829
PG_RETURN_ARRAYTYPE_P(result);
4834
* Create and fill array with default lower bounds.
4837
array_fill(PG_FUNCTION_ARGS)
4845
if (PG_ARGISNULL(1))
4847
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
4848
errmsg("dimension array or low bound array cannot be NULL")));
4850
dims = PG_GETARG_ARRAYTYPE_P(1);
4852
if (!PG_ARGISNULL(0))
4854
value = PG_GETARG_DATUM(0);
4863
elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
4864
if (!OidIsValid(elmtype))
4865
elog(ERROR, "could not determine data type of input");
4867
result = array_fill_internal(dims, NULL, value, isnull, elmtype, fcinfo);
4868
PG_RETURN_ARRAYTYPE_P(result);
4872
create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
4873
Oid elmtype, int dataoffset)
4877
result = (ArrayType *) palloc0(nbytes);
4878
SET_VARSIZE(result, nbytes);
4879
result->ndim = ndims;
4880
result->dataoffset = dataoffset;
4881
result->elemtype = elmtype;
4882
memcpy(ARR_DIMS(result), dimv, ndims * sizeof(int));
4883
memcpy(ARR_LBOUND(result), lbsv, ndims * sizeof(int));
4889
array_fill_internal(ArrayType *dims, ArrayType *lbs,
4890
Datum value, bool isnull, Oid elmtype,
4891
FunctionCallInfo fcinfo)
4902
ArrayMetaState *my_extra;
4907
if (ARR_NDIM(dims) != 1)
4909
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4910
errmsg("wrong number of array subscripts"),
4911
errdetail("Dimension array must be one dimensional.")));
4913
if (ARR_LBOUND(dims)[0] != 1)
4915
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4916
errmsg("wrong range of array subscripts"),
4917
errdetail("Lower bound of dimension array must be one.")));
4919
if (array_contains_nulls(dims))
4921
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
4922
errmsg("dimension values cannot be null")));
4924
dimv = (int *) ARR_DATA_PTR(dims);
4925
ndims = ARR_DIMS(dims)[0];
4927
if (ndims < 0) /* we do allow zero-dimension arrays */
4929
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4930
errmsg("invalid number of dimensions: %d", ndims)));
4933
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
4934
errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
4939
if (ARR_NDIM(lbs) != 1)
4941
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4942
errmsg("wrong number of array subscripts"),
4943
errdetail("Dimension array must be one dimensional.")));
4945
if (ARR_LBOUND(lbs)[0] != 1)
4947
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4948
errmsg("wrong range of array subscripts"),
4949
errdetail("Lower bound of dimension array must be one.")));
4951
if (array_contains_nulls(lbs))
4953
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
4954
errmsg("dimension values cannot be null")));
4956
if (ARR_DIMS(lbs)[0] != ndims)
4958
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4959
errmsg("wrong number of array subscripts"),
4960
errdetail("Low bound array has different size than dimensions array.")));
4962
lbsv = (int *) ARR_DATA_PTR(lbs);
4968
for (i = 0; i < MAXDIM; i++)
4974
/* fast track for empty array */
4976
return construct_empty_array(elmtype);
4978
nitems = ArrayGetNItems(ndims, dimv);
4981
* We arrange to look up info about element type only once per series of
4982
* calls, assuming the element type doesn't change underneath us.
4984
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
4985
if (my_extra == NULL)
4987
fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
4988
sizeof(ArrayMetaState));
4989
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
4990
my_extra->element_type = InvalidOid;
4993
if (my_extra->element_type != elmtype)
4995
/* Get info about element type */
4996
get_typlenbyvalalign(elmtype,
4998
&my_extra->typbyval,
4999
&my_extra->typalign);
5000
my_extra->element_type = elmtype;
5003
elmlen = my_extra->typlen;
5004
elmbyval = my_extra->typbyval;
5005
elmalign = my_extra->typalign;
5007
/* compute required space */
5015
/* make sure data is not toasted */
5017
value = PointerGetDatum(PG_DETOAST_DATUM(value));
5019
nbytes = att_addlength_datum(0, elmlen, value);
5020
nbytes = att_align_nominal(nbytes, elmalign);
5023
totbytes = nbytes * nitems;
5025
/* check for overflow of multiplication or total request */
5026
if (totbytes / nbytes != nitems ||
5027
!AllocSizeIsValid(totbytes))
5029
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
5030
errmsg("array size exceeds the maximum allowed (%d)",
5031
(int) MaxAllocSize)));
5034
* This addition can't overflow, but it might cause us to go past
5035
* MaxAllocSize. We leave it to palloc to complain in that case.
5037
totbytes += ARR_OVERHEAD_NONULLS(ndims);
5039
result = create_array_envelope(ndims, dimv, lbsv, totbytes,
5042
p = ARR_DATA_PTR(result);
5043
for (i = 0; i < nitems; i++)
5044
p += ArrayCastAndSet(value, elmlen, elmbyval, elmalign, p);
5051
dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
5052
nbytes = dataoffset;
5054
result = create_array_envelope(ndims, dimv, lbsv, nbytes,
5055
elmtype, dataoffset);
5057
/* create_array_envelope already zeroed the bitmap, so we're done */
5068
array_unnest(PG_FUNCTION_ARGS)
5075
char *elemdataptr; /* this moves with nextelem */
5076
bits8 *arraynullsptr; /* this does not */
5080
} array_unnest_fctx;
5082
FuncCallContext *funcctx;
5083
array_unnest_fctx *fctx;
5084
MemoryContext oldcontext;
5086
/* stuff done only on the first call of the function */
5087
if (SRF_IS_FIRSTCALL())
5091
/* create a function context for cross-call persistence */
5092
funcctx = SRF_FIRSTCALL_INIT();
5095
* switch to memory context appropriate for multiple function calls
5097
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
5100
* Get the array value and detoast if needed. We can't do this
5101
* earlier because if we have to detoast, we want the detoasted copy
5102
* to be in multi_call_memory_ctx, so it will go away when we're done
5103
* and not before. (If no detoast happens, we assume the originally
5104
* passed array will stick around till then.)
5106
arr = PG_GETARG_ARRAYTYPE_P(0);
5108
/* allocate memory for user context */
5109
fctx = (array_unnest_fctx *) palloc(sizeof(array_unnest_fctx));
5111
/* initialize state */
5114
fctx->numelems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
5116
fctx->elemdataptr = ARR_DATA_PTR(arr);
5117
fctx->arraynullsptr = ARR_NULLBITMAP(arr);
5119
get_typlenbyvalalign(ARR_ELEMTYPE(arr),
5124
funcctx->user_fctx = fctx;
5125
MemoryContextSwitchTo(oldcontext);
5128
/* stuff done on every call of the function */
5129
funcctx = SRF_PERCALL_SETUP();
5130
fctx = funcctx->user_fctx;
5132
if (fctx->nextelem < fctx->numelems)
5134
int offset = fctx->nextelem++;
5138
* Check for NULL array element
5140
if (array_get_isnull(fctx->arraynullsptr, offset))
5142
fcinfo->isnull = true;
5144
/* elemdataptr does not move */
5149
* OK, get the element
5151
char *ptr = fctx->elemdataptr;
5153
fcinfo->isnull = false;
5154
elem = ArrayCast(ptr, fctx->elmbyval, fctx->elmlen);
5157
* Advance elemdataptr over it
5159
ptr = att_addlength_pointer(ptr, fctx->elmlen, ptr);
5160
ptr = (char *) att_align_nominal(ptr, fctx->elmalign);
5161
fctx->elemdataptr = ptr;
5164
SRF_RETURN_NEXT(funcctx, elem);
5168
/* do when there is no more left */
5169
SRF_RETURN_DONE(funcctx);