Age Owner TLA Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * arrayfuncs.c
4 : * Support functions for arrays.
5 : *
6 : * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : *
10 : * IDENTIFICATION
11 : * src/backend/utils/adt/arrayfuncs.c
12 : *
13 : *-------------------------------------------------------------------------
14 : */
15 : #include "postgres.h"
16 :
17 : #include <ctype.h>
18 : #include <math.h>
19 :
20 : #include "access/htup_details.h"
21 : #include "catalog/pg_type.h"
22 : #include "funcapi.h"
23 : #include "libpq/pqformat.h"
24 : #include "nodes/nodeFuncs.h"
25 : #include "nodes/supportnodes.h"
26 : #include "optimizer/optimizer.h"
27 : #include "port/pg_bitutils.h"
28 : #include "utils/array.h"
29 : #include "utils/arrayaccess.h"
30 : #include "utils/builtins.h"
31 : #include "utils/datum.h"
32 : #include "utils/fmgroids.h"
33 : #include "utils/lsyscache.h"
34 : #include "utils/memutils.h"
35 : #include "utils/selfuncs.h"
36 : #include "utils/typcache.h"
37 :
38 :
39 : /*
40 : * GUC parameter
41 : */
42 : bool Array_nulls = true;
43 :
44 : /*
45 : * Local definitions
46 : */
47 : #define ASSGN "="
48 :
49 : #define AARR_FREE_IF_COPY(array,n) \
50 : do { \
51 : if (!VARATT_IS_EXPANDED_HEADER(array)) \
52 : PG_FREE_IF_COPY(array, n); \
53 : } while (0)
54 :
55 : typedef enum
56 : {
57 : ARRAY_NO_LEVEL,
58 : ARRAY_LEVEL_STARTED,
59 : ARRAY_ELEM_STARTED,
60 : ARRAY_ELEM_COMPLETED,
61 : ARRAY_QUOTED_ELEM_STARTED,
62 : ARRAY_QUOTED_ELEM_COMPLETED,
63 : ARRAY_ELEM_DELIMITED,
64 : ARRAY_LEVEL_COMPLETED,
65 : ARRAY_LEVEL_DELIMITED
66 : } ArrayParseState;
67 :
68 : /* Working state for array_iterate() */
69 : typedef struct ArrayIteratorData
70 : {
71 : /* basic info about the array, set up during array_create_iterator() */
72 : ArrayType *arr; /* array we're iterating through */
73 : bits8 *nullbitmap; /* its null bitmap, if any */
74 : int nitems; /* total number of elements in array */
75 : int16 typlen; /* element type's length */
76 : bool typbyval; /* element type's byval property */
77 : char typalign; /* element type's align property */
78 :
79 : /* information about the requested slice size */
80 : int slice_ndim; /* slice dimension, or 0 if not slicing */
81 : int slice_len; /* number of elements per slice */
82 : int *slice_dims; /* slice dims array */
83 : int *slice_lbound; /* slice lbound array */
84 : Datum *slice_values; /* workspace of length slice_len */
85 : bool *slice_nulls; /* workspace of length slice_len */
86 :
87 : /* current position information, updated on each iteration */
88 : char *data_ptr; /* our current position in the array */
89 : int current_item; /* the item # we're at in the array */
90 : } ArrayIteratorData;
91 :
92 : static bool array_isspace(char ch);
93 : static int ArrayCount(const char *str, int *dim, char typdelim,
94 : Node *escontext);
95 : static bool ReadArrayStr(char *arrayStr, const char *origStr,
96 : int nitems, int ndim, int *dim,
97 : FmgrInfo *inputproc, Oid typioparam, int32 typmod,
98 : char typdelim,
99 : int typlen, bool typbyval, char typalign,
100 : Datum *values, bool *nulls,
101 : bool *hasnulls, int32 *nbytes, Node *escontext);
102 : static void ReadArrayBinary(StringInfo buf, int nitems,
103 : FmgrInfo *receiveproc, Oid typioparam, int32 typmod,
104 : int typlen, bool typbyval, char typalign,
105 : Datum *values, bool *nulls,
106 : bool *hasnulls, int32 *nbytes);
107 : static Datum array_get_element_expanded(Datum arraydatum,
108 : int nSubscripts, int *indx,
109 : int arraytyplen,
110 : int elmlen, bool elmbyval, char elmalign,
111 : bool *isNull);
112 : static Datum array_set_element_expanded(Datum arraydatum,
113 : int nSubscripts, int *indx,
114 : Datum dataValue, bool isNull,
115 : int arraytyplen,
116 : int elmlen, bool elmbyval, char elmalign);
117 : static bool array_get_isnull(const bits8 *nullbitmap, int offset);
118 : static void array_set_isnull(bits8 *nullbitmap, int offset, bool isNull);
119 : static Datum ArrayCast(char *value, bool byval, int len);
120 : static int ArrayCastAndSet(Datum src,
121 : int typlen, bool typbyval, char typalign,
122 : char *dest);
123 : static char *array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
124 : int typlen, bool typbyval, char typalign);
125 : static int array_nelems_size(char *ptr, int offset, bits8 *nullbitmap,
126 : int nitems, int typlen, bool typbyval, char typalign);
127 : static int array_copy(char *destptr, int nitems,
128 : char *srcptr, int offset, bits8 *nullbitmap,
129 : int typlen, bool typbyval, char typalign);
130 : static int array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
131 : int ndim, int *dim, int *lb,
132 : int *st, int *endp,
133 : int typlen, bool typbyval, char typalign);
134 : static void array_extract_slice(ArrayType *newarray,
135 : int ndim, int *dim, int *lb,
136 : char *arraydataptr, bits8 *arraynullsptr,
137 : int *st, int *endp,
138 : int typlen, bool typbyval, char typalign);
139 : static void array_insert_slice(ArrayType *destArray, ArrayType *origArray,
140 : ArrayType *srcArray,
141 : int ndim, int *dim, int *lb,
142 : int *st, int *endp,
143 : int typlen, bool typbyval, char typalign);
144 : static int array_cmp(FunctionCallInfo fcinfo);
145 : static ArrayType *create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
146 : Oid elmtype, int dataoffset);
147 : static ArrayType *array_fill_internal(ArrayType *dims, ArrayType *lbs,
148 : Datum value, bool isnull, Oid elmtype,
149 : FunctionCallInfo fcinfo);
150 : static ArrayType *array_replace_internal(ArrayType *array,
151 : Datum search, bool search_isnull,
152 : Datum replace, bool replace_isnull,
153 : bool remove, Oid collation,
154 : FunctionCallInfo fcinfo);
155 : static int width_bucket_array_float8(Datum operand, ArrayType *thresholds);
156 : static int width_bucket_array_fixed(Datum operand,
157 : ArrayType *thresholds,
158 : Oid collation,
159 : TypeCacheEntry *typentry);
160 : static int width_bucket_array_variable(Datum operand,
161 : ArrayType *thresholds,
162 : Oid collation,
163 : TypeCacheEntry *typentry);
164 :
165 :
166 : /*
167 : * array_in :
168 : * converts an array from the external format in "string" to
169 : * its internal format.
170 : *
171 : * return value :
172 : * the internal representation of the input array
173 : */
174 : Datum
8335 tgl 175 GIC 182316 : array_in(PG_FUNCTION_ARGS)
9770 scrappy 176 ECB : {
8053 bruce 177 GIC 182316 : char *string = PG_GETARG_CSTRING(0); /* external form */
2118 tgl 178 CBC 182316 : Oid element_type = PG_GETARG_OID(1); /* type of an array
2118 tgl 179 ECB : * element */
6385 bruce 180 GIC 182316 : int32 typmod = PG_GETARG_INT32(2); /* typmod for array elements */
121 tgl 181 GNC 182316 : Node *escontext = fcinfo->context;
9344 bruce 182 ECB : int typlen;
8181 tgl 183 : bool typbyval;
184 : char typalign;
185 : char typdelim;
186 : Oid typioparam;
187 : char *string_save,
188 : *p;
189 : int i,
190 : nitems;
191 : Datum *dataPtr;
192 : bool *nullsPtr;
193 : bool hasnulls;
194 : int32 nbytes;
195 : int32 dataoffset;
196 : ArrayType *retval;
197 : int ndim,
198 : dim[MAXDIM],
199 : lBound[MAXDIM];
200 : ArrayMetaState *my_extra;
201 :
202 : /*
203 : * We arrange to look up info about element type, including its input
204 : * conversion proc, only once per series of calls, assuming the element
205 : * type doesn't change underneath us.
206 : */
7226 tgl 207 GIC 182316 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
208 182316 : if (my_extra == NULL)
7226 tgl 209 ECB : {
7226 tgl 210 CBC 129675 : fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
211 : sizeof(ArrayMetaState));
212 129675 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
6446 tgl 213 GIC 129675 : my_extra->element_type = ~element_type;
7226 tgl 214 ECB : }
215 :
7226 tgl 216 GIC 182316 : if (my_extra->element_type != element_type)
217 : {
7188 bruce 218 ECB : /*
219 : * Get info about element type, including its input conversion proc
220 : */
7226 tgl 221 GIC 129675 : get_type_io_data(element_type, IOFunc_input,
222 : &my_extra->typlen, &my_extra->typbyval,
7226 tgl 223 ECB : &my_extra->typalign, &my_extra->typdelim,
224 : &my_extra->typioparam, &my_extra->typiofunc);
7226 tgl 225 GIC 129675 : fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
226 129675 : fcinfo->flinfo->fn_mcxt);
7226 tgl 227 CBC 129675 : my_extra->element_type = element_type;
7226 tgl 228 ECB : }
7226 tgl 229 CBC 182316 : typlen = my_extra->typlen;
7226 tgl 230 GIC 182316 : typbyval = my_extra->typbyval;
7226 tgl 231 CBC 182316 : typalign = my_extra->typalign;
232 182316 : typdelim = my_extra->typdelim;
6881 233 182316 : typioparam = my_extra->typioparam;
9345 bruce 234 ECB :
8181 tgl 235 : /* Make a modifiable copy of the input */
6798 mail 236 GIC 182316 : string_save = pstrdup(string);
237 :
8181 tgl 238 ECB : /*
239 : * If the input string starts with dimension info, read and use that.
240 : * Otherwise, we require the input to be in curly-brace style, and we
241 : * prescan the input to determine dimensions.
242 : *
243 : * Dimension info takes the form of one or more [n] or [m:n] items. The
244 : * outer loop iterates once per dimension item.
245 : */
8181 tgl 246 GIC 182316 : p = string_save;
247 182316 : ndim = 0;
8181 tgl 248 ECB : for (;;)
9345 bruce 249 CBC 57 : {
250 : char *q;
8181 tgl 251 ECB : int ub;
252 :
253 : /*
254 : * Note: we currently allow whitespace between, but not within,
255 : * dimension items.
256 : */
4614 tgl 257 GIC 182379 : while (array_isspace(*p))
9345 bruce 258 6 : p++;
8181 tgl 259 CBC 182373 : if (*p != '[')
260 182316 : break; /* no more dimension items */
261 57 : p++;
262 57 : if (ndim >= MAXDIM)
121 tgl 263 UNC 0 : ereturn(escontext, (Datum) 0,
7196 tgl 264 ECB : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7136 peter_e 265 EUB : errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
266 : ndim + 1, MAXDIM)));
267 :
3050 tgl 268 GIC 129 : for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++)
269 : /* skip */ ;
8181 tgl 270 CBC 57 : if (q == p) /* no digits? */
121 tgl 271 UNC 0 : ereturn(escontext, (Datum) 0,
7196 tgl 272 ECB : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
3050 tgl 273 EUB : errmsg("malformed array literal: \"%s\"", string),
274 : errdetail("\"[\" must introduce explicitly-specified array dimensions.")));
275 :
8181 tgl 276 GIC 57 : if (*q == ':')
277 : {
8181 tgl 278 ECB : /* [m:n] format */
9345 bruce 279 GIC 57 : *q = '\0';
8181 tgl 280 57 : lBound[ndim] = atoi(p);
9345 bruce 281 CBC 57 : p = q + 1;
3050 tgl 282 123 : for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++)
3050 tgl 283 ECB : /* skip */ ;
8181 tgl 284 CBC 57 : if (q == p) /* no digits? */
121 tgl 285 UNC 0 : ereturn(escontext, (Datum) 0,
7196 tgl 286 ECB : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
3050 tgl 287 EUB : errmsg("malformed array literal: \"%s\"", string),
288 : errdetail("Missing array dimension value.")));
289 : }
290 : else
291 : {
292 : /* [n] format */
8181 tgl 293 UIC 0 : lBound[ndim] = 1;
294 : }
8181 tgl 295 GBC 57 : if (*q != ']')
121 tgl 296 UNC 0 : ereturn(escontext, (Datum) 0,
7196 tgl 297 ECB : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
3050 tgl 298 EUB : errmsg("malformed array literal: \"%s\"", string),
299 : errdetail("Missing \"%s\" after array dimensions.",
300 : "]")));
301 :
8181 tgl 302 GIC 57 : *q = '\0';
303 57 : ub = atoi(p);
8181 tgl 304 CBC 57 : p = q + 1;
305 57 : if (ub < lBound[ndim])
121 tgl 306 UNC 0 : ereturn(escontext, (Datum) 0,
7196 tgl 307 ECB : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
6385 bruce 308 EUB : errmsg("upper bound cannot be less than lower bound")));
309 :
8181 tgl 310 GIC 57 : dim[ndim] = ub - lBound[ndim] + 1;
311 57 : ndim++;
9345 bruce 312 ECB : }
313 :
9345 bruce 314 GIC 182316 : if (ndim == 0)
315 : {
8181 tgl 316 ECB : /* No array dimensions, so intuit dimensions from brace structure */
8181 tgl 317 GIC 182275 : if (*p != '{')
121 tgl 318 GNC 1 : ereturn(escontext, (Datum) 0,
7196 tgl 319 ECB : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
3050 320 : errmsg("malformed array literal: \"%s\"", string),
321 : errdetail("Array value must start with \"{\" or dimension information.")));
121 tgl 322 GNC 182274 : ndim = ArrayCount(p, dim, typdelim, escontext);
323 182254 : if (ndim < 0)
324 3 : PG_RETURN_NULL();
8181 tgl 325 GIC 361151 : for (i = 0; i < ndim; i++)
8181 tgl 326 CBC 178900 : lBound[i] = 1;
9345 bruce 327 ECB : }
328 : else
329 : {
6821 mail 330 : int ndim_braces,
331 : dim_braces[MAXDIM];
332 :
333 : /* If array dimensions are given, expect '=' operator */
8295 tgl 334 GIC 41 : if (strncmp(p, ASSGN, strlen(ASSGN)) != 0)
121 tgl 335 UNC 0 : ereturn(escontext, (Datum) 0,
336 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
337 : errmsg("malformed array literal: \"%s\"", string),
3050 tgl 338 ECB : errdetail("Missing \"%s\" after array dimensions.",
3050 tgl 339 EUB : ASSGN)));
9345 bruce 340 GIC 41 : p += strlen(ASSGN);
4614 tgl 341 41 : while (array_isspace(*p))
9345 bruce 342 UIC 0 : p++;
343 :
6821 mail 344 ECB : /*
6385 bruce 345 : * intuit dimensions from brace structure -- it better match what we
6385 bruce 346 EUB : * were given
347 : */
6821 mail 348 GIC 41 : if (*p != '{')
121 tgl 349 UNC 0 : ereturn(escontext, (Datum) 0,
350 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
351 : errmsg("malformed array literal: \"%s\"", string),
3050 tgl 352 ECB : errdetail("Array contents must start with \"{\".")));
121 tgl 353 GNC 41 : ndim_braces = ArrayCount(p, dim_braces, typdelim, escontext);
354 41 : if (ndim_braces < 0)
121 tgl 355 UNC 0 : PG_RETURN_NULL();
6821 mail 356 GIC 41 : if (ndim_braces != ndim)
121 tgl 357 UNC 0 : ereturn(escontext, (Datum) 0,
358 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
3050 tgl 359 ECB : errmsg("malformed array literal: \"%s\"", string),
360 : errdetail("Specified array dimensions do not match array contents.")));
6821 mail 361 GBC 98 : for (i = 0; i < ndim; ++i)
6821 mail 362 ECB : {
6821 mail 363 GBC 57 : if (dim[i] != dim_braces[i])
121 tgl 364 UNC 0 : ereturn(escontext, (Datum) 0,
365 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
366 : errmsg("malformed array literal: \"%s\"", string),
3050 tgl 367 ECB : errdetail("Specified array dimensions do not match array contents.")));
368 : }
9345 bruce 369 : }
9345 bruce 370 EUB :
371 : #ifdef ARRAYDEBUG
372 : printf("array_in- ndim %d (", ndim);
373 : for (i = 0; i < ndim; i++)
374 : {
375 : printf(" %d", dim[i]);
376 : };
377 : printf(") for %s\n", string);
378 : #endif
379 :
380 : /* This checks for overflow of the array dimensions */
121 tgl 381 GNC 182292 : nitems = ArrayGetNItemsSafe(ndim, dim, escontext);
382 182292 : if (nitems < 0)
121 tgl 383 UNC 0 : PG_RETURN_NULL();
121 tgl 384 GNC 182292 : if (!ArrayCheckBoundsSafe(ndim, dim, lBound, escontext))
121 tgl 385 UNC 0 : PG_RETURN_NULL();
386 :
387 : /* Empty array? */
9345 bruce 388 GIC 182292 : if (nitems == 0)
6352 tgl 389 3543 : PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
6352 tgl 390 ECB :
6352 tgl 391 CBC 178749 : dataPtr = (Datum *) palloc(nitems * sizeof(Datum));
6352 tgl 392 GBC 178749 : nullsPtr = (bool *) palloc(nitems * sizeof(bool));
121 tgl 393 GNC 178749 : if (!ReadArrayStr(p, string,
394 : nitems, ndim, dim,
395 : &my_extra->proc, typioparam, typmod,
396 : typdelim,
397 : typlen, typbyval, typalign,
398 : dataPtr, nullsPtr,
399 : &hasnulls, &nbytes, escontext))
400 6 : PG_RETURN_NULL();
6352 tgl 401 CBC 178733 : if (hasnulls)
9345 bruce 402 ECB : {
6352 tgl 403 CBC 187 : dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
6352 tgl 404 GIC 187 : nbytes += dataoffset;
405 : }
406 : else
407 : {
408 178546 : dataoffset = 0; /* marker for no null bitmap */
409 178546 : nbytes += ARR_OVERHEAD_NONULLS(ndim);
6352 tgl 410 ECB : }
5476 tgl 411 CBC 178733 : retval = (ArrayType *) palloc0(nbytes);
5885 tgl 412 GIC 178733 : SET_VARSIZE(retval, nbytes);
8296 tgl 413 CBC 178733 : retval->ndim = ndim;
6352 414 178733 : retval->dataoffset = dataoffset;
415 :
416 : /*
417 : * This comes from the array's pg_type.typelem (which points to the base
4790 bruce 418 ECB : * data type's pg_type.oid) and stores system oids in user tables. This
419 : * oid must be preserved by binary upgrades.
420 : */
7531 tgl 421 CBC 178733 : retval->elemtype = element_type;
7032 neilc 422 178733 : memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
423 178733 : memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
8296 tgl 424 ECB :
6352 tgl 425 GIC 178733 : CopyArrayEls(retval,
426 : dataPtr, nullsPtr, nitems,
427 : typlen, typbyval, typalign,
428 : true);
429 :
8296 430 178733 : pfree(dataPtr);
6352 tgl 431 CBC 178733 : pfree(nullsPtr);
9345 bruce 432 178733 : pfree(string_save);
6352 tgl 433 ECB :
8301 tgl 434 GIC 178733 : PG_RETURN_ARRAYTYPE_P(retval);
9770 scrappy 435 ECB : }
436 :
437 : /*
438 : * array_isspace() --- a non-locale-dependent isspace()
439 : *
4614 tgl 440 : * We used to use isspace() for parsing array values, but that has
441 : * undesirable results: an array value might be silently interpreted
442 : * differently depending on the locale setting. Now we just hard-wire
443 : * the traditional ASCII definition of isspace().
444 : */
445 : static bool
4614 tgl 446 GIC 17324899 : array_isspace(char ch)
447 : {
448 17324899 : if (ch == ' ' ||
449 17284869 : ch == '\t' ||
450 17284826 : ch == '\n' ||
451 17284822 : ch == '\r' ||
452 17284821 : ch == '\v' ||
453 : ch == '\f')
454 40079 : return true;
455 17284820 : return false;
4614 tgl 456 ECB : }
457 :
6352 458 : /*
8296 459 : * ArrayCount
6352 460 : * Determines the dimensions for an array string.
461 : *
462 : * Returns number of dimensions as function result. The axis lengths are
463 : * returned in dim[], which must be of size MAXDIM.
464 : *
465 : * If we detect an error, fill *escontext with error details and return -1
466 : * (unless escontext isn't provided, in which case errors will be thrown).
9770 scrappy 467 : */
468 : static int
121 tgl 469 GNC 182315 : ArrayCount(const char *str, int *dim, char typdelim, Node *escontext)
470 : {
6797 bruce 471 GIC 182315 : int nest_level = 0,
472 : i;
473 182315 : int ndim = 1,
474 : temp[MAXDIM],
475 : nelems[MAXDIM],
476 : nelems_last[MAXDIM];
6768 tgl 477 182315 : bool in_quotes = false;
6797 bruce 478 182315 : bool eoArray = false;
479 182315 : bool empty_array = true;
480 : const char *ptr;
481 182315 : ArrayParseState parse_state = ARRAY_NO_LEVEL;
9345 bruce 482 ECB :
9345 bruce 483 GIC 1276205 : for (i = 0; i < MAXDIM; ++i)
6821 mail 484 ECB : {
3354 bruce 485 GIC 1093890 : temp[i] = dim[i] = nelems_last[i] = 0;
3354 bruce 486 CBC 1093890 : nelems[i] = 1;
487 : }
488 :
7694 tgl 489 GIC 182315 : ptr = str;
7694 tgl 490 CBC 1076238 : while (!eoArray)
9345 bruce 491 ECB : {
7694 tgl 492 CBC 893940 : bool itemdone = false;
493 :
494 6481631 : while (!itemdone)
495 : {
6798 mail 496 5587708 : if (parse_state == ARRAY_ELEM_STARTED ||
497 : parse_state == ARRAY_QUOTED_ELEM_STARTED)
498 4310245 : empty_array = false;
6797 bruce 499 ECB :
7694 tgl 500 GIC 5587708 : switch (*ptr)
501 : {
9344 bruce 502 CBC 3 : case '\0':
7694 tgl 503 ECB : /* Signal a premature end of the string */
121 tgl 504 GNC 3 : ereturn(escontext, -1,
7196 tgl 505 ECB : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
506 : errmsg("malformed array literal: \"%s\"", str),
3050 507 : errdetail("Unexpected end of input.")));
7694 tgl 508 CBC 71 : case '\\':
509 :
6818 mail 510 ECB : /*
511 : * An escape must be after a level start, after an element
6385 bruce 512 : * start, or after an element delimiter. In any case we
513 : * now must be past an element start.
6818 mail 514 : */
6818 mail 515 GIC 71 : if (parse_state != ARRAY_LEVEL_STARTED &&
6818 mail 516 CBC 71 : parse_state != ARRAY_ELEM_STARTED &&
6818 mail 517 GIC 3 : parse_state != ARRAY_QUOTED_ELEM_STARTED &&
518 : parse_state != ARRAY_ELEM_DELIMITED)
121 tgl 519 GNC 3 : ereturn(escontext, -1,
6385 bruce 520 ECB : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
521 : errmsg("malformed array literal: \"%s\"", str),
522 : errdetail("Unexpected \"%c\" character.",
523 : '\\')));
6818 mail 524 GIC 68 : if (parse_state != ARRAY_QUOTED_ELEM_STARTED)
6818 mail 525 UIC 0 : parse_state = ARRAY_ELEM_STARTED;
526 : /* skip the escaped character */
7694 tgl 527 CBC 68 : if (*(ptr + 1))
528 68 : ptr++;
7694 tgl 529 ECB : else
121 tgl 530 UNC 0 : ereturn(escontext, -1,
6385 bruce 531 ECB : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
532 : errmsg("malformed array literal: \"%s\"", str),
533 : errdetail("Unexpected end of input.")));
7694 tgl 534 GIC 68 : break;
2665 peter_e 535 362646 : case '"':
6797 bruce 536 ECB :
6818 mail 537 EUB : /*
538 : * A quote must be after a level start, after a quoted
6385 bruce 539 ECB : * element start, or after an element delimiter. In any
540 : * case we now must be past an element start.
541 : */
6818 mail 542 GBC 362646 : if (parse_state != ARRAY_LEVEL_STARTED &&
6818 mail 543 GIC 138696 : parse_state != ARRAY_QUOTED_ELEM_STARTED &&
544 : parse_state != ARRAY_ELEM_DELIMITED)
121 tgl 545 UNC 0 : ereturn(escontext, -1,
6385 bruce 546 ECB : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
2118 tgl 547 : errmsg("malformed array literal: \"%s\"", str),
548 : errdetail("Unexpected array element.")));
6768 tgl 549 GIC 362646 : in_quotes = !in_quotes;
550 362646 : if (in_quotes)
6818 mail 551 181323 : parse_state = ARRAY_QUOTED_ELEM_STARTED;
552 : else
553 181323 : parse_state = ARRAY_QUOTED_ELEM_COMPLETED;
9344 bruce 554 CBC 362646 : break;
555 182960 : case '{':
6768 tgl 556 GIC 182960 : if (!in_quotes)
9344 bruce 557 EUB : {
558 : /*
559 : * A left brace can occur if no nesting has occurred
560 : * yet, after a level start, or after a level
6385 bruce 561 ECB : * delimiter.
6818 mail 562 : */
6818 mail 563 CBC 182931 : if (parse_state != ARRAY_NO_LEVEL &&
6818 mail 564 GIC 354 : parse_state != ARRAY_LEVEL_STARTED &&
6818 mail 565 ECB : parse_state != ARRAY_LEVEL_DELIMITED)
121 tgl 566 GNC 3 : ereturn(escontext, -1,
2118 tgl 567 ECB : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
568 : errmsg("malformed array literal: \"%s\"", str),
569 : errdetail("Unexpected \"%c\" character.",
570 : '{')));
6818 mail 571 GIC 182928 : parse_state = ARRAY_LEVEL_STARTED;
7694 tgl 572 182928 : if (nest_level >= MAXDIM)
121 tgl 573 GNC 1 : ereturn(escontext, -1,
574 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
6385 bruce 575 ECB : errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
4450 itagaki.takahiro 576 : nest_level + 1, MAXDIM)));
9344 bruce 577 GIC 182927 : temp[nest_level] = 0;
9344 bruce 578 CBC 182927 : nest_level++;
7694 tgl 579 GIC 182927 : if (ndim < nest_level)
580 227 : ndim = nest_level;
581 : }
9344 bruce 582 182956 : break;
9344 bruce 583 CBC 182927 : case '}':
6768 tgl 584 182927 : if (!in_quotes)
9344 bruce 585 ECB : {
586 : /*
587 : * A right brace can occur after an element start, an
588 : * element completion, a quoted element completion, or
6385 589 : * a level completion.
6818 mail 590 : */
6818 mail 591 CBC 182898 : if (parse_state != ARRAY_ELEM_STARTED &&
592 46476 : parse_state != ARRAY_ELEM_COMPLETED &&
6818 mail 593 GIC 3794 : parse_state != ARRAY_QUOTED_ELEM_COMPLETED &&
6798 mail 594 CBC 3552 : parse_state != ARRAY_LEVEL_COMPLETED &&
6797 bruce 595 3549 : !(nest_level == 1 && parse_state == ARRAY_LEVEL_STARTED))
121 tgl 596 GNC 3 : ereturn(escontext, -1,
597 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
598 : errmsg("malformed array literal: \"%s\"", str),
599 : errdetail("Unexpected \"%c\" character.",
600 : '}')));
6818 mail 601 GIC 182895 : parse_state = ARRAY_LEVEL_COMPLETED;
7694 tgl 602 182895 : if (nest_level == 0)
121 tgl 603 UNC 0 : ereturn(escontext, -1,
2118 tgl 604 ECB : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
605 : errmsg("malformed array literal: \"%s\"", str),
606 : errdetail("Unmatched \"%c\" character.", '}')));
9344 bruce 607 CBC 182895 : nest_level--;
6821 mail 608 ECB :
3354 bruce 609 GIC 182895 : if (nelems_last[nest_level] != 0 &&
610 385 : nelems[nest_level] != nelems_last[nest_level])
121 tgl 611 GNC 1 : ereturn(escontext, -1,
612 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
2118 tgl 613 ECB : errmsg("malformed array literal: \"%s\"", str),
614 : errdetail("Multidimensional arrays must have "
2118 tgl 615 EUB : "sub-arrays with matching "
616 : "dimensions.")));
6821 mail 617 GIC 182894 : nelems_last[nest_level] = nelems[nest_level];
618 182894 : nelems[nest_level] = 1;
9344 bruce 619 CBC 182894 : if (nest_level == 0)
7694 tgl 620 GIC 182298 : eoArray = itemdone = true;
7694 tgl 621 ECB : else
622 : {
623 : /*
624 : * We don't set itemdone here; see comments in
625 : * ReadArrayStr
626 : */
7694 tgl 627 GIC 596 : temp[nest_level - 1]++;
628 : }
9344 bruce 629 ECB : }
9344 bruce 630 CBC 182923 : break;
631 4859101 : default:
6768 tgl 632 4859101 : if (!in_quotes)
633 : {
6818 mail 634 GIC 3716324 : if (*ptr == typdelim)
635 : {
636 : /*
637 : * Delimiters can occur after an element start, an
638 : * element completion, a quoted element
6385 bruce 639 ECB : * completion, or a level completion.
640 : */
6818 mail 641 GIC 711625 : if (parse_state != ARRAY_ELEM_STARTED &&
6818 mail 642 CBC 138992 : parse_state != ARRAY_ELEM_COMPLETED &&
6385 bruce 643 354 : parse_state != ARRAY_QUOTED_ELEM_COMPLETED &&
6818 mail 644 ECB : parse_state != ARRAY_LEVEL_COMPLETED)
121 tgl 645 UNC 0 : ereturn(escontext, -1,
2118 tgl 646 ECB : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
647 : errmsg("malformed array literal: \"%s\"", str),
648 : errdetail("Unexpected \"%c\" character.",
649 : typdelim)));
6818 mail 650 GIC 711625 : if (parse_state == ARRAY_LEVEL_COMPLETED)
651 354 : parse_state = ARRAY_LEVEL_DELIMITED;
652 : else
6818 mail 653 CBC 711271 : parse_state = ARRAY_ELEM_DELIMITED;
654 711625 : itemdone = true;
655 711625 : nelems[nest_level - 1]++;
656 : }
4614 tgl 657 GBC 3004699 : else if (!array_isspace(*ptr))
658 : {
659 : /*
660 : * Other non-space characters must be after a
661 : * level start, after an element start, or after
6385 bruce 662 ECB : * an element delimiter. In any case we now must
663 : * be past an element start.
664 : */
6818 mail 665 CBC 2985611 : if (parse_state != ARRAY_LEVEL_STARTED &&
666 572575 : parse_state != ARRAY_ELEM_STARTED &&
6818 mail 667 ECB : parse_state != ARRAY_ELEM_DELIMITED)
121 tgl 668 GNC 3 : ereturn(escontext, -1,
2118 tgl 669 ECB : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
670 : errmsg("malformed array literal: \"%s\"", str),
671 : errdetail("Unexpected array element.")));
6818 mail 672 GIC 2985608 : parse_state = ARRAY_ELEM_STARTED;
673 : }
674 : }
9344 bruce 675 4859098 : break;
676 : }
7694 tgl 677 CBC 5587691 : if (!itemdone)
678 4693768 : ptr++;
679 : }
9345 bruce 680 893923 : temp[ndim - 1]++;
7694 tgl 681 GIC 893923 : ptr++;
682 : }
683 :
6798 mail 684 ECB : /* only whitespace is allowed after the closing brace */
6798 mail 685 GIC 182298 : while (*ptr)
686 : {
4614 tgl 687 CBC 6 : if (!array_isspace(*ptr++))
121 tgl 688 GNC 6 : ereturn(escontext, -1,
6797 bruce 689 ECB : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
3050 tgl 690 : errmsg("malformed array literal: \"%s\"", str),
691 : errdetail("Junk after closing right brace.")));
6798 mail 692 : }
6797 bruce 693 :
694 : /* special case for an empty array */
6798 mail 695 GIC 182292 : if (empty_array)
696 3543 : return 0;
6797 bruce 697 ECB :
9345 bruce 698 GIC 357706 : for (i = 0; i < ndim; ++i)
9345 bruce 699 CBC 178957 : dim[i] = temp[i];
9345 bruce 700 ECB :
8986 bruce 701 GIC 178749 : return ndim;
702 : }
703 :
704 : /*
705 : * ReadArrayStr :
706 : * parses the array string pointed to by "arrayStr" and converts the values
6352 tgl 707 ECB : * to internal format. Unspecified elements are initialized to nulls.
708 : * The array dimensions must already have been determined.
709 : *
710 : * Inputs:
711 : * arrayStr: the string to parse.
712 : * CAUTION: the contents of "arrayStr" will be modified!
713 : * origStr: the unmodified input string, used only in error messages.
714 : * nitems: total number of array elements, as already determined.
715 : * ndim: number of array dimensions
716 : * dim[]: array axis lengths
717 : * inputproc: type-specific input procedure for element datatype.
718 : * typioparam, typmod: auxiliary values to pass to inputproc.
719 : * typdelim: the value delimiter (type-specific).
720 : * typlen, typbyval, typalign: storage parameters of element datatype.
721 : *
722 : * Outputs:
723 : * values[]: filled with converted data values.
724 : * nulls[]: filled with is-null markers.
725 : * *hasnulls: set true iff there are any null elements.
726 : * *nbytes: set to total size of data area needed (including alignment
727 : * padding but not including array header overhead).
728 : * *escontext: if this points to an ErrorSaveContext, details of
729 : * any error are reported there.
730 : *
731 : * Result:
732 : * true for success, false for failure (if escontext is provided).
733 : *
734 : * Note that values[] and nulls[] are allocated by the caller, and must have
735 : * nitems elements.
736 : */
737 : static bool
8301 tgl 738 GIC 178749 : ReadArrayStr(char *arrayStr,
739 : const char *origStr,
740 : int nitems,
741 : int ndim,
742 : int *dim,
743 : FmgrInfo *inputproc,
744 : Oid typioparam,
745 : int32 typmod,
746 : char typdelim,
747 : int typlen,
748 : bool typbyval,
749 : char typalign,
750 : Datum *values,
751 : bool *nulls,
752 : bool *hasnulls,
753 : int32 *nbytes,
754 : Node *escontext)
755 : {
9344 bruce 756 ECB : int i,
9344 bruce 757 GIC 178749 : nest_level = 0;
758 : char *srcptr;
6768 tgl 759 178749 : bool in_quotes = false;
7694 760 178749 : bool eoArray = false;
761 : bool hasnull;
762 : int32 totbytes;
267 peter 763 GNC 178749 : int indx[MAXDIM] = {0},
764 : prod[MAXDIM];
765 :
9345 bruce 766 GIC 178749 : mda_get_prod(ndim, dim, prod);
767 :
768 : /* Initialize is-null markers to true */
6352 tgl 769 178749 : memset(nulls, true, nitems * sizeof(bool));
770 :
771 : /*
772 : * We have to remove " and \ characters to create a clean item value to
773 : * pass to the datatype input routine. We overwrite each item value
6385 bruce 774 ECB : * in-place within arrayStr to do this. srcptr is the current scan point,
775 : * and dstptr is where we are copying to.
6768 tgl 776 : *
6347 bruce 777 : * We also want to suppress leading and trailing unquoted whitespace. We
778 : * use the leadingspace flag to suppress leading space. Trailing space is
779 : * tracked by using dstendptr to point to the last significant output
6385 780 : * character.
781 : *
782 : * The error checking in this routine is mostly pro-forma, since we expect
3050 tgl 783 : * that ArrayCount() already validated the string. So we don't bother
784 : * with errdetail messages.
785 : */
6768 tgl 786 CBC 178749 : srcptr = arrayStr;
9345 bruce 787 GIC 1068084 : while (!eoArray)
788 : {
7694 tgl 789 889351 : bool itemdone = false;
6768 790 889351 : bool leadingspace = true;
6352 791 889351 : bool hasquoting = false;
792 : char *itemstart;
793 : char *dstptr;
794 : char *dstendptr;
795 :
6768 796 889351 : i = -1;
797 889351 : itemstart = dstptr = dstendptr = srcptr;
798 :
7694 799 6465833 : while (!itemdone)
800 : {
6768 801 5576482 : switch (*srcptr)
802 : {
7694 tgl 803 LBC 0 : case '\0':
7694 tgl 804 ECB : /* Signal a premature end of the string */
121 tgl 805 UNC 0 : ereturn(escontext, false,
7196 tgl 806 ECB : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
6768 807 : errmsg("malformed array literal: \"%s\"",
808 : origStr)));
809 : break;
9344 bruce 810 GIC 68 : case '\\':
811 : /* Skip backslash, copy next character as-is. */
6768 tgl 812 68 : srcptr++;
6768 tgl 813 CBC 68 : if (*srcptr == '\0')
121 tgl 814 UNC 0 : ereturn(escontext, false,
815 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
6768 tgl 816 ECB : errmsg("malformed array literal: \"%s\"",
817 : origStr)));
6768 tgl 818 CBC 68 : *dstptr++ = *srcptr++;
819 : /* Treat the escaped character as non-whitespace */
6768 tgl 820 GBC 68 : leadingspace = false;
6768 tgl 821 GIC 68 : dstendptr = dstptr;
6347 bruce 822 GBC 68 : hasquoting = true; /* can't be a NULL marker */
6768 tgl 823 GIC 68 : break;
2665 peter_e 824 362640 : case '"':
6768 tgl 825 362640 : in_quotes = !in_quotes;
826 362640 : if (in_quotes)
6768 tgl 827 CBC 181320 : leadingspace = false;
828 : else
7522 bruce 829 ECB : {
6768 tgl 830 : /*
6768 tgl 831 EUB : * Advance dstendptr when we exit in_quotes; this
832 : * saves having to do it in all the other in_quotes
833 : * cases.
834 : */
6768 tgl 835 CBC 181320 : dstendptr = dstptr;
836 : }
6347 bruce 837 362640 : hasquoting = true; /* can't be a NULL marker */
6768 tgl 838 362640 : srcptr++;
839 362640 : break;
9344 bruce 840 179370 : case '{':
6768 tgl 841 179370 : if (!in_quotes)
9344 bruce 842 ECB : {
7694 tgl 843 CBC 179341 : if (nest_level >= ndim)
121 tgl 844 UNC 0 : ereturn(escontext, false,
845 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
846 : errmsg("malformed array literal: \"%s\"",
847 : origStr)));
7694 tgl 848 GIC 179341 : nest_level++;
9344 bruce 849 179341 : indx[nest_level - 1] = 0;
6768 tgl 850 179341 : srcptr++;
851 : }
6768 tgl 852 ECB : else
6768 tgl 853 GIC 29 : *dstptr++ = *srcptr++;
9344 bruce 854 CBC 179370 : break;
855 179366 : case '}':
6768 tgl 856 179366 : if (!in_quotes)
9344 bruce 857 ECB : {
7694 tgl 858 CBC 179337 : if (nest_level == 0)
121 tgl 859 UNC 0 : ereturn(escontext, false,
2118 tgl 860 ECB : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
2118 tgl 861 EUB : errmsg("malformed array literal: \"%s\"",
862 : origStr)));
9344 bruce 863 GIC 179337 : if (i == -1)
8296 tgl 864 179095 : i = ArrayGetOffset0(ndim, indx, prod);
7694 tgl 865 CBC 179337 : indx[nest_level - 1] = 0;
9344 bruce 866 179337 : nest_level--;
867 179337 : if (nest_level == 0)
7694 tgl 868 GIC 178745 : eoArray = itemdone = true;
869 : else
9344 bruce 870 CBC 592 : indx[nest_level - 1]++;
6768 tgl 871 179337 : srcptr++;
9344 bruce 872 ECB : }
6768 tgl 873 : else
6768 tgl 874 GIC 29 : *dstptr++ = *srcptr++;
9344 bruce 875 CBC 179366 : break;
9344 bruce 876 GBC 4855038 : default:
6768 tgl 877 GIC 4855038 : if (in_quotes)
878 1142768 : *dstptr++ = *srcptr++;
879 3712270 : else if (*srcptr == typdelim)
9344 bruce 880 ECB : {
9344 bruce 881 CBC 710606 : if (i == -1)
8296 tgl 882 710256 : i = ArrayGetOffset0(ndim, indx, prod);
7694 883 710606 : itemdone = true;
9344 bruce 884 710606 : indx[ndim - 1]++;
6768 tgl 885 710606 : srcptr++;
886 : }
4614 887 3001664 : else if (array_isspace(*srcptr))
6768 tgl 888 ECB : {
889 : /*
890 : * If leading space, drop it immediately. Else, copy
6385 bruce 891 : * but don't advance dstendptr.
6768 tgl 892 : */
6768 tgl 893 CBC 19081 : if (leadingspace)
894 18643 : srcptr++;
6768 tgl 895 ECB : else
6768 tgl 896 CBC 438 : *dstptr++ = *srcptr++;
897 : }
6768 tgl 898 ECB : else
899 : {
6768 tgl 900 CBC 2982583 : *dstptr++ = *srcptr++;
901 2982583 : leadingspace = false;
902 2982583 : dstendptr = dstptr;
903 : }
9344 bruce 904 4855038 : break;
905 : }
906 : }
907 :
6768 tgl 908 GIC 889351 : Assert(dstptr < srcptr);
909 889351 : *dstendptr = '\0';
6768 tgl 910 ECB :
7694 tgl 911 CBC 889351 : if (i < 0 || i >= nitems)
121 tgl 912 UNC 0 : ereturn(escontext, false,
7196 tgl 913 ECB : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
914 : errmsg("malformed array literal: \"%s\"",
915 : origStr)));
916 :
6347 bruce 917 CBC 1597382 : if (Array_nulls && !hasquoting &&
6352 tgl 918 708031 : pg_strcasecmp(itemstart, "NULL") == 0)
6352 tgl 919 ECB : {
920 : /* it's a NULL item */
121 tgl 921 GNC 206 : if (!InputFunctionCallSafe(inputproc, NULL,
922 : typioparam, typmod,
923 : escontext,
924 206 : &values[i]))
121 tgl 925 UNC 0 : return false;
6352 tgl 926 GIC 206 : nulls[i] = true;
927 : }
6352 tgl 928 ECB : else
929 : {
121 tgl 930 GNC 889135 : if (!InputFunctionCallSafe(inputproc, itemstart,
931 : typioparam, typmod,
932 : escontext,
933 889145 : &values[i]))
934 6 : return false;
6352 tgl 935 GBC 889129 : nulls[i] = false;
936 : }
937 : }
938 :
939 : /*
6352 tgl 940 ECB : * Check for nulls, compute total data space needed
8301 941 : */
6352 tgl 942 GIC 178733 : hasnull = false;
943 178733 : totbytes = 0;
6352 tgl 944 CBC 1068062 : for (i = 0; i < nitems; i++)
945 : {
6352 tgl 946 GIC 889329 : if (nulls[i])
6352 tgl 947 CBC 206 : hasnull = true;
6352 tgl 948 EUB : else
9345 bruce 949 ECB : {
950 : /* let's just make sure data is not toasted */
6352 tgl 951 GIC 889123 : if (typlen == -1)
952 420527 : values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
5847 tgl 953 CBC 889123 : totbytes = att_addlength_datum(totbytes, typlen, values[i]);
5847 tgl 954 GIC 889123 : totbytes = att_align_nominal(totbytes, typalign);
955 : /* check for overflow of total request */
6352 tgl 956 CBC 889123 : if (!AllocSizeIsValid(totbytes))
121 tgl 957 UNC 0 : ereturn(escontext, false,
6352 tgl 958 ECB : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
959 : errmsg("array size exceeds the maximum allowed (%d)",
960 : (int) MaxAllocSize)));
961 : }
962 : }
6352 tgl 963 GIC 178733 : *hasnulls = hasnull;
6768 964 178733 : *nbytes = totbytes;
121 tgl 965 GNC 178733 : return true;
9770 scrappy 966 ECB : }
967 :
968 :
969 : /*
8301 tgl 970 : * Copy data into an array object from a temporary array of Datums.
971 : *
972 : * array: array object (with header fields already filled in)
973 : * values: array of Datums to be copied
974 : * nulls: array of is-null flags (can be NULL if no nulls)
975 : * nitems: number of Datums to be copied
976 : * typbyval, typlen, typalign: info about element datatype
2062 peter_e 977 : * freedata: if true and element type is pass-by-ref, pfree data values
8301 tgl 978 : * referenced by Datums after copying them.
979 : *
8291 980 : * If the input data is of varlena type, the caller must have ensured that
3260 bruce 981 EUB : * the values are not toasted. (Doing it here doesn't work since the
982 : * caller has already allocated space for the array...)
983 : */
984 : void
6352 tgl 985 GIC 1160413 : CopyArrayEls(ArrayType *array,
986 : Datum *values,
6352 tgl 987 ECB : bool *nulls,
8301 988 : int nitems,
989 : int typlen,
990 : bool typbyval,
991 : char typalign,
992 : bool freedata)
993 : {
6352 tgl 994 GIC 1160413 : char *p = ARR_DATA_PTR(array);
995 1160413 : bits8 *bitmap = ARR_NULLBITMAP(array);
996 1160413 : int bitval = 0;
997 1160413 : int bitmask = 1;
998 : int i;
999 :
8301 1000 1160413 : if (typbyval)
1001 818469 : freedata = false;
1002 :
9345 bruce 1003 10196611 : for (i = 0; i < nitems; i++)
1004 : {
6352 tgl 1005 9036198 : if (nulls && nulls[i])
1006 : {
6347 bruce 1007 16546 : if (!bitmap) /* shouldn't happen */
6352 tgl 1008 UIC 0 : elog(ERROR, "null array element where not supported");
6352 tgl 1009 ECB : /* bitmap bit stays 0 */
1010 : }
1011 : else
1012 : {
6352 tgl 1013 GIC 9019652 : bitval |= bitmask;
1014 9019652 : p += ArrayCastAndSet(values[i], typlen, typbyval, typalign, p);
1015 9019652 : if (freedata)
1016 420715 : pfree(DatumGetPointer(values[i]));
1017 : }
6352 tgl 1018 CBC 9036198 : if (bitmap)
6352 tgl 1019 ECB : {
6352 tgl 1020 CBC 392673 : bitmask <<= 1;
1021 392673 : if (bitmask == 0x100)
1022 : {
6352 tgl 1023 GIC 47568 : *bitmap++ = bitval;
6352 tgl 1024 CBC 47568 : bitval = 0;
1025 47568 : bitmask = 1;
1026 : }
6352 tgl 1027 ECB : }
1028 : }
1029 :
6352 tgl 1030 GIC 1160413 : if (bitmap && bitmask != 1)
6352 tgl 1031 CBC 8863 : *bitmap = bitval;
9770 scrappy 1032 GBC 1160413 : }
1033 :
1034 : /*
1035 : * array_out :
1036 : * takes the internal representation of an array and returns a string
9345 bruce 1037 ECB : * containing the array in its external format.
9770 scrappy 1038 : */
8335 tgl 1039 : Datum
8335 tgl 1040 CBC 266654 : array_out(PG_FUNCTION_ARGS)
1041 : {
2029 1042 266654 : AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
2887 tgl 1043 GIC 266654 : Oid element_type = AARR_ELEMTYPE(v);
9344 bruce 1044 ECB : int typlen;
1045 : bool typbyval;
1046 : char typalign;
7226 tgl 1047 : char typdelim;
8986 bruce 1048 : char *p,
1049 : *tmp,
1050 : *retval,
1051 : **values,
1052 : dims_str[(MAXDIM * 33) + 2];
1053 :
6797 1054 : /*
1055 : * 33 per dim since we assume 15 digits per number + ':' +'[]'
1056 : *
1057 : * +2 allows for assignment operator + trailing null
1058 : */
1059 : bool *needquotes,
6821 mail 1060 GIC 266654 : needdims = false;
1061 : size_t overall_length;
1062 : int nitems,
1063 : i,
9344 bruce 1064 ECB : j,
1065 : k,
1066 : indx[MAXDIM];
1067 : int ndim,
1068 : *dims,
1069 : *lb;
1070 : array_iter iter;
1071 : ArrayMetaState *my_extra;
1072 :
1073 : /*
1074 : * We arrange to look up info about element type, including its output
1075 : * conversion proc, only once per series of calls, assuming the element
1076 : * type doesn't change underneath us.
1077 : */
7226 tgl 1078 GIC 266654 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1079 266654 : if (my_extra == NULL)
1080 : {
1081 10460 : fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1082 : sizeof(ArrayMetaState));
1083 10460 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
6352 tgl 1084 CBC 10460 : my_extra->element_type = ~element_type;
1085 : }
1086 :
7226 tgl 1087 GIC 266654 : if (my_extra->element_type != element_type)
1088 : {
1089 : /*
1090 : * Get info about element type, including its output conversion proc
1091 : */
1092 10460 : get_type_io_data(element_type, IOFunc_output,
1093 : &my_extra->typlen, &my_extra->typbyval,
1094 : &my_extra->typalign, &my_extra->typdelim,
1095 : &my_extra->typioparam, &my_extra->typiofunc);
1096 10460 : fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1097 10460 : fcinfo->flinfo->fn_mcxt);
1098 10460 : my_extra->element_type = element_type;
1099 : }
1100 266654 : typlen = my_extra->typlen;
1101 266654 : typbyval = my_extra->typbyval;
7226 tgl 1102 CBC 266654 : typalign = my_extra->typalign;
1103 266654 : typdelim = my_extra->typdelim;
1104 :
2887 1105 266654 : ndim = AARR_NDIM(v);
2887 tgl 1106 GIC 266654 : dims = AARR_DIMS(v);
2887 tgl 1107 CBC 266654 : lb = AARR_LBOUND(v);
6779 neilc 1108 266654 : nitems = ArrayGetNItems(ndim, dims);
1109 :
9345 bruce 1110 GIC 266654 : if (nitems == 0)
9345 bruce 1111 ECB : {
7801 tgl 1112 GIC 1261 : retval = pstrdup("{}");
8335 1113 1261 : PG_RETURN_CSTRING(retval);
1114 : }
1115 :
6821 mail 1116 ECB : /*
1117 : * we will need to add explicit dimensions if any dimension has a lower
1118 : * bound other than one
1119 : */
6821 mail 1120 CBC 531089 : for (i = 0; i < ndim; i++)
6821 mail 1121 ECB : {
6821 mail 1122 CBC 265835 : if (lb[i] != 1)
1123 : {
1124 139 : needdims = true;
1125 139 : break;
6821 mail 1126 ECB : }
1127 : }
1128 :
7801 tgl 1129 : /*
6385 bruce 1130 : * Convert all values to string form, count total space needed (including
1131 : * any overhead such as escaping backslashes), and detect whether each
1132 : * item needs double quotes.
1133 : */
7801 tgl 1134 CBC 265393 : values = (char **) palloc(nitems * sizeof(char *));
7801 tgl 1135 GIC 265393 : needquotes = (bool *) palloc(nitems * sizeof(bool));
1658 tgl 1136 CBC 265393 : overall_length = 0;
6779 neilc 1137 ECB :
2887 tgl 1138 GIC 265393 : array_iter_setup(&iter, v);
1139 :
9345 bruce 1140 943063 : for (i = 0; i < nitems; i++)
1141 : {
1142 : Datum itemvalue;
1143 : bool isnull;
6779 neilc 1144 ECB : bool needquote;
1145 :
6352 tgl 1146 : /* Get source element, checking for NULL */
2887 tgl 1147 GIC 677670 : itemvalue = array_iter_next(&iter, &isnull, i,
2887 tgl 1148 ECB : typlen, typbyval, typalign);
1149 :
2887 tgl 1150 GIC 677670 : if (isnull)
1151 : {
6352 1152 1110 : values[i] = pstrdup("NULL");
1153 1110 : overall_length += 4;
6779 neilc 1154 1110 : needquote = false;
1155 : }
1156 : else
1157 : {
6214 tgl 1158 CBC 676560 : values[i] = OutputFunctionCall(&my_extra->proc, itemvalue);
6352 tgl 1159 ECB :
1160 : /* count data plus backslashes; detect chars needing quotes */
6352 tgl 1161 GIC 676560 : if (values[i][0] == '\0')
2118 tgl 1162 CBC 207 : needquote = true; /* force quotes for empty string */
6352 tgl 1163 GIC 676353 : else if (pg_strcasecmp(values[i], "NULL") == 0)
2118 tgl 1164 CBC 4 : needquote = true; /* force quotes for literal NULL */
1165 : else
6352 tgl 1166 GIC 676349 : needquote = false;
1167 :
1168 11817713 : for (tmp = values[i]; *tmp != '\0'; tmp++)
1169 : {
1170 11141153 : char ch = *tmp;
6352 tgl 1171 ECB :
8986 bruce 1172 GIC 11141153 : overall_length += 1;
6352 tgl 1173 11141153 : if (ch == '"' || ch == '\\')
6352 tgl 1174 ECB : {
6352 tgl 1175 GIC 1295 : needquote = true;
6352 tgl 1176 CBC 1295 : overall_length += 1;
6352 tgl 1177 ECB : }
6352 tgl 1178 CBC 22275968 : else if (ch == '{' || ch == '}' || ch == typdelim ||
4614 tgl 1179 GIC 11136110 : array_isspace(ch))
6352 1180 5652 : needquote = true;
1181 : }
9151 bruce 1182 ECB : }
1183 :
6779 neilc 1184 GIC 677670 : needquotes[i] = needquote;
7801 tgl 1185 ECB :
1186 : /* Count the pair of double quotes, if needed */
6779 neilc 1187 CBC 677670 : if (needquote)
7801 tgl 1188 3689 : overall_length += 2;
1189 : /* and the comma (or other typdelim delimiter) */
9151 bruce 1190 677670 : overall_length += 1;
1191 : }
9345 bruce 1192 ECB :
1193 : /*
1658 tgl 1194 : * The very last array element doesn't have a typdelim delimiter after it,
1195 : * but that's OK; that space is needed for the trailing '\0'.
1196 : *
1197 : * Now count total number of curly brace pairs in output string.
1198 : */
6779 neilc 1199 CBC 531255 : for (i = j = 0, k = 1; i < ndim; i++)
1658 tgl 1200 ECB : {
1658 tgl 1201 GIC 265862 : j += k, k *= dims[i];
1658 tgl 1202 ECB : }
1658 tgl 1203 CBC 265393 : overall_length += 2 * j;
6779 neilc 1204 ECB :
1205 : /* Format explicit dimensions if required */
6779 neilc 1206 GIC 265393 : dims_str[0] = '\0';
6821 mail 1207 265393 : if (needdims)
6821 mail 1208 ECB : {
6797 bruce 1209 GIC 139 : char *ptr = dims_str;
1210 :
6821 mail 1211 CBC 306 : for (i = 0; i < ndim; i++)
6821 mail 1212 ECB : {
6779 neilc 1213 GIC 167 : sprintf(ptr, "[%d:%d]", lb[i], lb[i] + dims[i] - 1);
6821 mail 1214 CBC 167 : ptr += strlen(ptr);
1215 : }
6821 mail 1216 GIC 139 : *ptr++ = *ASSGN;
1217 139 : *ptr = '\0';
1658 tgl 1218 139 : overall_length += ptr - dims_str;
1219 : }
1220 :
1221 : /* Now construct the output string */
1222 265393 : retval = (char *) palloc(overall_length);
7801 tgl 1223 CBC 265393 : p = retval;
1224 :
7801 tgl 1225 ECB : #define APPENDSTR(str) (strcpy(p, (str)), p += strlen(p))
1226 : #define APPENDCHAR(ch) (*p++ = (ch), *p = '\0')
9345 bruce 1227 :
6821 mail 1228 GIC 265393 : if (needdims)
1229 139 : APPENDSTR(dims_str);
7801 tgl 1230 CBC 265393 : APPENDCHAR('{');
6779 neilc 1231 531255 : for (i = 0; i < ndim; i++)
6779 neilc 1232 GIC 265862 : indx[i] = 0;
9345 bruce 1233 CBC 265393 : j = 0;
9345 bruce 1234 GIC 265393 : k = 0;
9345 bruce 1235 ECB : do
1236 : {
9345 bruce 1237 CBC 678836 : for (i = j; i < ndim - 1; i++)
7801 tgl 1238 1166 : APPENDCHAR('{');
1239 :
1240 677670 : if (needquotes[k])
9345 bruce 1241 ECB : {
7801 tgl 1242 CBC 3689 : APPENDCHAR('"');
8986 bruce 1243 GIC 35863 : for (tmp = values[k]; *tmp; tmp++)
1244 : {
7522 1245 32174 : char ch = *tmp;
7801 tgl 1246 ECB :
7801 tgl 1247 CBC 32174 : if (ch == '"' || ch == '\\')
7801 tgl 1248 GIC 1295 : *p++ = '\\';
1249 32174 : *p++ = ch;
1250 : }
1251 3689 : *p = '\0';
7801 tgl 1252 CBC 3689 : APPENDCHAR('"');
9345 bruce 1253 ECB : }
1254 : else
7801 tgl 1255 CBC 673981 : APPENDSTR(values[k]);
9345 bruce 1256 677670 : pfree(values[k++]);
9345 bruce 1257 ECB :
9345 bruce 1258 CBC 944229 : for (i = ndim - 1; i >= 0; i--)
1259 : {
1658 tgl 1260 GIC 678836 : if (++(indx[i]) < dims[i])
9345 bruce 1261 ECB : {
7801 tgl 1262 CBC 412277 : APPENDCHAR(typdelim);
9345 bruce 1263 GIC 412277 : break;
9345 bruce 1264 ECB : }
1265 : else
1658 tgl 1266 : {
1658 tgl 1267 CBC 266559 : indx[i] = 0;
7801 tgl 1268 GIC 266559 : APPENDCHAR('}');
1658 tgl 1269 ECB : }
1270 : }
9345 bruce 1271 CBC 677670 : j = i;
1272 677670 : } while (j != -1);
9345 bruce 1273 ECB :
1274 : #undef APPENDSTR
7801 tgl 1275 : #undef APPENDCHAR
1276 :
1277 : /* Assert that we calculated the string length accurately */
1658 tgl 1278 GIC 265393 : Assert(overall_length == (p - retval + 1));
1658 tgl 1279 ECB :
9345 bruce 1280 CBC 265393 : pfree(values);
7801 tgl 1281 GIC 265393 : pfree(needquotes);
7801 tgl 1282 ECB :
8335 tgl 1283 GIC 265393 : PG_RETURN_CSTRING(retval);
9770 scrappy 1284 ECB : }
1285 :
6352 tgl 1286 : /*
7276 1287 : * array_recv :
1288 : * converts an array from the external binary format to
1289 : * its internal format.
1290 : *
1291 : * return value :
1292 : * the internal representation of the input array
1293 : */
1294 : Datum
7276 tgl 1295 CBC 31 : array_recv(PG_FUNCTION_ARGS)
7276 tgl 1296 ECB : {
7275 tgl 1297 GIC 31 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
1298 31 : Oid spec_element_type = PG_GETARG_OID(1); /* type of an array
1299 : * element */
6385 bruce 1300 31 : int32 typmod = PG_GETARG_INT32(2); /* typmod for array elements */
1301 : Oid element_type;
7275 tgl 1302 ECB : int typlen;
1303 : bool typbyval;
7226 1304 : char typalign;
6881 1305 : Oid typioparam;
1306 : int i,
7275 1307 : nitems;
1308 : Datum *dataPtr;
1309 : bool *nullsPtr;
1310 : bool hasnulls;
1311 : int32 nbytes;
1312 : int32 dataoffset;
1313 : ArrayType *retval;
1314 : int ndim,
1315 : flags,
1316 : dim[MAXDIM],
1317 : lBound[MAXDIM];
1318 : ArrayMetaState *my_extra;
1319 :
1320 : /* Get the array header information */
7275 tgl 1321 CBC 31 : ndim = pq_getmsgint(buf, 4);
7196 1322 31 : if (ndim < 0) /* we do allow zero-dimension arrays */
7196 tgl 1323 UIC 0 : ereport(ERROR,
7196 tgl 1324 ECB : (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1325 : errmsg("invalid number of dimensions: %d", ndim)));
7196 tgl 1326 GIC 31 : if (ndim > MAXDIM)
7196 tgl 1327 UIC 0 : ereport(ERROR,
1328 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1329 : errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
1330 : ndim, MAXDIM)));
1331 :
7275 tgl 1332 GIC 31 : flags = pq_getmsgint(buf, 4);
6352 1333 31 : if (flags != 0 && flags != 1)
7196 tgl 1334 UIC 0 : ereport(ERROR,
1335 : (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1336 : errmsg("invalid array flags")));
1337 :
1338 : /* Check element type recorded in the data */
7275 tgl 1339 GIC 31 : element_type = pq_getmsgint(buf, sizeof(Oid));
1340 :
1341 : /*
1342 : * From a security standpoint, it doesn't matter whether the input's
1343 : * element type matches what we expect: the element type's receive
1344 : * function has to be robust enough to cope with invalid data. However,
992 tgl 1345 ECB : * from a user-friendliness standpoint, it's nicer to complain about type
1346 : * mismatches than to throw "improper binary format" errors. But there's
992 tgl 1347 EUB : * a problem: only built-in types have OIDs that are stable enough to
1348 : * believe that a mismatch is a real issue. So complain only if both OIDs
1349 : * are in the built-in range. Otherwise, carry on with the element type
992 tgl 1350 ECB : * we "should" be getting.
992 tgl 1351 EUB : */
7275 tgl 1352 GIC 31 : if (element_type != spec_element_type)
1353 : {
992 tgl 1354 UIC 0 : if (element_type < FirstGenbkiObjectId &&
1355 : spec_element_type < FirstGenbkiObjectId)
992 tgl 1356 LBC 0 : ereport(ERROR,
992 tgl 1357 ECB : (errcode(ERRCODE_DATATYPE_MISMATCH),
992 tgl 1358 EUB : errmsg("binary data has array element type %u (%s) instead of expected %u (%s)",
1359 : element_type,
1360 : format_type_extended(element_type, -1,
1361 : FORMAT_TYPE_ALLOW_INVALID),
1362 : spec_element_type,
992 tgl 1363 ECB : format_type_extended(spec_element_type, -1,
1364 : FORMAT_TYPE_ALLOW_INVALID))));
992 tgl 1365 UIC 0 : element_type = spec_element_type;
1366 : }
1367 :
7275 tgl 1368 GIC 62 : for (i = 0; i < ndim; i++)
1369 : {
1370 31 : dim[i] = pq_getmsgint(buf, 4);
1371 31 : lBound[i] = pq_getmsgint(buf, 4);
1372 : }
1373 :
1374 : /* This checks for overflow of array dimensions */
1375 31 : nitems = ArrayGetNItems(ndim, dim);
699 tgl 1376 CBC 31 : ArrayCheckBounds(ndim, dim, lBound);
1377 :
7226 tgl 1378 EUB : /*
1379 : * We arrange to look up info about element type, including its receive
6385 bruce 1380 : * conversion proc, only once per series of calls, assuming the element
1381 : * type doesn't change underneath us.
1382 : */
7226 tgl 1383 GIC 31 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1384 31 : if (my_extra == NULL)
1385 : {
1386 28 : fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1387 : sizeof(ArrayMetaState));
1388 28 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
6446 tgl 1389 GBC 28 : my_extra->element_type = ~element_type;
1390 : }
1391 :
7226 tgl 1392 CBC 31 : if (my_extra->element_type != element_type)
1393 : {
7226 tgl 1394 ECB : /* Get info about element type, including its receive proc */
7226 tgl 1395 CBC 28 : get_type_io_data(element_type, IOFunc_receive,
1396 : &my_extra->typlen, &my_extra->typbyval,
1397 : &my_extra->typalign, &my_extra->typdelim,
1398 : &my_extra->typioparam, &my_extra->typiofunc);
1399 28 : if (!OidIsValid(my_extra->typiofunc))
7196 tgl 1400 LBC 0 : ereport(ERROR,
1401 : (errcode(ERRCODE_UNDEFINED_FUNCTION),
1402 : errmsg("no binary input function available for type %s",
1403 : format_type_be(element_type))));
7226 tgl 1404 GIC 28 : fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1405 28 : fcinfo->flinfo->fn_mcxt);
1406 28 : my_extra->element_type = element_type;
7226 tgl 1407 ECB : }
6446 1408 :
6446 tgl 1409 GIC 31 : if (nitems == 0)
6446 tgl 1410 ECB : {
1411 : /* Return empty array ... but not till we've validated element_type */
6352 tgl 1412 LBC 0 : PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
6446 tgl 1413 ECB : }
1414 :
7226 tgl 1415 GIC 31 : typlen = my_extra->typlen;
7226 tgl 1416 CBC 31 : typbyval = my_extra->typbyval;
7226 tgl 1417 GIC 31 : typalign = my_extra->typalign;
6881 1418 31 : typioparam = my_extra->typioparam;
7275 tgl 1419 ECB :
6352 tgl 1420 GIC 31 : dataPtr = (Datum *) palloc(nitems * sizeof(Datum));
1421 31 : nullsPtr = (bool *) palloc(nitems * sizeof(bool));
1422 31 : ReadArrayBinary(buf, nitems,
6352 tgl 1423 ECB : &my_extra->proc, typioparam, typmod,
6352 tgl 1424 EUB : typlen, typbyval, typalign,
1425 : dataPtr, nullsPtr,
1426 : &hasnulls, &nbytes);
6352 tgl 1427 GIC 31 : if (hasnulls)
6352 tgl 1428 ECB : {
6352 tgl 1429 LBC 0 : dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
1430 0 : nbytes += dataoffset;
1431 : }
1432 : else
6352 tgl 1433 ECB : {
6352 tgl 1434 GIC 31 : dataoffset = 0; /* marker for no null bitmap */
1435 31 : nbytes += ARR_OVERHEAD_NONULLS(ndim);
6352 tgl 1436 EUB : }
4365 tgl 1437 GIC 31 : retval = (ArrayType *) palloc0(nbytes);
5885 1438 31 : SET_VARSIZE(retval, nbytes);
7275 tgl 1439 CBC 31 : retval->ndim = ndim;
6352 1440 31 : retval->dataoffset = dataoffset;
7275 1441 31 : retval->elemtype = element_type;
7032 neilc 1442 31 : memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
7032 neilc 1443 GIC 31 : memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
7275 tgl 1444 ECB :
6352 tgl 1445 CBC 31 : CopyArrayEls(retval,
6352 tgl 1446 ECB : dataPtr, nullsPtr, nitems,
1447 : typlen, typbyval, typalign,
1448 : true);
1449 :
7275 tgl 1450 GIC 31 : pfree(dataPtr);
6352 tgl 1451 CBC 31 : pfree(nullsPtr);
1452 :
7275 tgl 1453 GBC 31 : PG_RETURN_ARRAYTYPE_P(retval);
7276 tgl 1454 EUB : }
1455 :
1456 : /*
1457 : * ReadArrayBinary:
7275 tgl 1458 ECB : * collect the data elements of an array being read in binary style.
6352 1459 : *
1460 : * Inputs:
1461 : * buf: the data buffer to read from.
1462 : * nitems: total number of array elements (already read).
1463 : * receiveproc: type-specific receive procedure for element datatype.
1464 : * typioparam, typmod: auxiliary values to pass to receiveproc.
1465 : * typlen, typbyval, typalign: storage parameters of element datatype.
1466 : *
1467 : * Outputs:
1468 : * values[]: filled with converted data values.
1469 : * nulls[]: filled with is-null markers.
1470 : * *hasnulls: set true iff there are any null elements.
1471 : * *nbytes: set to total size of data area needed (including alignment
1472 : * padding but not including array header overhead).
1473 : *
1474 : * Note that values[] and nulls[] are allocated by the caller, and must have
1475 : * nitems elements.
1476 : */
1477 : static void
7275 tgl 1478 GIC 31 : ReadArrayBinary(StringInfo buf,
1479 : int nitems,
1480 : FmgrInfo *receiveproc,
1481 : Oid typioparam,
1482 : int32 typmod,
1483 : int typlen,
1484 : bool typbyval,
1485 : char typalign,
1486 : Datum *values,
1487 : bool *nulls,
1488 : bool *hasnulls,
1489 : int32 *nbytes)
1490 : {
1491 : int i;
1492 : bool hasnull;
1493 : int32 totbytes;
1494 :
1495 124 : for (i = 0; i < nitems; i++)
1496 : {
1497 : int itemlen;
1498 : StringInfoData elem_buf;
1499 : char csave;
1500 :
1501 : /* Get and check the item length */
7275 tgl 1502 CBC 93 : itemlen = pq_getmsgint(buf, 4);
6352 tgl 1503 GIC 93 : if (itemlen < -1 || itemlen > (buf->len - buf->cursor))
7196 tgl 1504 UIC 0 : ereport(ERROR,
1505 : (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1506 : errmsg("insufficient data left in message")));
1507 :
6352 tgl 1508 GIC 93 : if (itemlen == -1)
1509 : {
1510 : /* -1 length means NULL */
6214 tgl 1511 UIC 0 : values[i] = ReceiveFunctionCall(receiveproc, NULL,
1512 : typioparam, typmod);
6352 1513 0 : nulls[i] = true;
1514 0 : continue;
1515 : }
1516 :
1517 : /*
1518 : * Rather than copying data around, we just set up a phony StringInfo
6385 bruce 1519 ECB : * pointing to the correct portion of the input buffer. We assume we
1520 : * can scribble on the input buffer so as to maintain the convention
1521 : * that StringInfos have a trailing null.
1522 : */
7275 tgl 1523 GIC 93 : elem_buf.data = &buf->data[buf->cursor];
1524 93 : elem_buf.maxlen = itemlen + 1;
1525 93 : elem_buf.len = itemlen;
7275 tgl 1526 CBC 93 : elem_buf.cursor = 0;
7275 tgl 1527 ECB :
7275 tgl 1528 GBC 93 : buf->cursor += itemlen;
1529 :
7275 tgl 1530 GIC 93 : csave = buf->data[buf->cursor];
1531 93 : buf->data[buf->cursor] = '\0';
7275 tgl 1532 ECB :
1533 : /* Now call the element's receiveproc */
6214 tgl 1534 GIC 93 : values[i] = ReceiveFunctionCall(receiveproc, &elem_buf,
6214 tgl 1535 EUB : typioparam, typmod);
6352 tgl 1536 GIC 93 : nulls[i] = false;
7275 tgl 1537 EUB :
1538 : /* Trouble if it didn't eat the whole buffer */
7275 tgl 1539 GIC 93 : if (elem_buf.cursor != itemlen)
7196 tgl 1540 UIC 0 : ereport(ERROR,
1541 : (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1542 : errmsg("improper binary format in array element %d",
1543 : i + 1)));
1544 :
7275 tgl 1545 GIC 93 : buf->data[buf->cursor] = csave;
1546 : }
7275 tgl 1547 ECB :
1548 : /*
6352 1549 : * Check for nulls, compute total data space needed
7275 1550 : */
6352 tgl 1551 GIC 31 : hasnull = false;
6352 tgl 1552 CBC 31 : totbytes = 0;
6352 tgl 1553 GIC 124 : for (i = 0; i < nitems; i++)
7275 tgl 1554 ECB : {
6352 tgl 1555 CBC 93 : if (nulls[i])
6352 tgl 1556 UIC 0 : hasnull = true;
1557 : else
7275 tgl 1558 ECB : {
1559 : /* let's just make sure data is not toasted */
7275 tgl 1560 CBC 93 : if (typlen == -1)
7275 tgl 1561 GIC 54 : values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
5847 1562 93 : totbytes = att_addlength_datum(totbytes, typlen, values[i]);
5847 tgl 1563 CBC 93 : totbytes = att_align_nominal(totbytes, typalign);
6352 tgl 1564 EUB : /* check for overflow of total request */
6352 tgl 1565 GIC 93 : if (!AllocSizeIsValid(totbytes))
6352 tgl 1566 UIC 0 : ereport(ERROR,
1567 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1568 : errmsg("array size exceeds the maximum allowed (%d)",
6352 tgl 1569 ECB : (int) MaxAllocSize)));
1570 : }
1571 : }
6352 tgl 1572 GIC 31 : *hasnulls = hasnull;
1573 31 : *nbytes = totbytes;
7275 1574 31 : }
7275 tgl 1575 ECB :
1576 :
6352 1577 : /*
1578 : * array_send :
1579 : * takes the internal representation of an array and returns a bytea
7276 tgl 1580 EUB : * containing the array in its external binary format.
1581 : */
1582 : Datum
7276 tgl 1583 GIC 23 : array_send(PG_FUNCTION_ARGS)
7276 tgl 1584 ECB : {
2029 tgl 1585 CBC 23 : AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
2887 1586 23 : Oid element_type = AARR_ELEMTYPE(v);
7275 tgl 1587 ECB : int typlen;
1588 : bool typbyval;
1589 : char typalign;
7275 tgl 1590 EUB : int nitems,
1591 : i;
1592 : int ndim,
1593 : *dim,
1594 : *lb;
1595 : StringInfoData buf;
2887 tgl 1596 ECB : array_iter iter;
7226 1597 : ArrayMetaState *my_extra;
7275 1598 :
1599 : /*
1600 : * We arrange to look up info about element type, including its send
1601 : * conversion proc, only once per series of calls, assuming the element
1602 : * type doesn't change underneath us.
1603 : */
7226 tgl 1604 GIC 23 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1605 23 : if (my_extra == NULL)
1606 : {
7226 tgl 1607 CBC 20 : fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1608 : sizeof(ArrayMetaState));
1609 20 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
6352 1610 20 : my_extra->element_type = ~element_type;
1611 : }
1612 :
7226 tgl 1613 GIC 23 : if (my_extra->element_type != element_type)
1614 : {
1615 : /* Get info about element type, including its send proc */
1616 20 : get_type_io_data(element_type, IOFunc_send,
1617 : &my_extra->typlen, &my_extra->typbyval,
1618 : &my_extra->typalign, &my_extra->typdelim,
1619 : &my_extra->typioparam, &my_extra->typiofunc);
1620 20 : if (!OidIsValid(my_extra->typiofunc))
7196 tgl 1621 UIC 0 : ereport(ERROR,
1622 : (errcode(ERRCODE_UNDEFINED_FUNCTION),
1623 : errmsg("no binary output function available for type %s",
1624 : format_type_be(element_type))));
7226 tgl 1625 GIC 20 : fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1626 20 : fcinfo->flinfo->fn_mcxt);
1627 20 : my_extra->element_type = element_type;
7226 tgl 1628 ECB : }
7226 tgl 1629 CBC 23 : typlen = my_extra->typlen;
7226 tgl 1630 GIC 23 : typbyval = my_extra->typbyval;
7226 tgl 1631 CBC 23 : typalign = my_extra->typalign;
1632 :
2887 1633 23 : ndim = AARR_NDIM(v);
1634 23 : dim = AARR_DIMS(v);
2887 tgl 1635 GIC 23 : lb = AARR_LBOUND(v);
7275 1636 23 : nitems = ArrayGetNItems(ndim, dim);
7275 tgl 1637 ECB :
7275 tgl 1638 GIC 23 : pq_begintypsend(&buf);
1639 :
7275 tgl 1640 ECB : /* Send the array header information */
2006 andres 1641 GIC 23 : pq_sendint32(&buf, ndim);
1642 23 : pq_sendint32(&buf, AARR_HASNULL(v) ? 1 : 0);
1643 23 : pq_sendint32(&buf, element_type);
7275 tgl 1644 CBC 46 : for (i = 0; i < ndim; i++)
7275 tgl 1645 EUB : {
2006 andres 1646 GIC 23 : pq_sendint32(&buf, dim[i]);
1647 23 : pq_sendint32(&buf, lb[i]);
1648 : }
7275 tgl 1649 ECB :
1650 : /* Send the array elements using the element's own sendproc */
2887 tgl 1651 CBC 23 : array_iter_setup(&iter, v);
1652 :
7275 1653 92 : for (i = 0; i < nitems; i++)
7275 tgl 1654 ECB : {
2887 1655 : Datum itemvalue;
1656 : bool isnull;
1657 :
6352 1658 : /* Get source element, checking for NULL */
2887 tgl 1659 CBC 69 : itemvalue = array_iter_next(&iter, &isnull, i,
2887 tgl 1660 ECB : typlen, typbyval, typalign);
1661 :
2887 tgl 1662 CBC 69 : if (isnull)
1663 : {
1664 : /* -1 length means a NULL */
2006 andres 1665 LBC 0 : pq_sendint32(&buf, -1);
6352 tgl 1666 ECB : }
1667 : else
1668 : {
1669 : bytea *outputbytes;
7275 1670 :
6214 tgl 1671 CBC 69 : outputbytes = SendFunctionCall(&my_extra->proc, itemvalue);
2006 andres 1672 GIC 69 : pq_sendint32(&buf, VARSIZE(outputbytes) - VARHDRSZ);
6352 tgl 1673 69 : pq_sendbytes(&buf, VARDATA(outputbytes),
1674 69 : VARSIZE(outputbytes) - VARHDRSZ);
6352 tgl 1675 CBC 69 : pfree(outputbytes);
1676 : }
7275 tgl 1677 ECB : }
1678 :
7275 tgl 1679 GIC 23 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
1680 : }
1681 :
1682 : /*
5269 peter_e 1683 ECB : * array_ndims :
1684 : * returns the number of dimensions of the array pointed to by "v"
1685 : */
1686 : Datum
5269 peter_e 1687 GIC 964 : array_ndims(PG_FUNCTION_ARGS)
1688 : {
2029 tgl 1689 GBC 964 : AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
1690 :
1691 : /* Sanity check: does it look like an array at all? */
2887 tgl 1692 GIC 964 : if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
5269 peter_e 1693 6 : PG_RETURN_NULL();
1694 :
2887 tgl 1695 CBC 958 : PG_RETURN_INT32(AARR_NDIM(v));
5269 peter_e 1696 ECB : }
1697 :
6352 tgl 1698 : /*
9770 scrappy 1699 : * array_dims :
1700 : * returns the dimensions of the array pointed to by "v", as a "text"
1701 : */
1702 : Datum
8335 tgl 1703 CBC 3919 : array_dims(PG_FUNCTION_ARGS)
1704 : {
2029 tgl 1705 GIC 3919 : AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
1706 : char *p;
1707 : int i;
1708 : int *dimv,
1709 : *lb;
1710 :
9345 bruce 1711 ECB : /*
1712 : * 33 since we assume 15 digits per number + ':' +'[]'
8335 tgl 1713 : *
1714 : * +1 for trailing null
1715 : */
5493 1716 : char buf[MAXDIM * 33 + 1];
8335 1717 :
1718 : /* Sanity check: does it look like an array at all? */
2887 tgl 1719 CBC 3919 : if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
5493 tgl 1720 GIC 34 : PG_RETURN_NULL();
1721 :
2887 1722 3885 : dimv = AARR_DIMS(v);
1723 3885 : lb = AARR_LBOUND(v);
1724 :
5493 1725 3885 : p = buf;
2887 1726 7824 : for (i = 0; i < AARR_NDIM(v); i++)
9345 bruce 1727 ECB : {
9345 bruce 1728 GIC 3939 : sprintf(p, "[%d:%d]", lb[i], dimv[i] + lb[i] - 1);
9345 bruce 1729 CBC 3939 : p += strlen(p);
1730 : }
1731 :
5493 tgl 1732 GIC 3885 : PG_RETURN_TEXT_P(cstring_to_text(buf));
1733 : }
1734 :
1735 : /*
1736 : * array_lower :
1737 : * returns the lower dimension, of the DIM requested, for
1738 : * the array pointed to by "v", as an int4
1739 : */
1740 : Datum
7457 bruce 1741 12696 : array_lower(PG_FUNCTION_ARGS)
1742 : {
2029 tgl 1743 CBC 12696 : AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
7457 bruce 1744 12696 : int reqdim = PG_GETARG_INT32(1);
1745 : int *lb;
7457 bruce 1746 ECB : int result;
1747 :
1748 : /* Sanity check: does it look like an array at all? */
2887 tgl 1749 CBC 12696 : if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
7457 bruce 1750 LBC 0 : PG_RETURN_NULL();
1751 :
7457 bruce 1752 ECB : /* Sanity check: was the requested dim valid */
2887 tgl 1753 CBC 12696 : if (reqdim <= 0 || reqdim > AARR_NDIM(v))
7457 bruce 1754 UIC 0 : PG_RETURN_NULL();
1755 :
2887 tgl 1756 CBC 12696 : lb = AARR_LBOUND(v);
7457 bruce 1757 GIC 12696 : result = lb[reqdim - 1];
1758 :
1759 12696 : PG_RETURN_INT32(result);
1760 : }
1761 :
1762 : /*
1763 : * array_upper :
1764 : * returns the upper dimension, of the DIM requested, for
7457 bruce 1765 ECB : * the array pointed to by "v", as an int4
1766 : */
1767 : Datum
7457 bruce 1768 CBC 12918 : array_upper(PG_FUNCTION_ARGS)
1769 : {
2029 tgl 1770 GIC 12918 : AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
7457 bruce 1771 12918 : int reqdim = PG_GETARG_INT32(1);
1772 : int *dimv,
7457 bruce 1773 ECB : *lb;
7457 bruce 1774 EUB : int result;
1775 :
1776 : /* Sanity check: does it look like an array at all? */
2887 tgl 1777 CBC 12918 : if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
7457 bruce 1778 GBC 15 : PG_RETURN_NULL();
1779 :
7457 bruce 1780 ECB : /* Sanity check: was the requested dim valid */
2887 tgl 1781 CBC 12903 : if (reqdim <= 0 || reqdim > AARR_NDIM(v))
7457 bruce 1782 UIC 0 : PG_RETURN_NULL();
7457 bruce 1783 ECB :
2887 tgl 1784 GIC 12903 : lb = AARR_LBOUND(v);
1785 12903 : dimv = AARR_DIMS(v);
1786 :
7457 bruce 1787 12903 : result = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
1788 :
1789 12903 : PG_RETURN_INT32(result);
1790 : }
1791 :
5261 peter_e 1792 ECB : /*
1793 : * array_length :
1794 : * returns the length, of the dimension requested, for
1795 : * the array pointed to by "v", as an int4
1796 : */
1797 : Datum
5261 peter_e 1798 GIC 54207 : array_length(PG_FUNCTION_ARGS)
1799 : {
2029 tgl 1800 54207 : AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
5261 peter_e 1801 CBC 54207 : int reqdim = PG_GETARG_INT32(1);
5261 peter_e 1802 ECB : int *dimv;
1803 : int result;
1804 :
1805 : /* Sanity check: does it look like an array at all? */
2887 tgl 1806 GBC 54207 : if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
5261 peter_e 1807 UIC 0 : PG_RETURN_NULL();
5261 peter_e 1808 ECB :
1809 : /* Sanity check: was the requested dim valid */
2887 tgl 1810 GIC 54207 : if (reqdim <= 0 || reqdim > AARR_NDIM(v))
5261 peter_e 1811 CBC 6 : PG_RETURN_NULL();
1812 :
2887 tgl 1813 54201 : dimv = AARR_DIMS(v);
1814 :
5261 peter_e 1815 GIC 54201 : result = dimv[reqdim - 1];
1816 :
1817 54201 : PG_RETURN_INT32(result);
1818 : }
1819 :
1820 : /*
1821 : * array_cardinality:
3365 rhaas 1822 ECB : * returns the total number of elements in an array
1823 : */
1824 : Datum
3365 rhaas 1825 CBC 1266 : array_cardinality(PG_FUNCTION_ARGS)
1826 : {
2029 tgl 1827 GIC 1266 : AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
1828 :
2887 1829 1266 : PG_RETURN_INT32(ArrayGetNItems(AARR_NDIM(v), AARR_DIMS(v)));
3365 rhaas 1830 ECB : }
3365 rhaas 1831 EUB :
1832 :
1833 : /*
2974 tgl 1834 ECB : * array_get_element :
1835 : * This routine takes an array datum and a subscript array and returns
1836 : * the referenced item as a Datum. Note that for a pass-by-reference
8301 1837 : * datatype, the returned Datum is a pointer into the array object.
1838 : *
6352 1839 : * This handles both ordinary varlena arrays and fixed-length arrays.
1840 : *
1841 : * Inputs:
1842 : * arraydatum: the array object (mustn't be NULL)
1843 : * nSubscripts: number of subscripts supplied
1844 : * indx[]: the subscript values
1845 : * arraytyplen: pg_type.typlen for the array type
1846 : * elmlen: pg_type.typlen for the array's element type
1847 : * elmbyval: pg_type.typbyval for the array's element type
1848 : * elmalign: pg_type.typalign for the array's element type
1849 : *
1850 : * Outputs:
1851 : * The return value is the element Datum.
1852 : * *isNull is set to indicate whether the element is NULL.
9770 scrappy 1853 : */
1854 : Datum
2974 tgl 1855 GIC 371942 : array_get_element(Datum arraydatum,
1856 : int nSubscripts,
1857 : int *indx,
1858 : int arraytyplen,
1859 : int elmlen,
1860 : bool elmbyval,
1861 : char elmalign,
1862 : bool *isNull)
1863 : {
1864 : int i,
1865 : ndim,
1866 : *dim,
1867 : *lb,
1868 : offset,
1869 : fixedDim[1],
1870 : fixedLb[1];
1871 : char *arraydataptr,
1872 : *retptr;
1873 : bits8 *arraynullsptr;
1874 :
6352 1875 371942 : if (arraytyplen > 0)
1876 : {
1877 : /*
1878 : * fixed-length arrays -- these are assumed to be 1-d, 0-based
9345 bruce 1879 ECB : */
8295 tgl 1880 GIC 105269 : ndim = 1;
6352 1881 105269 : fixedDim[0] = arraytyplen / elmlen;
8295 1882 105269 : fixedLb[0] = 0;
1883 105269 : dim = fixedDim;
1884 105269 : lb = fixedLb;
2974 1885 105269 : arraydataptr = (char *) DatumGetPointer(arraydatum);
6352 1886 105269 : arraynullsptr = NULL;
1887 : }
2887 1888 266673 : else if (VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(arraydatum)))
1889 : {
1890 : /* expanded array: let's do this in a separate function */
1891 2490 : return array_get_element_expanded(arraydatum,
1892 : nSubscripts,
1893 : indx,
1894 : arraytyplen,
1895 : elmlen,
1896 : elmbyval,
1897 : elmalign,
1898 : isNull);
2887 tgl 1899 ECB : }
1900 : else
1901 : {
1902 : /* detoast array if necessary, producing normal varlena input */
2887 tgl 1903 GIC 264183 : ArrayType *array = DatumGetArrayTypeP(arraydatum);
8296 tgl 1904 ECB :
8295 tgl 1905 CBC 264183 : ndim = ARR_NDIM(array);
1906 264183 : dim = ARR_DIMS(array);
1907 264183 : lb = ARR_LBOUND(array);
1908 264183 : arraydataptr = ARR_DATA_PTR(array);
6352 1909 264183 : arraynullsptr = ARR_NULLBITMAP(array);
8295 tgl 1910 ECB : }
1911 :
1912 : /*
1913 : * Return NULL for invalid subscript
1914 : */
8295 tgl 1915 CBC 369452 : if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
1916 : {
6352 tgl 1917 GIC 48 : *isNull = true;
1918 48 : return (Datum) 0;
1919 : }
8295 1920 711769 : for (i = 0; i < ndim; i++)
1921 : {
1922 369452 : if (indx[i] < lb[i] || indx[i] >= (dim[i] + lb[i]))
1923 : {
6352 1924 27087 : *isNull = true;
1925 27087 : return (Datum) 0;
1926 : }
6352 tgl 1927 ECB : }
1928 :
8295 1929 : /*
6352 1930 : * Calculate the element number
8295 1931 : */
8296 tgl 1932 CBC 342317 : offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
9345 bruce 1933 ECB :
1934 : /*
1935 : * Check for NULL array element
1936 : */
6352 tgl 1937 GIC 342317 : if (array_get_isnull(arraynullsptr, offset))
1938 : {
6352 tgl 1939 CBC 27 : *isNull = true;
6352 tgl 1940 GIC 27 : return (Datum) 0;
6352 tgl 1941 ECB : }
9345 bruce 1942 :
1943 : /*
6352 tgl 1944 : * OK, get the element
1945 : */
7709 peter_e 1946 CBC 342290 : *isNull = false;
6352 tgl 1947 GIC 342290 : retptr = array_seek(arraydataptr, 0, arraynullsptr, offset,
6352 tgl 1948 ECB : elmlen, elmbyval, elmalign);
8296 tgl 1949 CBC 342290 : return ArrayCast(retptr, elmbyval, elmlen);
1950 : }
1951 :
1952 : /*
1953 : * Implementation of array_get_element() for an expanded array
1954 : */
1955 : static Datum
2887 1956 2490 : array_get_element_expanded(Datum arraydatum,
1957 : int nSubscripts, int *indx,
1958 : int arraytyplen,
1959 : int elmlen, bool elmbyval, char elmalign,
1960 : bool *isNull)
2887 tgl 1961 ECB : {
1962 : ExpandedArrayHeader *eah;
1963 : int i,
1964 : ndim,
1965 : *dim,
1966 : *lb,
1967 : offset;
1968 : Datum *dvalues;
1969 : bool *dnulls;
1970 :
2887 tgl 1971 CBC 2490 : eah = (ExpandedArrayHeader *) DatumGetEOHP(arraydatum);
2887 tgl 1972 GIC 2490 : Assert(eah->ea_magic == EA_MAGIC);
2887 tgl 1973 ECB :
1974 : /* sanity-check caller's info against object */
2887 tgl 1975 GIC 2490 : Assert(arraytyplen == -1);
1976 2490 : Assert(elmlen == eah->typlen);
1977 2490 : Assert(elmbyval == eah->typbyval);
1978 2490 : Assert(elmalign == eah->typalign);
1979 :
2887 tgl 1980 CBC 2490 : ndim = eah->ndims;
2887 tgl 1981 GIC 2490 : dim = eah->dims;
1982 2490 : lb = eah->lbound;
1983 :
1984 : /*
1985 : * Return NULL for invalid subscript
1986 : */
1987 2490 : if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
1988 : {
1989 3 : *isNull = true;
1990 3 : return (Datum) 0;
1991 : }
1992 4974 : for (i = 0; i < ndim; i++)
1993 : {
1994 2487 : if (indx[i] < lb[i] || indx[i] >= (dim[i] + lb[i]))
2887 tgl 1995 ECB : {
2887 tgl 1996 LBC 0 : *isNull = true;
2887 tgl 1997 UIC 0 : return (Datum) 0;
1998 : }
2887 tgl 1999 ECB : }
2000 :
2001 : /*
2002 : * Calculate the element number
2003 : */
2887 tgl 2004 CBC 2487 : offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
2887 tgl 2005 ECB :
2006 : /*
2007 : * Deconstruct array if we didn't already. Note that we apply this even
2008 : * if the input is nominally read-only: it should be safe enough.
2009 : */
2887 tgl 2010 GIC 2487 : deconstruct_expanded_array(eah);
2887 tgl 2011 ECB :
2887 tgl 2012 GIC 2487 : dvalues = eah->dvalues;
2887 tgl 2013 CBC 2487 : dnulls = eah->dnulls;
2887 tgl 2014 ECB :
2015 : /*
2016 : * Check for NULL array element
2017 : */
2887 tgl 2018 CBC 2487 : if (dnulls && dnulls[offset])
2019 : {
2887 tgl 2020 UBC 0 : *isNull = true;
2021 0 : return (Datum) 0;
2022 : }
2023 :
2024 : /*
2025 : * OK, get the element. It's OK to return a pass-by-ref value as a
2026 : * pointer into the expanded array, for the same reason that regular
2027 : * array_get_element can return a pointer into flat arrays: the value is
2887 tgl 2028 ECB : * assumed not to change for as long as the Datum reference can exist.
2029 : */
2887 tgl 2030 GIC 2487 : *isNull = false;
2031 2487 : return dvalues[offset];
2032 : }
2033 :
6352 tgl 2034 ECB : /*
2035 : * array_get_slice :
1329 michael 2036 : * This routine takes an array and a range of indices (upperIndx and
9345 bruce 2037 : * lowerIndx), creates a new array structure for the referred elements
2038 : * and returns a pointer to it.
2039 : *
2040 : * This handles both ordinary varlena arrays and fixed-length arrays.
2041 : *
6352 tgl 2042 : * Inputs:
2043 : * arraydatum: the array object (mustn't be NULL)
6352 tgl 2044 EUB : * nSubscripts: number of subscripts supplied (must be same for upper/lower)
2045 : * upperIndx[]: the upper subscript values
2046 : * lowerIndx[]: the lower subscript values
2047 : * upperProvided[]: true for provided upper subscript values
2048 : * lowerProvided[]: true for provided lower subscript values
2049 : * arraytyplen: pg_type.typlen for the array type
2050 : * elmlen: pg_type.typlen for the array's element type
2051 : * elmbyval: pg_type.typbyval for the array's element type
2052 : * elmalign: pg_type.typalign for the array's element type
2053 : *
6352 tgl 2054 ECB : * Outputs:
2055 : * The return value is the new array Datum (it's never NULL)
2056 : *
2057 : * Omitted upper and lower subscript values are replaced by the corresponding
2058 : * array bound.
2059 : *
2060 : * NOTE: we assume it is OK to scribble on the provided subscript arrays
2061 : * lowerIndx[] and upperIndx[]; also, these arrays must be of size MAXDIM
2062 : * even when nSubscripts is less. These are generally just temporaries.
2063 : */
2064 : Datum
2974 tgl 2065 GIC 192 : array_get_slice(Datum arraydatum,
2066 : int nSubscripts,
2067 : int *upperIndx,
2068 : int *lowerIndx,
2069 : bool *upperProvided,
2070 : bool *lowerProvided,
2071 : int arraytyplen,
2072 : int elmlen,
2073 : bool elmbyval,
2074 : char elmalign)
2075 : {
2076 : ArrayType *array;
2077 : ArrayType *newarray;
2078 : int i,
2079 : ndim,
2080 : *dim,
2081 : *lb,
2082 : *newlb;
2083 : int fixedDim[1],
2084 : fixedLb[1];
2085 : Oid elemtype;
2086 : char *arraydataptr;
2087 : bits8 *arraynullsptr;
2088 : int32 dataoffset;
9344 bruce 2089 ECB : int bytes,
2090 : span[MAXDIM];
2091 :
6352 tgl 2092 GIC 192 : if (arraytyplen > 0)
2093 : {
2094 : /*
2095 : * fixed-length arrays -- currently, cannot slice these because parser
2096 : * labels output as being of the fixed-length array type! Code below
2097 : * shows how we could support it if the parser were changed to label
2098 : * output as a suitable varlena array type.
2099 : */
7196 2100 12 : ereport(ERROR,
2101 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2102 : errmsg("slices of fixed-length arrays not implemented")));
2103 :
2104 : /*
2105 : * fixed-length arrays -- these are assumed to be 1-d, 0-based
2106 : *
2107 : * XXX where would we get the correct ELEMTYPE from?
2108 : */
2109 : ndim = 1;
2110 : fixedDim[0] = arraytyplen / elmlen;
2111 : fixedLb[0] = 0;
2112 : dim = fixedDim;
2113 : lb = fixedLb;
2114 : elemtype = InvalidOid; /* XXX */
2115 : arraydataptr = (char *) DatumGetPointer(arraydatum);
6352 tgl 2116 ECB : arraynullsptr = NULL;
2117 : }
2118 : else
2119 : {
2120 : /* detoast input array if necessary */
2974 tgl 2121 GIC 180 : array = DatumGetArrayTypeP(arraydatum);
2122 :
8295 2123 180 : ndim = ARR_NDIM(array);
8295 tgl 2124 CBC 180 : dim = ARR_DIMS(array);
8295 tgl 2125 GIC 180 : lb = ARR_LBOUND(array);
6352 2126 180 : elemtype = ARR_ELEMTYPE(array);
8295 2127 180 : arraydataptr = ARR_DATA_PTR(array);
6352 2128 180 : arraynullsptr = ARR_NULLBITMAP(array);
2129 : }
2130 :
2131 : /*
2132 : * Check provided subscripts. A slice exceeding the current array limits
2133 : * is silently truncated to the array limits. If we end up with an empty
2134 : * slice, return an empty array.
2135 : */
7690 2136 180 : if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
2974 2137 48 : return PointerGetDatum(construct_empty_array(elemtype));
2138 :
7690 2139 303 : for (i = 0; i < nSubscripts; i++)
2140 : {
2665 2141 177 : if (!lowerProvided[i] || lowerIndx[i] < lb[i])
8295 2142 63 : lowerIndx[i] = lb[i];
2665 2143 177 : if (!upperProvided[i] || upperIndx[i] >= (dim[i] + lb[i]))
8295 2144 36 : upperIndx[i] = dim[i] + lb[i] - 1;
9345 bruce 2145 CBC 177 : if (lowerIndx[i] > upperIndx[i])
2974 tgl 2146 GIC 6 : return PointerGetDatum(construct_empty_array(elemtype));
8295 tgl 2147 ECB : }
7690 2148 : /* fill any missing subscript positions with full array range */
7690 tgl 2149 CBC 147 : for (; i < ndim; i++)
7690 tgl 2150 ECB : {
7690 tgl 2151 CBC 21 : lowerIndx[i] = lb[i];
2152 21 : upperIndx[i] = dim[i] + lb[i] - 1;
7690 tgl 2153 GIC 21 : if (lowerIndx[i] > upperIndx[i])
2974 tgl 2154 UIC 0 : return PointerGetDatum(construct_empty_array(elemtype));
2155 : }
2156 :
7690 tgl 2157 GIC 126 : mda_get_range(ndim, span, lowerIndx, upperIndx);
2158 :
6352 2159 126 : bytes = array_slice_size(arraydataptr, arraynullsptr,
6352 tgl 2160 ECB : ndim, dim, lb,
7531 2161 : lowerIndx, upperIndx,
2162 : elmlen, elmbyval, elmalign);
6352 2163 :
2164 : /*
2165 : * Currently, we put a null bitmap in the result if the source has one;
2166 : * could be smarter ...
2167 : */
6352 tgl 2168 CBC 126 : if (arraynullsptr)
6352 tgl 2169 ECB : {
6352 tgl 2170 CBC 18 : dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, ArrayGetNItems(ndim, span));
6352 tgl 2171 GIC 18 : bytes += dataoffset;
2172 : }
6352 tgl 2173 ECB : else
2174 : {
6352 tgl 2175 CBC 108 : dataoffset = 0; /* marker for no null bitmap */
2176 108 : bytes += ARR_OVERHEAD_NONULLS(ndim);
6352 tgl 2177 ECB : }
8295 tgl 2178 EUB :
4365 tgl 2179 GIC 126 : newarray = (ArrayType *) palloc0(bytes);
5885 2180 126 : SET_VARSIZE(newarray, bytes);
8295 tgl 2181 CBC 126 : newarray->ndim = ndim;
6352 tgl 2182 GIC 126 : newarray->dataoffset = dataoffset;
6352 tgl 2183 CBC 126 : newarray->elemtype = elemtype;
8295 tgl 2184 GIC 126 : memcpy(ARR_DIMS(newarray), span, ndim * sizeof(int));
2185 :
2186 : /*
2187 : * Lower bounds of the new array are set to 1. Formerly (before 7.3) we
2188 : * copied the given lowerIndx values ... but that seems confusing.
2189 : */
7708 2190 126 : newlb = ARR_LBOUND(newarray);
2191 318 : for (i = 0; i < ndim; i++)
7708 tgl 2192 CBC 192 : newlb[i] = 1;
2193 :
6352 2194 126 : array_extract_slice(newarray,
6352 tgl 2195 ECB : ndim, dim, lb,
2196 : arraydataptr, arraynullsptr,
2197 : lowerIndx, upperIndx,
2198 : elmlen, elmbyval, elmalign);
8295 2199 :
2974 tgl 2200 CBC 126 : return PointerGetDatum(newarray);
2201 : }
2202 :
6352 tgl 2203 ECB : /*
2974 2204 : * array_set_element :
2205 : * This routine sets the value of one array element (specified by
6352 2206 : * a subscript array) to a new value specified by "dataValue".
2207 : *
2208 : * This handles both ordinary varlena arrays and fixed-length arrays.
2209 : *
2210 : * Inputs:
2211 : * arraydatum: the initial array object (mustn't be NULL)
2212 : * nSubscripts: number of subscripts supplied
2213 : * indx[]: the subscript values
2214 : * dataValue: the datum to be inserted at the given position
2215 : * isNull: whether dataValue is NULL
2216 : * arraytyplen: pg_type.typlen for the array type
2217 : * elmlen: pg_type.typlen for the array's element type
2218 : * elmbyval: pg_type.typbyval for the array's element type
2219 : * elmalign: pg_type.typalign for the array's element type
2220 : *
2221 : * Result:
2222 : * A new array is returned, just like the old except for the one
2223 : * modified entry. The original array object is not changed,
2887 2224 : * unless what is passed is a read-write reference to an expanded
2225 : * array object; in that case the expanded array is updated in-place.
2226 : *
2227 : * For one-dimensional arrays only, we allow the array to be extended
2228 : * by assigning to a position outside the existing subscript range; any
2229 : * positions between the existing elements and the new one are set to NULLs.
2230 : * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
2231 : *
2232 : * NOTE: For assignments, we throw an error for invalid subscripts etc,
2233 : * rather than returning a NULL as the fetch operations do.
2234 : */
2235 : Datum
2974 tgl 2236 GIC 2270 : array_set_element(Datum arraydatum,
2237 : int nSubscripts,
2238 : int *indx,
2239 : Datum dataValue,
2240 : bool isNull,
2241 : int arraytyplen,
2242 : int elmlen,
2243 : bool elmbyval,
2244 : char elmalign)
2245 : {
2246 : ArrayType *array;
2247 : ArrayType *newarray;
2248 : int i,
2249 : ndim,
2250 : dim[MAXDIM],
2251 : lb[MAXDIM],
2252 : offset;
2253 : char *elt_ptr;
2254 : bool newhasnulls;
2255 : bits8 *oldnullbitmap;
2256 : int oldnitems,
2257 : newnitems,
2258 : olddatasize,
2259 : newsize,
8295 tgl 2260 ECB : olditemlen,
2261 : newitemlen,
2262 : overheadlen,
2263 : oldoverheadlen,
2264 : addedbefore,
2265 : addedafter,
2266 : lenbefore,
2267 : lenafter;
2268 :
6352 tgl 2269 GIC 2270 : if (arraytyplen > 0)
2270 : {
2271 : /*
2272 : * fixed-length arrays -- these are assumed to be 1-d, 0-based. We
2273 : * cannot extend them, either.
2274 : */
2275 : char *resultarray;
2276 :
8296 2277 9 : if (nSubscripts != 1)
7196 tgl 2278 UIC 0 : ereport(ERROR,
2279 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2280 : errmsg("wrong number of array subscripts")));
2281 :
699 tgl 2282 GIC 9 : if (indx[0] < 0 || indx[0] >= arraytyplen / elmlen)
7196 2283 3 : ereport(ERROR,
2284 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2285 : errmsg("array subscript out of range")));
2286 :
6352 2287 6 : if (isNull)
6352 tgl 2288 UIC 0 : ereport(ERROR,
2289 : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
2290 : errmsg("cannot assign null value to an element of a fixed-length array")));
2291 :
2974 tgl 2292 GIC 6 : resultarray = (char *) palloc(arraytyplen);
2974 tgl 2293 CBC 6 : memcpy(resultarray, DatumGetPointer(arraydatum), arraytyplen);
2974 tgl 2294 GIC 6 : elt_ptr = (char *) resultarray + indx[0] * elmlen;
7531 2295 6 : ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign, elt_ptr);
2974 2296 6 : return PointerGetDatum(resultarray);
2297 : }
2298 :
6352 2299 2261 : if (nSubscripts <= 0 || nSubscripts > MAXDIM)
6352 tgl 2300 UIC 0 : ereport(ERROR,
6352 tgl 2301 ECB : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
6036 tgl 2302 EUB : errmsg("wrong number of array subscripts")));
2303 :
2304 : /* make sure item to be inserted is not toasted */
6352 tgl 2305 GIC 2261 : if (elmlen == -1 && !isNull)
8291 tgl 2306 CBC 1128 : dataValue = PointerGetDatum(PG_DETOAST_DATUM(dataValue));
8291 tgl 2307 ECB :
2887 tgl 2308 GIC 2261 : if (VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(arraydatum)))
2309 : {
2310 : /* expanded array: let's do this in a separate function */
2887 tgl 2311 CBC 996 : return array_set_element_expanded(arraydatum,
2887 tgl 2312 EUB : nSubscripts,
2313 : indx,
2314 : dataValue,
2315 : isNull,
2887 tgl 2316 ECB : arraytyplen,
2317 : elmlen,
2318 : elmbyval,
2319 : elmalign);
2320 : }
2321 :
2322 : /* detoast input array if necessary */
2974 tgl 2323 CBC 1265 : array = DatumGetArrayTypeP(arraydatum);
8296 tgl 2324 EUB :
8296 tgl 2325 GIC 1265 : ndim = ARR_NDIM(array);
2326 :
2327 : /*
2328 : * if number of dims is zero, i.e. an empty array, create an array with
6385 bruce 2329 ECB : * nSubscripts dimensions, and set the lower bounds to the supplied
2330 : * subscripts
2331 : */
7226 tgl 2332 CBC 1265 : if (ndim == 0)
2333 : {
7188 bruce 2334 GIC 108 : Oid elmtype = ARR_ELEMTYPE(array);
7226 tgl 2335 ECB :
7226 tgl 2336 GIC 217 : for (i = 0; i < nSubscripts; i++)
2337 : {
2338 109 : dim[i] = 1;
2339 109 : lb[i] = indx[i];
2340 : }
2341 :
2974 2342 108 : return PointerGetDatum(construct_md_array(&dataValue, &isNull,
2343 : nSubscripts, dim, lb,
2344 : elmtype,
2345 : elmlen, elmbyval, elmalign));
2346 : }
7226 tgl 2347 ECB :
6352 tgl 2348 GIC 1157 : if (ndim != nSubscripts)
7196 tgl 2349 CBC 3 : ereport(ERROR,
2350 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2351 : errmsg("wrong number of array subscripts")));
2352 :
2353 : /* copy dim/lb since we may modify them */
8295 tgl 2354 GIC 1154 : memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
2355 1154 : memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
9345 bruce 2356 ECB :
6036 tgl 2357 GIC 1154 : newhasnulls = (ARR_HASNULL(array) || isNull);
6036 tgl 2358 CBC 1154 : addedbefore = addedafter = 0;
2359 :
8295 tgl 2360 ECB : /*
2361 : * Check subscripts
2362 : */
6036 tgl 2363 CBC 1154 : if (ndim == 1)
2364 : {
6036 tgl 2365 GIC 1151 : if (indx[0] < lb[0])
8295 tgl 2366 ECB : {
6036 tgl 2367 GIC 12 : addedbefore = lb[0] - indx[0];
2368 12 : dim[0] += addedbefore;
2369 12 : lb[0] = indx[0];
2370 12 : if (addedbefore > 1)
2118 2371 6 : newhasnulls = true; /* will insert nulls */
8295 tgl 2372 ECB : }
6036 tgl 2373 CBC 1151 : if (indx[0] >= (dim[0] + lb[0]))
2374 : {
6036 tgl 2375 GIC 974 : addedafter = indx[0] - (dim[0] + lb[0]) + 1;
2376 974 : dim[0] += addedafter;
2377 974 : if (addedafter > 1)
2118 tgl 2378 CBC 18 : newhasnulls = true; /* will insert nulls */
6036 tgl 2379 ECB : }
2380 : }
2381 : else
2382 : {
2383 : /*
2384 : * XXX currently we do not support extending multi-dimensional arrays
2385 : * during assignment
2386 : */
6036 tgl 2387 CBC 9 : for (i = 0; i < ndim; i++)
2388 : {
2389 6 : if (indx[i] < lb[i] ||
6036 tgl 2390 GIC 6 : indx[i] >= (dim[i] + lb[i]))
7196 tgl 2391 LBC 0 : ereport(ERROR,
7196 tgl 2392 ECB : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
6036 2393 : errmsg("array subscript out of range")));
8295 2394 : }
2395 : }
2396 :
699 2397 : /* This checks for overflow of the array dimensions */
699 tgl 2398 GIC 1154 : newnitems = ArrayGetNItems(ndim, dim);
699 tgl 2399 CBC 1154 : ArrayCheckBounds(ndim, dim, lb);
699 tgl 2400 ECB :
8295 2401 : /*
2402 : * Compute sizes of items and areas to copy
2403 : */
6036 tgl 2404 GIC 1154 : if (newhasnulls)
2405 67 : overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, newnitems);
2406 : else
6352 2407 1087 : overheadlen = ARR_OVERHEAD_NONULLS(ndim);
2408 1154 : oldnitems = ArrayGetNItems(ndim, ARR_DIMS(array));
2409 1154 : oldnullbitmap = ARR_NULLBITMAP(array);
2410 1154 : oldoverheadlen = ARR_DATA_OFFSET(array);
6352 tgl 2411 CBC 1154 : olddatasize = ARR_SIZE(array) - oldoverheadlen;
6036 tgl 2412 GIC 1154 : if (addedbefore)
8295 tgl 2413 ECB : {
6352 tgl 2414 CBC 12 : offset = 0;
8295 tgl 2415 GBC 12 : lenbefore = 0;
8295 tgl 2416 GIC 12 : olditemlen = 0;
2417 12 : lenafter = olddatasize;
2418 : }
6036 2419 1142 : else if (addedafter)
2420 : {
6352 2421 974 : offset = oldnitems;
8295 tgl 2422 CBC 974 : lenbefore = olddatasize;
2423 974 : olditemlen = 0;
8295 tgl 2424 GIC 974 : lenafter = 0;
2425 : }
2426 : else
2427 : {
8295 tgl 2428 CBC 168 : offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
6352 2429 168 : elt_ptr = array_seek(ARR_DATA_PTR(array), 0, oldnullbitmap, offset,
2430 : elmlen, elmbyval, elmalign);
8295 2431 168 : lenbefore = (int) (elt_ptr - ARR_DATA_PTR(array));
6352 2432 168 : if (array_get_isnull(oldnullbitmap, offset))
2433 12 : olditemlen = 0;
6352 tgl 2434 ECB : else
2435 : {
5847 tgl 2436 CBC 156 : olditemlen = att_addlength_pointer(0, elmlen, elt_ptr);
5847 tgl 2437 GIC 156 : olditemlen = att_align_nominal(olditemlen, elmalign);
6352 tgl 2438 ECB : }
8295 tgl 2439 CBC 168 : lenafter = (int) (olddatasize - lenbefore - olditemlen);
9345 bruce 2440 ECB : }
8296 tgl 2441 :
6352 tgl 2442 GIC 1154 : if (isNull)
6352 tgl 2443 CBC 10 : newitemlen = 0;
2444 : else
6352 tgl 2445 ECB : {
5847 tgl 2446 CBC 1144 : newitemlen = att_addlength_datum(0, elmlen, dataValue);
2447 1144 : newitemlen = att_align_nominal(newitemlen, elmalign);
6352 tgl 2448 ECB : }
2449 :
8295 tgl 2450 GIC 1154 : newsize = overheadlen + lenbefore + newitemlen + lenafter;
2451 :
8295 tgl 2452 ECB : /*
6352 2453 : * OK, create the new array and fill in header/dimensions
2454 : */
4365 tgl 2455 CBC 1154 : newarray = (ArrayType *) palloc0(newsize);
5885 2456 1154 : SET_VARSIZE(newarray, newsize);
8295 2457 1154 : newarray->ndim = ndim;
6352 tgl 2458 GIC 1154 : newarray->dataoffset = newhasnulls ? overheadlen : 0;
7531 2459 1154 : newarray->elemtype = ARR_ELEMTYPE(array);
8295 tgl 2460 CBC 1154 : memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
2461 1154 : memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
2462 :
6352 tgl 2463 ECB : /*
2464 : * Fill in data
2465 : */
8295 tgl 2466 CBC 1154 : memcpy((char *) newarray + overheadlen,
6352 tgl 2467 ECB : (char *) array + oldoverheadlen,
2468 : lenbefore);
6352 tgl 2469 GIC 1154 : if (!isNull)
6352 tgl 2470 CBC 1144 : ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign,
2471 1144 : (char *) newarray + overheadlen + lenbefore);
8295 tgl 2472 GIC 1154 : memcpy((char *) newarray + overheadlen + lenbefore + newitemlen,
6352 2473 1154 : (char *) array + oldoverheadlen + lenbefore + olditemlen,
8295 tgl 2474 ECB : lenafter);
2475 :
2476 : /*
2477 : * Fill in nulls bitmap if needed
2478 : *
6347 bruce 2479 : * Note: it's possible we just replaced the last NULL with a non-NULL, and
2480 : * could get rid of the bitmap. Seems not worth testing for though.
6352 tgl 2481 : */
6352 tgl 2482 CBC 1154 : if (newhasnulls)
6352 tgl 2483 ECB : {
6347 bruce 2484 CBC 67 : bits8 *newnullbitmap = ARR_NULLBITMAP(newarray);
6352 tgl 2485 ECB :
2486 : /* palloc0 above already marked any inserted positions as nulls */
2487 : /* Fix the inserted value */
6036 tgl 2488 GIC 67 : if (addedafter)
2489 28 : array_set_isnull(newnullbitmap, newnitems - 1, isNull);
6036 tgl 2490 ECB : else
6036 tgl 2491 GIC 39 : array_set_isnull(newnullbitmap, offset, isNull);
2492 : /* Fix the copied range(s) */
6036 tgl 2493 CBC 67 : if (addedbefore)
2494 12 : array_bitmap_copy(newnullbitmap, addedbefore,
6352 tgl 2495 ECB : oldnullbitmap, 0,
2496 : oldnitems);
2497 : else
2498 : {
6352 tgl 2499 GIC 55 : array_bitmap_copy(newnullbitmap, 0,
2500 : oldnullbitmap, 0,
2501 : offset);
6036 2502 55 : if (addedafter == 0)
6347 bruce 2503 27 : array_bitmap_copy(newnullbitmap, offset + 1,
2504 : oldnullbitmap, offset + 1,
6352 tgl 2505 27 : oldnitems - offset - 1);
6352 tgl 2506 ECB : }
2507 : }
8296 2508 :
2974 tgl 2509 GIC 1154 : return PointerGetDatum(newarray);
2510 : }
2511 :
2887 tgl 2512 ECB : /*
2513 : * Implementation of array_set_element() for an expanded array
2514 : *
2515 : * Note: as with any operation on a read/write expanded object, we must
2516 : * take pains not to leave the object in a corrupt state if we fail partway
2517 : * through.
2518 : */
2519 : static Datum
2887 tgl 2520 GIC 996 : array_set_element_expanded(Datum arraydatum,
2521 : int nSubscripts, int *indx,
2522 : Datum dataValue, bool isNull,
2887 tgl 2523 ECB : int arraytyplen,
2524 : int elmlen, bool elmbyval, char elmalign)
2525 : {
2526 : ExpandedArrayHeader *eah;
2527 : Datum *dvalues;
2528 : bool *dnulls;
2529 : int i,
2530 : ndim,
2531 : dim[MAXDIM],
2532 : lb[MAXDIM],
2533 : offset;
2534 : bool dimschanged,
2535 : newhasnulls;
2536 : int addedbefore,
2537 : addedafter;
2538 : char *oldValue;
2539 :
2540 : /* Convert to R/W object if not so already */
2887 tgl 2541 GIC 996 : eah = DatumGetExpandedArray(arraydatum);
2542 :
2543 : /* Sanity-check caller's info against object; we don't use it otherwise */
2887 tgl 2544 CBC 996 : Assert(arraytyplen == -1);
2887 tgl 2545 GIC 996 : Assert(elmlen == eah->typlen);
2546 996 : Assert(elmbyval == eah->typbyval);
2547 996 : Assert(elmalign == eah->typalign);
2548 :
2549 : /*
2550 : * Copy dimension info into local storage. This allows us to modify the
2551 : * dimensions if needed, while not messing up the expanded value if we
2552 : * fail partway through.
2553 : */
2554 996 : ndim = eah->ndims;
2555 996 : Assert(ndim >= 0 && ndim <= MAXDIM);
2556 996 : memcpy(dim, eah->dims, ndim * sizeof(int));
2557 996 : memcpy(lb, eah->lbound, ndim * sizeof(int));
2558 996 : dimschanged = false;
2559 :
2560 : /*
2561 : * if number of dims is zero, i.e. an empty array, create an array with
2562 : * nSubscripts dimensions, and set the lower bounds to the supplied
2563 : * subscripts.
2564 : */
2887 tgl 2565 CBC 996 : if (ndim == 0)
2566 : {
2567 : /*
2887 tgl 2568 ECB : * Allocate adequate space for new dimension info. This is harmless
2569 : * if we fail later.
2570 : */
2887 tgl 2571 CBC 208 : Assert(nSubscripts > 0 && nSubscripts <= MAXDIM);
2887 tgl 2572 GIC 208 : eah->dims = (int *) MemoryContextAllocZero(eah->hdr.eoh_context,
2573 : nSubscripts * sizeof(int));
2574 208 : eah->lbound = (int *) MemoryContextAllocZero(eah->hdr.eoh_context,
2575 : nSubscripts * sizeof(int));
2576 :
2577 : /* Update local copies of dimension info */
2887 tgl 2578 CBC 208 : ndim = nSubscripts;
2579 416 : for (i = 0; i < nSubscripts; i++)
2887 tgl 2580 ECB : {
2887 tgl 2581 CBC 208 : dim[i] = 0;
2582 208 : lb[i] = indx[i];
2583 : }
2887 tgl 2584 GIC 208 : dimschanged = true;
2585 : }
2586 788 : else if (ndim != nSubscripts)
2887 tgl 2587 UIC 0 : ereport(ERROR,
2588 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2887 tgl 2589 ECB : errmsg("wrong number of array subscripts")));
2590 :
2591 : /*
2592 : * Deconstruct array if we didn't already. (Someday maybe add a special
2593 : * case path for fixed-length, no-nulls cases, where we can overwrite an
2594 : * element in place without ever deconstructing. But today is not that
2595 : * day.)
2596 : */
2887 tgl 2597 GIC 996 : deconstruct_expanded_array(eah);
2887 tgl 2598 ECB :
2599 : /*
2600 : * Copy new element into array's context, if needed (we assume it's
2601 : * already detoasted, so no junk should be created). Doing this before
825 2602 : * we've made any significant changes ensures that our behavior is sane
2603 : * even when the source is a reference to some element of this same array.
2604 : * If we fail further down, this memory is leaked, but that's reasonably
2605 : * harmless.
2887 2606 : */
2887 tgl 2607 GIC 996 : if (!eah->typbyval && !isNull)
2887 tgl 2608 ECB : {
2887 tgl 2609 GIC 449 : MemoryContext oldcxt = MemoryContextSwitchTo(eah->hdr.eoh_context);
2887 tgl 2610 ECB :
2887 tgl 2611 GBC 449 : dataValue = datumCopy(dataValue, false, eah->typlen);
2887 tgl 2612 GIC 449 : MemoryContextSwitchTo(oldcxt);
2613 : }
2614 :
2615 996 : dvalues = eah->dvalues;
2616 996 : dnulls = eah->dnulls;
2617 :
2618 996 : newhasnulls = ((dnulls != NULL) || isNull);
2619 996 : addedbefore = addedafter = 0;
2620 :
2887 tgl 2621 ECB : /*
2622 : * Check subscripts (this logic matches original array_set_element)
2623 : */
2887 tgl 2624 GIC 996 : if (ndim == 1)
2625 : {
2626 996 : if (indx[0] < lb[0])
2627 : {
2628 39 : addedbefore = lb[0] - indx[0];
2629 39 : dim[0] += addedbefore;
2630 39 : lb[0] = indx[0];
2887 tgl 2631 CBC 39 : dimschanged = true;
2887 tgl 2632 GIC 39 : if (addedbefore > 1)
2118 tgl 2633 LBC 0 : newhasnulls = true; /* will insert nulls */
2634 : }
2887 tgl 2635 CBC 996 : if (indx[0] >= (dim[0] + lb[0]))
2887 tgl 2636 ECB : {
2887 tgl 2637 GIC 938 : addedafter = indx[0] - (dim[0] + lb[0]) + 1;
2638 938 : dim[0] += addedafter;
2887 tgl 2639 CBC 938 : dimschanged = true;
2640 938 : if (addedafter > 1)
2118 tgl 2641 UIC 0 : newhasnulls = true; /* will insert nulls */
2887 tgl 2642 ECB : }
2643 : }
2644 : else
2645 : {
2646 : /*
2647 : * XXX currently we do not support extending multi-dimensional arrays
2648 : * during assignment
2649 : */
2887 tgl 2650 LBC 0 : for (i = 0; i < ndim; i++)
2651 : {
2652 0 : if (indx[i] < lb[i] ||
2653 0 : indx[i] >= (dim[i] + lb[i]))
2654 0 : ereport(ERROR,
2887 tgl 2655 ECB : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2656 : errmsg("array subscript out of range")));
2887 tgl 2657 EUB : }
2658 : }
2887 tgl 2659 ECB :
2660 : /* Check for overflow of the array dimensions */
699 tgl 2661 CBC 996 : if (dimschanged)
699 tgl 2662 ECB : {
699 tgl 2663 CBC 977 : (void) ArrayGetNItems(ndim, dim);
2664 977 : ArrayCheckBounds(ndim, dim, lb);
699 tgl 2665 EUB : }
2666 :
2667 : /* Now we can calculate linear offset of target item in array */
2887 tgl 2668 GIC 996 : offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
2669 :
2670 : /* Physically enlarge existing dvalues/dnulls arrays if needed */
2671 996 : if (dim[0] > eah->dvalueslen)
2672 : {
2673 : /* We want some extra space if we're enlarging */
2887 tgl 2674 GBC 971 : int newlen = dim[0] + dim[0] / 8;
2675 :
2676 971 : newlen = Max(newlen, dim[0]); /* integer overflow guard */
2677 971 : eah->dvalues = dvalues = (Datum *)
2678 971 : repalloc(dvalues, newlen * sizeof(Datum));
2887 tgl 2679 GIC 971 : if (dnulls)
2887 tgl 2680 UIC 0 : eah->dnulls = dnulls = (bool *)
2681 0 : repalloc(dnulls, newlen * sizeof(bool));
2887 tgl 2682 GIC 971 : eah->dvalueslen = newlen;
2683 : }
2684 :
2887 tgl 2685 ECB : /*
2686 : * If we need a nulls bitmap and don't already have one, create it, being
2687 : * sure to mark all existing entries as not null.
2688 : */
2887 tgl 2689 GIC 996 : if (newhasnulls && dnulls == NULL)
2887 tgl 2690 UIC 0 : eah->dnulls = dnulls = (bool *)
2691 0 : MemoryContextAllocZero(eah->hdr.eoh_context,
2887 tgl 2692 LBC 0 : eah->dvalueslen * sizeof(bool));
2693 :
2694 : /*
2887 tgl 2695 ECB : * We now have all the needed space allocated, so we're ready to make
2696 : * irreversible changes. Be very wary of allowing failure below here.
2697 : */
2698 :
2699 : /* Flattened value will no longer represent array accurately */
2887 tgl 2700 CBC 996 : eah->fvalue = NULL;
2887 tgl 2701 ECB : /* And we don't know the flattened size either */
2887 tgl 2702 CBC 996 : eah->flat_size = 0;
2887 tgl 2703 ECB :
2887 tgl 2704 EUB : /* Update dimensionality info if needed */
2887 tgl 2705 GBC 996 : if (dimschanged)
2887 tgl 2706 ECB : {
2887 tgl 2707 GIC 977 : eah->ndims = ndim;
2708 977 : memcpy(eah->dims, dim, ndim * sizeof(int));
2709 977 : memcpy(eah->lbound, lb, ndim * sizeof(int));
2710 : }
2711 :
2712 : /* Reposition items if needed, and fill addedbefore items with nulls */
2887 tgl 2713 CBC 996 : if (addedbefore > 0)
2887 tgl 2714 EUB : {
2887 tgl 2715 GBC 39 : memmove(dvalues + addedbefore, dvalues, eah->nelems * sizeof(Datum));
2716 78 : for (i = 0; i < addedbefore; i++)
2887 tgl 2717 GIC 39 : dvalues[i] = (Datum) 0;
2718 39 : if (dnulls)
2719 : {
2887 tgl 2720 UIC 0 : memmove(dnulls + addedbefore, dnulls, eah->nelems * sizeof(bool));
2721 0 : for (i = 0; i < addedbefore; i++)
2722 0 : dnulls[i] = true;
2723 : }
2887 tgl 2724 CBC 39 : eah->nelems += addedbefore;
2725 : }
2887 tgl 2726 ECB :
2727 : /* fill addedafter items with nulls */
2887 tgl 2728 GIC 996 : if (addedafter > 0)
2887 tgl 2729 ECB : {
2887 tgl 2730 GIC 1876 : for (i = 0; i < addedafter; i++)
2887 tgl 2731 CBC 938 : dvalues[eah->nelems + i] = (Datum) 0;
2732 938 : if (dnulls)
2887 tgl 2733 ECB : {
2887 tgl 2734 UIC 0 : for (i = 0; i < addedafter; i++)
2735 0 : dnulls[eah->nelems + i] = true;
2736 : }
2887 tgl 2737 CBC 938 : eah->nelems += addedafter;
2738 : }
2887 tgl 2739 ECB :
2740 : /* Grab old element value for pfree'ing, if needed. */
2887 tgl 2741 CBC 996 : if (!eah->typbyval && (dnulls == NULL || !dnulls[offset]))
2742 449 : oldValue = (char *) DatumGetPointer(dvalues[offset]);
2743 : else
2887 tgl 2744 GBC 547 : oldValue = NULL;
2887 tgl 2745 EUB :
2746 : /* And finally we can insert the new element. */
2887 tgl 2747 GIC 996 : dvalues[offset] = dataValue;
2887 tgl 2748 CBC 996 : if (dnulls)
2887 tgl 2749 UIC 0 : dnulls[offset] = isNull;
2750 :
2751 : /*
2887 tgl 2752 ECB : * Free old element if needed; this keeps repeated element replacements
2753 : * from bloating the array's storage. If the pfree somehow fails, it
2754 : * won't corrupt the array.
2755 : */
2887 tgl 2756 CBC 996 : if (oldValue)
2757 : {
2887 tgl 2758 EUB : /* Don't try to pfree a part of the original flat array */
2887 tgl 2759 GBC 1 : if (oldValue < eah->fstartptr || oldValue >= eah->fendptr)
2887 tgl 2760 UIC 0 : pfree(oldValue);
2887 tgl 2761 ECB : }
2762 :
2763 : /* Done, return standard TOAST pointer for object */
2887 tgl 2764 GIC 996 : return EOHPGetRWDatum(&eah->hdr);
2887 tgl 2765 ECB : }
2766 :
2767 : /*
8296 2768 : * array_set_slice :
2769 : * This routine sets the value of a range of array locations (specified
2770 : * by upper and lower subscript values) to new values passed as
6352 2771 : * another array.
2772 : *
6352 tgl 2773 EUB : * This handles both ordinary varlena arrays and fixed-length arrays.
2774 : *
2775 : * Inputs:
2776 : * arraydatum: the initial array object (mustn't be NULL)
2777 : * nSubscripts: number of subscripts supplied (must be same for upper/lower)
2778 : * upperIndx[]: the upper subscript values
2779 : * lowerIndx[]: the lower subscript values
2665 tgl 2780 ECB : * upperProvided[]: true for provided upper subscript values
2781 : * lowerProvided[]: true for provided lower subscript values
2782 : * srcArrayDatum: the source for the inserted values
2974 2783 : * isNull: indicates whether srcArrayDatum is NULL
6352 tgl 2784 EUB : * arraytyplen: pg_type.typlen for the array type
2785 : * elmlen: pg_type.typlen for the array's element type
2786 : * elmbyval: pg_type.typbyval for the array's element type
2787 : * elmalign: pg_type.typalign for the array's element type
6352 tgl 2788 ECB : *
2789 : * Result:
2790 : * A new array is returned, just like the old except for the
2791 : * modified range. The original array object is not changed.
2792 : *
2793 : * Omitted upper and lower subscript values are replaced by the corresponding
2794 : * array bound.
2795 : *
2796 : * For one-dimensional arrays only, we allow the array to be extended
2797 : * by assigning to positions outside the existing subscript range; any
2798 : * positions between the existing elements and the new ones are set to NULLs.
2799 : * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
2800 : *
2801 : * NOTE: we assume it is OK to scribble on the provided index arrays
2802 : * lowerIndx[] and upperIndx[]; also, these arrays must be of size MAXDIM
2803 : * even when nSubscripts is less. These are generally just temporaries.
2804 : *
2805 : * NOTE: For assignments, we throw an error for silly subscripts etc,
2806 : * rather than returning a NULL or empty array as the fetch operations do.
2807 : */
2808 : Datum
2974 tgl 2809 GIC 122 : array_set_slice(Datum arraydatum,
2810 : int nSubscripts,
2811 : int *upperIndx,
2812 : int *lowerIndx,
2813 : bool *upperProvided,
2814 : bool *lowerProvided,
2815 : Datum srcArrayDatum,
2816 : bool isNull,
2817 : int arraytyplen,
2818 : int elmlen,
2819 : bool elmbyval,
2820 : char elmalign)
2821 : {
2822 : ArrayType *array;
2823 : ArrayType *srcArray;
2824 : ArrayType *newarray;
2825 : int i,
2826 : ndim,
2827 : dim[MAXDIM],
2828 : lb[MAXDIM],
2829 : span[MAXDIM];
2830 : bool newhasnulls;
2831 : int nitems,
2832 : nsrcitems,
8295 tgl 2833 ECB : olddatasize,
2834 : newsize,
2835 : olditemsize,
2836 : newitemsize,
2837 : overheadlen,
2838 : oldoverheadlen,
2839 : addedbefore,
2840 : addedafter,
2841 : lenbefore,
2842 : lenafter,
2843 : itemsbefore,
2844 : itemsafter,
2845 : nolditems;
2846 :
2847 : /* Currently, assignment from a NULL source array is a no-op */
6352 tgl 2848 GIC 122 : if (isNull)
2974 tgl 2849 UIC 0 : return arraydatum;
2850 :
6352 tgl 2851 GIC 122 : if (arraytyplen > 0)
2852 : {
2853 : /*
2854 : * fixed-length arrays -- not got round to doing this...
2855 : */
7196 tgl 2856 UIC 0 : ereport(ERROR,
2857 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2858 : errmsg("updates on slices of fixed-length arrays not implemented")));
2859 : }
2860 :
2861 : /* detoast arrays if necessary */
2974 tgl 2862 GIC 122 : array = DatumGetArrayTypeP(arraydatum);
2863 122 : srcArray = DatumGetArrayTypeP(srcArrayDatum);
2864 :
2865 : /* note: we assume srcArray contains no toasted elements */
2866 :
8296 2867 122 : ndim = ARR_NDIM(array);
2868 :
2869 : /*
2870 : * if number of dims is zero, i.e. an empty array, create an array with
2871 : * nSubscripts dimensions, and set the upper and lower bounds to the
6385 bruce 2872 ECB : * supplied subscripts
7226 tgl 2873 EUB : */
7226 tgl 2874 GIC 122 : if (ndim == 0)
7226 tgl 2875 ECB : {
2876 : Datum *dvalues;
2877 : bool *dnulls;
2878 : int nelems;
7188 bruce 2879 GIC 22 : Oid elmtype = ARR_ELEMTYPE(array);
7226 tgl 2880 EUB :
7226 tgl 2881 GIC 22 : deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
2882 : &dvalues, &dnulls, &nelems);
2883 :
2884 50 : for (i = 0; i < nSubscripts; i++)
2885 : {
2665 tgl 2886 CBC 31 : if (!upperProvided[i] || !lowerProvided[i])
2887 3 : ereport(ERROR,
2888 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2889 : errmsg("array slice subscript must provide both boundaries"),
2890 : errdetail("When assigning to a slice of an empty array value,"
2118 tgl 2891 ECB : " slice boundaries must be fully specified.")));
2892 :
7226 tgl 2893 GIC 28 : dim[i] = 1 + upperIndx[i] - lowerIndx[i];
2894 28 : lb[i] = lowerIndx[i];
2895 : }
2896 :
2897 : /* complain if too few source items; we ignore extras, however */
6879 tgl 2898 CBC 19 : if (nelems < ArrayGetNItems(nSubscripts, dim))
6879 tgl 2899 UIC 0 : ereport(ERROR,
2900 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2901 : errmsg("source array too small")));
2902 :
2974 tgl 2903 CBC 19 : return PointerGetDatum(construct_md_array(dvalues, dnulls, nSubscripts,
2904 : dim, lb, elmtype,
2118 tgl 2905 ECB : elmlen, elmbyval, elmalign));
2906 : }
2907 :
7690 tgl 2908 CBC 100 : if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
7196 tgl 2909 UIC 0 : ereport(ERROR,
7196 tgl 2910 ECB : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
6036 2911 : errmsg("wrong number of array subscripts")));
2912 :
2913 : /* copy dim/lb since we may modify them */
8295 tgl 2914 GIC 100 : memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
2915 100 : memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
2916 :
6036 tgl 2917 CBC 100 : newhasnulls = (ARR_HASNULL(array) || ARR_HASNULL(srcArray));
2918 100 : addedbefore = addedafter = 0;
2919 :
2920 : /*
2921 : * Check subscripts
8295 tgl 2922 ECB : */
6036 tgl 2923 GBC 100 : if (ndim == 1)
2924 : {
6036 tgl 2925 GIC 85 : Assert(nSubscripts == 1);
2665 2926 85 : if (!lowerProvided[0])
2665 tgl 2927 CBC 18 : lowerIndx[0] = lb[0];
2665 tgl 2928 GIC 85 : if (!upperProvided[0])
2929 21 : upperIndx[0] = dim[0] + lb[0] - 1;
6036 2930 85 : if (lowerIndx[0] > upperIndx[0])
7196 tgl 2931 UIC 0 : ereport(ERROR,
7196 tgl 2932 ECB : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
6036 tgl 2933 EUB : errmsg("upper bound cannot be less than lower bound")));
6036 tgl 2934 GIC 85 : if (lowerIndx[0] < lb[0])
2935 : {
2936 24 : if (upperIndx[0] < lb[0] - 1)
2118 2937 6 : newhasnulls = true; /* will insert nulls */
6036 tgl 2938 CBC 24 : addedbefore = lb[0] - lowerIndx[0];
2939 24 : dim[0] += addedbefore;
6036 tgl 2940 GIC 24 : lb[0] = lowerIndx[0];
6036 tgl 2941 ECB : }
6036 tgl 2942 CBC 85 : if (upperIndx[0] >= (dim[0] + lb[0]))
2943 : {
6036 tgl 2944 GIC 28 : if (lowerIndx[0] > (dim[0] + lb[0]))
2118 2945 6 : newhasnulls = true; /* will insert nulls */
6036 2946 28 : addedafter = upperIndx[0] - (dim[0] + lb[0]) + 1;
6036 tgl 2947 CBC 28 : dim[0] += addedafter;
2948 : }
6036 tgl 2949 ECB : }
2950 : else
2951 : {
2952 : /*
6031 bruce 2953 : * XXX currently we do not support extending multi-dimensional arrays
2954 : * during assignment
6036 tgl 2955 EUB : */
6036 tgl 2956 GIC 51 : for (i = 0; i < nSubscripts; i++)
2957 : {
2665 tgl 2958 CBC 36 : if (!lowerProvided[i])
2665 tgl 2959 GIC 6 : lowerIndx[i] = lb[i];
2665 tgl 2960 CBC 36 : if (!upperProvided[i])
2961 12 : upperIndx[i] = dim[i] + lb[i] - 1;
6036 2962 36 : if (lowerIndx[i] > upperIndx[i])
6036 tgl 2963 LBC 0 : ereport(ERROR,
6036 tgl 2964 ECB : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2965 : errmsg("upper bound cannot be less than lower bound")));
6036 tgl 2966 CBC 36 : if (lowerIndx[i] < lb[i] ||
6036 tgl 2967 GIC 36 : upperIndx[i] >= (dim[i] + lb[i]))
7196 tgl 2968 LBC 0 : ereport(ERROR,
7196 tgl 2969 ECB : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
6036 2970 : errmsg("array subscript out of range")));
8295 2971 : }
2972 : /* fill any missing subscript positions with full array range */
6036 tgl 2973 GIC 15 : for (; i < ndim; i++)
2974 : {
6036 tgl 2975 UIC 0 : lowerIndx[i] = lb[i];
2976 0 : upperIndx[i] = dim[i] + lb[i] - 1;
2977 0 : if (lowerIndx[i] > upperIndx[i])
7196 2978 0 : ereport(ERROR,
2979 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2118 tgl 2980 ECB : errmsg("upper bound cannot be less than lower bound")));
2981 : }
8295 2982 : }
9345 bruce 2983 :
6352 tgl 2984 : /* Do this mainly to check for overflow */
6352 tgl 2985 CBC 100 : nitems = ArrayGetNItems(ndim, dim);
699 2986 100 : ArrayCheckBounds(ndim, dim, lb);
6352 tgl 2987 EUB :
2988 : /*
2989 : * Make sure source array has enough entries. Note we ignore the shape of
6385 bruce 2990 ECB : * the source array and just read entries serially.
8295 tgl 2991 : */
8296 tgl 2992 GBC 100 : mda_get_range(ndim, span, lowerIndx, upperIndx);
8295 tgl 2993 GIC 100 : nsrcitems = ArrayGetNItems(ndim, span);
2994 100 : if (nsrcitems > ArrayGetNItems(ARR_NDIM(srcArray), ARR_DIMS(srcArray)))
7196 2995 3 : ereport(ERROR,
2996 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
7196 tgl 2997 ECB : errmsg("source array too small")));
2998 :
8295 tgl 2999 EUB : /*
3000 : * Compute space occupied by new entries, space occupied by replaced
3001 : * entries, and required space for new array.
3002 : */
6036 tgl 3003 GIC 97 : if (newhasnulls)
6352 3004 48 : overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
3005 : else
3006 49 : overheadlen = ARR_OVERHEAD_NONULLS(ndim);
3007 97 : newitemsize = array_nelems_size(ARR_DATA_PTR(srcArray), 0,
3008 97 : ARR_NULLBITMAP(srcArray), nsrcitems,
7531 tgl 3009 ECB : elmlen, elmbyval, elmalign);
6352 tgl 3010 CBC 97 : oldoverheadlen = ARR_DATA_OFFSET(array);
6352 tgl 3011 GIC 97 : olddatasize = ARR_SIZE(array) - oldoverheadlen;
8295 3012 97 : if (ndim > 1)
3013 : {
3014 : /*
3015 : * here we do not need to cope with extension of the array; it would
6385 bruce 3016 ECB : * be a lot more complicated if we had to do so...
8295 tgl 3017 : */
6352 tgl 3018 CBC 15 : olditemsize = array_slice_size(ARR_DATA_PTR(array),
3019 15 : ARR_NULLBITMAP(array),
3020 : ndim, dim, lb,
3021 : lowerIndx, upperIndx,
3022 : elmlen, elmbyval, elmalign);
2118 tgl 3023 GIC 15 : lenbefore = lenafter = 0; /* keep compiler quiet */
6352 3024 15 : itemsbefore = itemsafter = nolditems = 0;
3025 : }
3026 : else
8295 tgl 3027 ECB : {
3028 : /*
3029 : * here we must allow for possibility of slice larger than orig array
4465 3030 : * and/or not adjacent to orig array subscripts
8295 3031 : */
8053 bruce 3032 CBC 82 : int oldlb = ARR_LBOUND(array)[0];
8053 bruce 3033 GIC 82 : int oldub = oldlb + ARR_DIMS(array)[0] - 1;
7720 bruce 3034 CBC 82 : int slicelb = Max(oldlb, lowerIndx[0]);
3035 82 : int sliceub = Min(oldub, upperIndx[0]);
8053 3036 82 : char *oldarraydata = ARR_DATA_PTR(array);
6352 tgl 3037 GIC 82 : bits8 *oldarraybitmap = ARR_NULLBITMAP(array);
3038 :
3039 : /* count/size of old array entries that will go before the slice */
6036 3040 82 : itemsbefore = Min(slicelb, oldub + 1) - oldlb;
6352 3041 82 : lenbefore = array_nelems_size(oldarraydata, 0, oldarraybitmap,
6352 tgl 3042 ECB : itemsbefore,
7531 3043 : elmlen, elmbyval, elmalign);
3044 : /* count/size of old array entries that will be replaced by slice */
8295 tgl 3045 GIC 82 : if (slicelb > sliceub)
3046 : {
6352 tgl 3047 CBC 27 : nolditems = 0;
8295 3048 27 : olditemsize = 0;
3049 : }
3050 : else
3051 : {
6352 tgl 3052 GIC 55 : nolditems = sliceub - slicelb + 1;
8295 3053 55 : olditemsize = array_nelems_size(oldarraydata + lenbefore,
3054 : itemsbefore, oldarraybitmap,
3055 : nolditems,
7531 tgl 3056 ECB : elmlen, elmbyval, elmalign);
6352 3057 : }
4465 3058 : /* count/size of old array entries that will go after the slice */
4465 tgl 3059 CBC 82 : itemsafter = oldub + 1 - Max(sliceub + 1, oldlb);
8295 3060 82 : lenafter = olddatasize - lenbefore - olditemsize;
8295 tgl 3061 ECB : }
3062 :
8295 tgl 3063 GIC 97 : newsize = overheadlen + olddatasize - olditemsize + newitemsize;
8295 tgl 3064 ECB :
4365 tgl 3065 CBC 97 : newarray = (ArrayType *) palloc0(newsize);
5885 tgl 3066 GIC 97 : SET_VARSIZE(newarray, newsize);
8295 3067 97 : newarray->ndim = ndim;
6352 3068 97 : newarray->dataoffset = newhasnulls ? overheadlen : 0;
7531 tgl 3069 CBC 97 : newarray->elemtype = ARR_ELEMTYPE(array);
8295 tgl 3070 GIC 97 : memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
8295 tgl 3071 CBC 97 : memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
8295 tgl 3072 ECB :
8295 tgl 3073 GIC 97 : if (ndim > 1)
3074 : {
3075 : /*
6385 bruce 3076 ECB : * here we do not need to cope with extension of the array; it would
3077 : * be a lot more complicated if we had to do so...
3078 : */
6352 tgl 3079 GIC 15 : array_insert_slice(newarray, array, srcArray,
3080 : ndim, dim, lb,
3081 : lowerIndx, upperIndx,
3082 : elmlen, elmbyval, elmalign);
8295 tgl 3083 ECB : }
3084 : else
3085 : {
3086 : /* fill in data */
8295 tgl 3087 CBC 82 : memcpy((char *) newarray + overheadlen,
3088 : (char *) array + oldoverheadlen,
8295 tgl 3089 ECB : lenbefore);
8295 tgl 3090 CBC 164 : memcpy((char *) newarray + overheadlen + lenbefore,
3091 82 : ARR_DATA_PTR(srcArray),
8295 tgl 3092 ECB : newitemsize);
8295 tgl 3093 CBC 82 : memcpy((char *) newarray + overheadlen + lenbefore + newitemsize,
6352 3094 82 : (char *) array + oldoverheadlen + lenbefore + olditemsize,
8295 tgl 3095 ECB : lenafter);
3096 : /* fill in nulls bitmap if needed */
6352 tgl 3097 CBC 82 : if (newhasnulls)
3098 : {
6347 bruce 3099 GIC 48 : bits8 *newnullbitmap = ARR_NULLBITMAP(newarray);
3100 48 : bits8 *oldnullbitmap = ARR_NULLBITMAP(array);
3101 :
3102 : /* palloc0 above already marked any inserted positions as nulls */
6036 tgl 3103 CBC 48 : array_bitmap_copy(newnullbitmap, addedbefore,
3104 : oldnullbitmap, 0,
3105 : itemsbefore);
6036 tgl 3106 GIC 48 : array_bitmap_copy(newnullbitmap, lowerIndx[0] - lb[0],
6352 3107 48 : ARR_NULLBITMAP(srcArray), 0,
3108 : nsrcitems);
6036 3109 48 : array_bitmap_copy(newnullbitmap, addedbefore + itemsbefore + nolditems,
3110 : oldnullbitmap, itemsbefore + nolditems,
6352 tgl 3111 ECB : itemsafter);
3112 : }
3113 : }
9650 scrappy 3114 :
2974 tgl 3115 CBC 97 : return PointerGetDatum(newarray);
3116 : }
2974 tgl 3117 ECB :
3118 : /*
3119 : * array_ref : backwards compatibility wrapper for array_get_element
3120 : *
3121 : * This only works for detoasted/flattened varlena arrays, since the array
3122 : * argument is declared as "ArrayType *". However there's enough code like
3123 : * that to justify preserving this API.
3124 : */
3125 : Datum
2974 tgl 3126 GIC 37681 : array_ref(ArrayType *array, int nSubscripts, int *indx,
2974 tgl 3127 ECB : int arraytyplen, int elmlen, bool elmbyval, char elmalign,
3128 : bool *isNull)
3129 : {
2974 tgl 3130 CBC 37681 : return array_get_element(PointerGetDatum(array), nSubscripts, indx,
2974 tgl 3131 ECB : arraytyplen, elmlen, elmbyval, elmalign,
3132 : isNull);
3133 : }
3134 :
3135 : /*
3136 : * array_set : backwards compatibility wrapper for array_set_element
3137 : *
3138 : * This only works for detoasted/flattened varlena arrays, since the array
3139 : * argument and result are declared as "ArrayType *". However there's enough
3140 : * code like that to justify preserving this API.
3141 : */
3142 : ArrayType *
2974 tgl 3143 GIC 901 : array_set(ArrayType *array, int nSubscripts, int *indx,
3144 : Datum dataValue, bool isNull,
3145 : int arraytyplen, int elmlen, bool elmbyval, char elmalign)
3146 : {
3147 901 : return DatumGetArrayTypeP(array_set_element(PointerGetDatum(array),
3148 : nSubscripts, indx,
3149 : dataValue, isNull,
2974 tgl 3150 ECB : arraytyplen,
3151 : elmlen, elmbyval, elmalign));
3152 : }
3153 :
8742 bruce 3154 : /*
3155 : * array_map()
3156 : *
3157 : * Map an array through an arbitrary expression. Return a new array with
3158 : * the same dimensions and each source element transformed by the given,
3159 : * already-compiled expression. Each source element is placed in the
3160 : * innermost_caseval/innermost_casenull fields of the ExprState.
3161 : *
3162 : * Parameters are:
3163 : * * arrayd: Datum representing array argument.
3164 : * * exprstate: ExprState representing the per-element transformation.
3165 : * * econtext: context for expression evaluation.
3166 : * * retType: OID of element type of output array. This must be the same as,
2017 tgl 3167 : * or binary-compatible with, the result type of the expression. It might
3168 : * be different from the input array's element type.
3169 : * * amstate: workspace for array_map. Must be zeroed by caller before
3170 : * first call, and not touched after that.
6590 3171 : *
3172 : * It is legitimate to pass a freshly-zeroed ArrayMapState on each call,
3173 : * but better performance can be had if the state can be preserved across
3174 : * a series of calls.
3175 : *
3176 : * NB: caller must assure that input array is not NULL. NULL elements in
3177 : * the array are OK however.
3178 : * NB: caller should be running in econtext's per-tuple memory context.
3179 : */
3180 : Datum
2017 tgl 3181 GIC 204 : array_map(Datum arrayd,
3182 : ExprState *exprstate, ExprContext *econtext,
3183 : Oid retType, ArrayMapState *amstate)
3184 : {
3185 204 : AnyArrayType *v = DatumGetAnyArrayP(arrayd);
3186 : ArrayType *result;
3187 : Datum *values;
3188 : bool *nulls;
3189 : int *dim;
3190 : int ndim;
3191 : int nitems;
3192 : int i;
6352 3193 204 : int32 nbytes = 0;
3194 : int32 dataoffset;
3195 : bool hasnulls;
3196 : Oid inpType;
3197 : int inp_typlen;
3198 : bool inp_typbyval;
3199 : char inp_typalign;
3200 : int typlen;
3201 : bool typbyval;
3202 : char typalign;
3203 : array_iter iter;
3204 : ArrayMetaState *inp_extra;
7226 tgl 3205 ECB : ArrayMetaState *ret_extra;
2017 tgl 3206 GIC 204 : Datum *transform_source = exprstate->innermost_caseval;
3207 204 : bool *transform_source_isnull = exprstate->innermost_casenull;
3208 :
2887 tgl 3209 CBC 204 : inpType = AARR_ELEMTYPE(v);
2887 tgl 3210 GIC 204 : ndim = AARR_NDIM(v);
3211 204 : dim = AARR_DIMS(v);
8296 3212 204 : nitems = ArrayGetNItems(ndim, dim);
3213 :
3214 : /* Check for empty array */
8720 bruce 3215 204 : if (nitems <= 0)
3216 : {
6687 tgl 3217 ECB : /* Return empty array */
2017 tgl 3218 GIC 6 : return PointerGetDatum(construct_empty_array(retType));
3219 : }
3220 :
3221 : /*
3222 : * We arrange to look up info about input and return element types only
3223 : * once per series of calls, assuming the element type doesn't change
3224 : * underneath us.
3225 : */
6590 3226 198 : inp_extra = &amstate->inp_extra;
3227 198 : ret_extra = &amstate->ret_extra;
3228 :
7226 3229 198 : if (inp_extra->element_type != inpType)
7226 tgl 3230 ECB : {
7226 tgl 3231 CBC 198 : get_typlenbyvalalign(inpType,
3232 : &inp_extra->typlen,
7226 tgl 3233 ECB : &inp_extra->typbyval,
3234 : &inp_extra->typalign);
7226 tgl 3235 CBC 198 : inp_extra->element_type = inpType;
7226 tgl 3236 ECB : }
7226 tgl 3237 GIC 198 : inp_typlen = inp_extra->typlen;
3238 198 : inp_typbyval = inp_extra->typbyval;
7226 tgl 3239 CBC 198 : inp_typalign = inp_extra->typalign;
3240 :
7226 tgl 3241 GIC 198 : if (ret_extra->element_type != retType)
7226 tgl 3242 ECB : {
7226 tgl 3243 GIC 198 : get_typlenbyvalalign(retType,
3244 : &ret_extra->typlen,
3245 : &ret_extra->typbyval,
3246 : &ret_extra->typalign);
3247 198 : ret_extra->element_type = retType;
3248 : }
3249 198 : typlen = ret_extra->typlen;
7226 tgl 3250 CBC 198 : typbyval = ret_extra->typbyval;
3251 198 : typalign = ret_extra->typalign;
3252 :
6352 tgl 3253 ECB : /* Allocate temporary arrays for new values */
8301 tgl 3254 GIC 198 : values = (Datum *) palloc(nitems * sizeof(Datum));
6352 tgl 3255 CBC 198 : nulls = (bool *) palloc(nitems * sizeof(bool));
3256 :
3257 : /* Loop over source data */
2887 tgl 3258 GIC 198 : array_iter_setup(&iter, v);
6352 tgl 3259 CBC 198 : hasnulls = false;
3260 :
8720 bruce 3261 1574 : for (i = 0; i < nitems; i++)
8720 bruce 3262 ECB : {
6352 tgl 3263 : /* Get source element, checking for NULL */
2017 tgl 3264 GIC 1392 : *transform_source =
2017 tgl 3265 CBC 1392 : array_iter_next(&iter, transform_source_isnull, i,
3266 : inp_typlen, inp_typbyval, inp_typalign);
8742 bruce 3267 ECB :
3268 : /* Apply the given expression to source element */
2017 tgl 3269 GIC 1392 : values[i] = ExecEvalExpr(exprstate, econtext, &nulls[i]);
3270 :
2017 tgl 3271 CBC 1376 : if (nulls[i])
6352 tgl 3272 GIC 6 : hasnulls = true;
6352 tgl 3273 ECB : else
3274 : {
3275 : /* Ensure data is not toasted */
6352 tgl 3276 GIC 1370 : if (typlen == -1)
3277 189 : values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
6352 tgl 3278 ECB : /* Update total result size */
5847 tgl 3279 CBC 1370 : nbytes = att_addlength_datum(nbytes, typlen, values[i]);
5847 tgl 3280 GIC 1370 : nbytes = att_align_nominal(nbytes, typalign);
3281 : /* check for overflow of total request */
6352 tgl 3282 CBC 1370 : if (!AllocSizeIsValid(nbytes))
6352 tgl 3283 LBC 0 : ereport(ERROR,
3284 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
6352 tgl 3285 ECB : errmsg("array size exceeds the maximum allowed (%d)",
3286 : (int) MaxAllocSize)));
3287 : }
8742 bruce 3288 : }
3289 :
3290 : /* Allocate and fill the result array */
6352 tgl 3291 GIC 182 : if (hasnulls)
3292 : {
6352 tgl 3293 CBC 3 : dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
6352 tgl 3294 GIC 3 : nbytes += dataoffset;
6352 tgl 3295 ECB : }
3296 : else
3297 : {
6352 tgl 3298 GIC 179 : dataoffset = 0; /* marker for no null bitmap */
3299 179 : nbytes += ARR_OVERHEAD_NONULLS(ndim);
6352 tgl 3300 ECB : }
4365 tgl 3301 CBC 182 : result = (ArrayType *) palloc0(nbytes);
5885 tgl 3302 GIC 182 : SET_VARSIZE(result, nbytes);
8301 tgl 3303 CBC 182 : result->ndim = ndim;
6352 3304 182 : result->dataoffset = dataoffset;
7531 tgl 3305 GIC 182 : result->elemtype = retType;
2887 tgl 3306 CBC 182 : memcpy(ARR_DIMS(result), AARR_DIMS(v), ndim * sizeof(int));
2887 tgl 3307 GBC 182 : memcpy(ARR_LBOUND(result), AARR_LBOUND(v), ndim * sizeof(int));
3308 :
6352 tgl 3309 GIC 182 : CopyArrayEls(result,
3310 : values, nulls, nitems,
3311 : typlen, typbyval, typalign,
3312 : false);
3313 :
3314 : /*
2017 tgl 3315 ECB : * Note: do not risk trying to pfree the results of the called expression
3316 : */
8301 tgl 3317 CBC 182 : pfree(values);
6352 3318 182 : pfree(nulls);
3319 :
2017 tgl 3320 GIC 182 : return PointerGetDatum(result);
3321 : }
8742 bruce 3322 ECB :
6352 tgl 3323 : /*
3324 : * construct_array --- simple method for constructing an array object
8301 3325 : *
3326 : * elems: array of Datum items to become the array contents
6352 3327 : * (NULL element values are not supported).
8301 3328 : * nelems: number of items
7531 3329 : * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
8301 3330 : *
3331 : * A palloc'd 1-D array object is constructed and returned. Note that
3332 : * elem values will be copied into the object even if pass-by-ref type.
2022 3333 : * Also note the result will be 0-D not 1-D if nelems = 0.
3334 : *
3335 : * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
3336 : * from the system catalogs, given the elmtype. However, the caller is
3337 : * in a better position to cache this info across multiple uses, or even
3338 : * to hard-wire values if the element type is hard-wired.
3339 : */
3340 : ArrayType *
8301 tgl 3341 CBC 454236 : construct_array(Datum *elems, int nelems,
7531 tgl 3342 ECB : Oid elmtype,
3343 : int elmlen, bool elmbyval, char elmalign)
7306 3344 : {
3345 : int dims[1];
3346 : int lbs[1];
3347 :
7306 tgl 3348 GIC 454236 : dims[0] = nelems;
3349 454236 : lbs[0] = 1;
3350 :
6352 3351 454236 : return construct_md_array(elems, NULL, 1, dims, lbs,
3352 : elmtype, elmlen, elmbyval, elmalign);
3353 : }
3354 :
3355 : /*
3356 : * Like construct_array(), where elmtype must be a built-in type, and
3357 : * elmlen/elmbyval/elmalign is looked up from hardcoded data. This is often
3358 : * useful when manipulating arrays from/for system catalogs.
3359 : */
3360 : ArrayType *
282 peter 3361 GNC 299802 : construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
3362 : {
3363 : int elmlen;
3364 : bool elmbyval;
3365 : char elmalign;
3366 :
3367 299802 : switch (elmtype)
3368 : {
3369 4127 : case CHAROID:
3370 : case BOOLOID:
3371 4127 : elmlen = 1;
3372 4127 : elmbyval = true;
3373 4127 : elmalign = TYPALIGN_CHAR;
3374 4127 : break;
3375 :
3376 4378 : case CSTRINGOID:
3377 4378 : elmlen = -2;
3378 4378 : elmbyval = false;
3379 4378 : elmalign = TYPALIGN_CHAR;
3380 4378 : break;
3381 :
3382 217951 : case FLOAT4OID:
3383 217951 : elmlen = sizeof(float4);
3384 217951 : elmbyval = true;
3385 217951 : elmalign = TYPALIGN_INT;
3386 217951 : break;
3387 :
3388 45377 : case INT2OID:
3389 45377 : elmlen = sizeof(int16);
3390 45377 : elmbyval = true;
3391 45377 : elmalign = TYPALIGN_SHORT;
3392 45377 : break;
3393 :
3394 3192 : case INT4OID:
3395 3192 : elmlen = sizeof(int32);
3396 3192 : elmbyval = true;
3397 3192 : elmalign = TYPALIGN_INT;
3398 3192 : break;
3399 :
3400 2 : case INT8OID:
3401 2 : elmlen = sizeof(int64);
3402 2 : elmbyval = FLOAT8PASSBYVAL;
3403 2 : elmalign = TYPALIGN_DOUBLE;
3404 2 : break;
3405 :
3406 253 : case NAMEOID:
3407 253 : elmlen = NAMEDATALEN;
3408 253 : elmbyval = false;
3409 253 : elmalign = TYPALIGN_CHAR;
3410 253 : break;
3411 :
3412 9206 : case OIDOID:
3413 : case REGTYPEOID:
3414 9206 : elmlen = sizeof(Oid);
3415 9206 : elmbyval = true;
3416 9206 : elmalign = TYPALIGN_INT;
3417 9206 : break;
3418 :
3419 15295 : case TEXTOID:
3420 15295 : elmlen = -1;
3421 15295 : elmbyval = false;
3422 15295 : elmalign = TYPALIGN_INT;
3423 15295 : break;
3424 :
3425 21 : case TIDOID:
3426 21 : elmlen = sizeof(ItemPointerData);
3427 21 : elmbyval = false;
3428 21 : elmalign = TYPALIGN_SHORT;
3429 21 : break;
3430 :
282 peter 3431 UNC 0 : default:
3432 0 : elog(ERROR, "type %u not supported by construct_array_builtin()", elmtype);
3433 : /* keep compiler quiet */
3434 : elmlen = 0;
3435 : elmbyval = false;
3436 : elmalign = 0;
3437 : }
3438 :
282 peter 3439 GNC 299802 : return construct_array(elems, nelems, elmtype, elmlen, elmbyval, elmalign);
3440 : }
3441 :
3442 : /*
3443 : * construct_md_array --- simple method for constructing an array object
3444 : * with arbitrary dimensions and possible NULLs
3445 : *
3446 : * elems: array of Datum items to become the array contents
3447 : * nulls: array of is-null flags (can be NULL if no nulls)
3448 : * ndims: number of dimensions
3449 : * dims: integer array with size of each dimension
3450 : * lbs: integer array with lower bound of each dimension
3451 : * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
7306 tgl 3452 ECB : *
3453 : * A palloc'd ndims-D array object is constructed and returned. Note that
3454 : * elem values will be copied into the object even if pass-by-ref type.
3455 : * Also note the result will be 0-D not ndims-D if any dims[i] = 0.
3456 : *
3457 : * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
3458 : * from the system catalogs, given the elmtype. However, the caller is
3459 : * in a better position to cache this info across multiple uses, or even
3460 : * to hard-wire values if the element type is hard-wired.
3461 : */
3462 : ArrayType *
7306 tgl 3463 GIC 1001805 : construct_md_array(Datum *elems,
3464 : bool *nulls,
3465 : int ndims,
3466 : int *dims,
3467 : int *lbs,
3468 : Oid elmtype, int elmlen, bool elmbyval, char elmalign)
3469 : {
3470 : ArrayType *result;
3471 : bool hasnulls;
6352 tgl 3472 ECB : int32 nbytes;
3473 : int32 dataoffset;
3474 : int i;
3475 : int nelems;
3476 :
7196 tgl 3477 GIC 1001805 : if (ndims < 0) /* we do allow zero-dimension arrays */
7196 tgl 3478 LBC 0 : ereport(ERROR,
3479 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
7196 tgl 3480 ECB : errmsg("invalid number of dimensions: %d", ndims)));
7196 tgl 3481 GIC 1001805 : if (ndims > MAXDIM)
7196 tgl 3482 LBC 0 : ereport(ERROR,
7196 tgl 3483 ECB : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7136 peter_e 3484 : errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
3485 : ndims, MAXDIM)));
3486 :
699 tgl 3487 : /* This checks for overflow of the array dimensions */
7306 tgl 3488 CBC 1001805 : nelems = ArrayGetNItems(ndims, dims);
699 3489 1001805 : ArrayCheckBounds(ndims, dims, lbs);
8301 tgl 3490 ECB :
2022 3491 : /* if ndims <= 0 or any dims[i] == 0, return empty array */
2022 tgl 3492 GIC 1001805 : if (nelems <= 0)
2022 tgl 3493 CBC 21964 : return construct_empty_array(elmtype);
2022 tgl 3494 ECB :
7531 3495 : /* compute required space */
6352 tgl 3496 CBC 979841 : nbytes = 0;
3497 979841 : hasnulls = false;
6352 tgl 3498 GIC 9120827 : for (i = 0; i < nelems; i++)
8301 tgl 3499 ECB : {
6352 tgl 3500 CBC 8140986 : if (nulls && nulls[i])
8291 tgl 3501 ECB : {
6352 tgl 3502 CBC 16331 : hasnulls = true;
3503 16331 : continue;
3504 : }
6352 tgl 3505 ECB : /* make sure data is not toasted */
6352 tgl 3506 CBC 8124655 : if (elmlen == -1)
3507 1910822 : elems[i] = PointerGetDatum(PG_DETOAST_DATUM(elems[i]));
5847 3508 8124655 : nbytes = att_addlength_datum(nbytes, elmlen, elems[i]);
3509 8124655 : nbytes = att_align_nominal(nbytes, elmalign);
3510 : /* check for overflow of total request */
6352 3511 8124655 : if (!AllocSizeIsValid(nbytes))
6352 tgl 3512 LBC 0 : ereport(ERROR,
6352 tgl 3513 ECB : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3514 : errmsg("array size exceeds the maximum allowed (%d)",
3515 : (int) MaxAllocSize)));
3516 : }
8301 3517 :
6352 3518 : /* Allocate and initialize result array */
6352 tgl 3519 CBC 979841 : if (hasnulls)
6352 tgl 3520 ECB : {
6352 tgl 3521 CBC 8720 : dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nelems);
6352 tgl 3522 GIC 8720 : nbytes += dataoffset;
6352 tgl 3523 ECB : }
3524 : else
3525 : {
6352 tgl 3526 CBC 971121 : dataoffset = 0; /* marker for no null bitmap */
3527 971121 : nbytes += ARR_OVERHEAD_NONULLS(ndims);
6352 tgl 3528 ECB : }
4365 tgl 3529 GIC 979841 : result = (ArrayType *) palloc0(nbytes);
5885 tgl 3530 CBC 979841 : SET_VARSIZE(result, nbytes);
7306 3531 979841 : result->ndim = ndims;
6352 3532 979841 : result->dataoffset = dataoffset;
7531 3533 979841 : result->elemtype = elmtype;
7032 neilc 3534 979841 : memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
7032 neilc 3535 GIC 979841 : memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
8301 tgl 3536 ECB :
6352 tgl 3537 CBC 979841 : CopyArrayEls(result,
6352 tgl 3538 ECB : elems, nulls, nelems,
3539 : elmlen, elmbyval, elmalign,
3540 : false);
3541 :
6352 tgl 3542 GBC 979841 : return result;
6352 tgl 3543 EUB : }
3544 :
3545 : /*
3546 : * construct_empty_array --- make a zero-dimensional array of given type
3547 : */
3548 : ArrayType *
6352 tgl 3549 GIC 1124837 : construct_empty_array(Oid elmtype)
6352 tgl 3550 ECB : {
3551 : ArrayType *result;
3552 :
4365 tgl 3553 GIC 1124837 : result = (ArrayType *) palloc0(sizeof(ArrayType));
5885 3554 1124837 : SET_VARSIZE(result, sizeof(ArrayType));
6352 3555 1124837 : result->ndim = 0;
3556 1124837 : result->dataoffset = 0;
3557 1124837 : result->elemtype = elmtype;
8301 3558 1124837 : return result;
3559 : }
3560 :
3561 : /*
3562 : * construct_empty_expanded_array: make an empty expanded array
3563 : * given only type information. (metacache can be NULL if not needed.)
3564 : */
3565 : ExpandedArrayHeader *
2887 3566 12 : construct_empty_expanded_array(Oid element_type,
3567 : MemoryContext parentcontext,
3568 : ArrayMetaState *metacache)
3569 : {
3570 12 : ArrayType *array = construct_empty_array(element_type);
3571 : Datum d;
3572 :
3573 12 : d = expand_array(PointerGetDatum(array), parentcontext, metacache);
2887 tgl 3574 CBC 12 : pfree(array);
2887 tgl 3575 GIC 12 : return (ExpandedArrayHeader *) DatumGetEOHP(d);
3576 : }
3577 :
3578 : /*
3579 : * deconstruct_array --- simple method for extracting data from an array
3580 : *
3581 : * array: array object to examine (must not be NULL)
3582 : * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
3583 : * elemsp: return value, set to point to palloc'd array of Datum values
3584 : * nullsp: return value, set to point to palloc'd array of isnull markers
3585 : * nelemsp: return value, set to number of extracted values
3586 : *
3587 : * The caller may pass nullsp == NULL if it does not support NULLs in the
6352 tgl 3588 ECB : * array. Note that this produces a very uninformative error message,
6352 tgl 3589 EUB : * so do it only in cases where a NULL is really not expected.
3590 : *
3591 : * If array elements are pass-by-ref data type, the returned Datums will
8301 tgl 3592 ECB : * be pointers into the array object.
7531 tgl 3593 EUB : *
3594 : * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
3595 : * from the system catalogs, given the elmtype. However, the caller is
3596 : * in a better position to cache this info across multiple uses, or even
3597 : * to hard-wire values if the element type is hard-wired.
3598 : */
8301 tgl 3599 ECB : void
8301 tgl 3600 CBC 1538592 : deconstruct_array(ArrayType *array,
3601 : Oid elmtype,
3602 : int elmlen, bool elmbyval, char elmalign,
6352 tgl 3603 ECB : Datum **elemsp, bool **nullsp, int *nelemsp)
8301 3604 : {
3605 : Datum *elems;
3606 : bool *nulls;
3607 : int nelems;
3608 : char *p;
6352 3609 : bits8 *bitmap;
3610 : int bitmask;
8301 3611 : int i;
3612 :
7531 tgl 3613 CBC 1538592 : Assert(ARR_ELEMTYPE(array) == elmtype);
7531 tgl 3614 ECB :
8296 tgl 3615 GIC 1538592 : nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
8301 3616 1538592 : *elemsp = elems = (Datum *) palloc(nelems * sizeof(Datum));
6352 tgl 3617 CBC 1538592 : if (nullsp)
4474 3618 963361 : *nullsp = nulls = (bool *) palloc0(nelems * sizeof(bool));
6352 tgl 3619 ECB : else
6352 tgl 3620 CBC 575231 : nulls = NULL;
8301 tgl 3621 GIC 1538592 : *nelemsp = nelems;
8301 tgl 3622 ECB :
8301 tgl 3623 GBC 1538592 : p = ARR_DATA_PTR(array);
6352 tgl 3624 GIC 1538592 : bitmap = ARR_NULLBITMAP(array);
3625 1538592 : bitmask = 1;
3626 :
8301 3627 23080840 : for (i = 0; i < nelems; i++)
3628 : {
3629 : /* Get source element, checking for NULL */
6352 tgl 3630 CBC 21542248 : if (bitmap && (*bitmap & bitmask) == 0)
3631 : {
3632 1583 : elems[i] = (Datum) 0;
3633 1583 : if (nulls)
6352 tgl 3634 GIC 1583 : nulls[i] = true;
3635 : else
6352 tgl 3636 UIC 0 : ereport(ERROR,
6352 tgl 3637 ECB : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
2118 3638 : errmsg("null array element not allowed in this context")));
3639 : }
6352 3640 : else
3641 : {
6352 tgl 3642 CBC 21540665 : elems[i] = fetch_att(p, elmbyval, elmlen);
5847 3643 21540665 : p = att_addlength_pointer(p, elmlen, p);
3644 21540665 : p = (char *) att_align_nominal(p, elmalign);
6352 tgl 3645 ECB : }
3646 :
3647 : /* advance bitmap pointer if any */
6352 tgl 3648 CBC 21542248 : if (bitmap)
3649 : {
6352 tgl 3650 GIC 2470 : bitmask <<= 1;
3651 2470 : if (bitmask == 0x100)
3652 : {
6352 tgl 3653 CBC 33 : bitmap++;
6352 tgl 3654 GIC 33 : bitmask = 1;
3655 : }
3656 : }
3657 : }
8301 3658 1538592 : }
3659 :
3660 : /*
3661 : * Like deconstruct_array(), where elmtype must be a built-in type, and
3662 : * elmlen/elmbyval/elmalign is looked up from hardcoded data. This is often
3663 : * useful when manipulating arrays from/for system catalogs.
3664 : */
3665 : void
282 peter 3666 GNC 312703 : deconstruct_array_builtin(ArrayType *array,
3667 : Oid elmtype,
3668 : Datum **elemsp, bool **nullsp, int *nelemsp)
3669 : {
3670 : int elmlen;
3671 : bool elmbyval;
3672 : char elmalign;
3673 :
3674 312703 : switch (elmtype)
3675 : {
3676 9 : case CHAROID:
3677 9 : elmlen = 1;
3678 9 : elmbyval = true;
3679 9 : elmalign = TYPALIGN_CHAR;
3680 9 : break;
3681 :
3682 4378 : case CSTRINGOID:
3683 4378 : elmlen = -2;
3684 4378 : elmbyval = false;
3685 4378 : elmalign = TYPALIGN_CHAR;
3686 4378 : break;
3687 :
3688 15 : case FLOAT8OID:
3689 15 : elmlen = sizeof(float8);
3690 15 : elmbyval = FLOAT8PASSBYVAL;
3691 15 : elmalign = TYPALIGN_DOUBLE;
3692 15 : break;
3693 :
3694 1874 : case INT2OID:
3695 1874 : elmlen = sizeof(int16);
3696 1874 : elmbyval = true;
3697 1874 : elmalign = TYPALIGN_SHORT;
3698 1874 : break;
3699 :
3700 112 : case OIDOID:
3701 112 : elmlen = sizeof(Oid);
3702 112 : elmbyval = true;
3703 112 : elmalign = TYPALIGN_INT;
3704 112 : break;
3705 :
3706 306303 : case TEXTOID:
3707 306303 : elmlen = -1;
3708 306303 : elmbyval = false;
3709 306303 : elmalign = TYPALIGN_INT;
3710 306303 : break;
3711 :
3712 12 : case TIDOID:
3713 12 : elmlen = sizeof(ItemPointerData);
3714 12 : elmbyval = false;
3715 12 : elmalign = TYPALIGN_SHORT;
3716 12 : break;
3717 :
282 peter 3718 UNC 0 : default:
3719 0 : elog(ERROR, "type %u not supported by deconstruct_array_builtin()", elmtype);
3720 : /* keep compiler quiet */
3721 : elmlen = 0;
3722 : elmbyval = false;
3723 : elmalign = 0;
3724 : }
3725 :
282 peter 3726 GNC 312703 : deconstruct_array(array, elmtype, elmlen, elmbyval, elmalign, elemsp, nullsp, nelemsp);
3727 312703 : }
3728 :
4474 tgl 3729 ECB : /*
3730 : * array_contains_nulls --- detect whether an array has any null elements
3731 : *
3732 : * This gives an accurate answer, whereas testing ARR_HASNULL only tells
3733 : * if the array *might* contain a null.
3734 : */
3735 : bool
4474 tgl 3736 CBC 40230 : array_contains_nulls(ArrayType *array)
4474 tgl 3737 ECB : {
3738 : int nelems;
3739 : bits8 *bitmap;
3740 : int bitmask;
3741 :
3742 : /* Easy answer if there's no null bitmap */
4474 tgl 3743 GIC 40230 : if (!ARR_HASNULL(array))
3744 40205 : return false;
3745 :
4474 tgl 3746 CBC 25 : nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
3747 :
4474 tgl 3748 GIC 25 : bitmap = ARR_NULLBITMAP(array);
3749 :
4474 tgl 3750 ECB : /* check whole bytes of the bitmap byte-at-a-time */
4474 tgl 3751 GIC 25 : while (nelems >= 8)
3752 : {
4474 tgl 3753 CBC 6 : if (*bitmap != 0xFF)
3754 6 : return true;
4474 tgl 3755 LBC 0 : bitmap++;
4474 tgl 3756 UIC 0 : nelems -= 8;
3757 : }
3758 :
3759 : /* check last partial byte */
4474 tgl 3760 GIC 19 : bitmask = 1;
3761 43 : while (nelems > 0)
3762 : {
3763 43 : if ((*bitmap & bitmask) == 0)
3764 19 : return true;
3765 24 : bitmask <<= 1;
3766 24 : nelems--;
3767 : }
3768 :
4474 tgl 3769 UIC 0 : return false;
3770 : }
3771 :
3772 :
3773 : /*
3774 : * array_eq :
3775 : * compares two arrays for equality
3776 : * result :
3777 : * returns true if the arrays are equal, false otherwise.
3778 : *
3779 : * Note: we do not use array_cmp here, since equality may be meaningful in
7175 tgl 3780 ECB : * datatypes that don't have a total ordering (and hence no btree support).
3781 : */
3782 : Datum
8335 tgl 3783 GIC 265403 : array_eq(PG_FUNCTION_ARGS)
3784 : {
1534 andres 3785 265403 : LOCAL_FCINFO(locfcinfo, 2);
2029 tgl 3786 265403 : AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
3787 265403 : AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
4380 3788 265403 : Oid collation = PG_GET_COLLATION();
2887 3789 265403 : int ndims1 = AARR_NDIM(array1);
3790 265403 : int ndims2 = AARR_NDIM(array2);
3791 265403 : int *dims1 = AARR_DIMS(array1);
3792 265403 : int *dims2 = AARR_DIMS(array2);
2887 tgl 3793 CBC 265403 : int *lbs1 = AARR_LBOUND(array1);
2887 tgl 3794 GIC 265403 : int *lbs2 = AARR_LBOUND(array2);
2887 tgl 3795 CBC 265403 : Oid element_type = AARR_ELEMTYPE(array1);
8296 3796 265403 : bool result = true;
6350 tgl 3797 ECB : int nitems;
7175 3798 : TypeCacheEntry *typentry;
3799 : int typlen;
7226 3800 : bool typbyval;
3801 : char typalign;
3802 : array_iter it1;
2887 3803 : array_iter it2;
7226 3804 : int i;
8296 3805 :
2887 tgl 3806 GIC 265403 : if (element_type != AARR_ELEMTYPE(array2))
7196 tgl 3807 LBC 0 : ereport(ERROR,
3808 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3809 : errmsg("cannot compare arrays of different element types")));
7226 tgl 3810 ECB :
3811 : /* fast path if the arrays do not have the same dimensionality */
6350 tgl 3812 CBC 265403 : if (ndims1 != ndims2 ||
2887 3813 264761 : memcmp(dims1, dims2, ndims1 * sizeof(int)) != 0 ||
3814 206420 : memcmp(lbs1, lbs2, ndims1 * sizeof(int)) != 0)
8296 tgl 3815 GIC 58983 : result = false;
7226 tgl 3816 EUB : else
3817 : {
3818 : /*
3819 : * We arrange to look up the equality function only once per series of
3820 : * calls, assuming the element type doesn't change underneath us. The
3821 : * typcache is used so that we have no memory leakage when being used
6385 bruce 3822 ECB : * as an index support function.
7226 tgl 3823 : */
7175 tgl 3824 CBC 206420 : typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
7175 tgl 3825 GIC 206420 : if (typentry == NULL ||
3826 204140 : typentry->type_id != element_type)
3827 : {
7175 tgl 3828 CBC 2280 : typentry = lookup_type_cache(element_type,
3829 : TYPECACHE_EQ_OPR_FINFO);
3830 2280 : if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
7175 tgl 3831 LBC 0 : ereport(ERROR,
3832 : (errcode(ERRCODE_UNDEFINED_FUNCTION),
2118 tgl 3833 ECB : errmsg("could not identify an equality operator for type %s",
3834 : format_type_be(element_type))));
7175 tgl 3835 GIC 2280 : fcinfo->flinfo->fn_extra = (void *) typentry;
3836 : }
3837 206420 : typlen = typentry->typlen;
7175 tgl 3838 CBC 206420 : typbyval = typentry->typbyval;
7175 tgl 3839 GIC 206420 : typalign = typentry->typalign;
3840 :
3841 : /*
3842 : * apply the operator to each pair of array elements.
3843 : */
1534 andres 3844 206420 : InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
3845 : collation, NULL, NULL);
7226 tgl 3846 ECB :
3847 : /* Loop over source data */
6350 tgl 3848 GIC 206420 : nitems = ArrayGetNItems(ndims1, dims1);
2887 3849 206420 : array_iter_setup(&it1, array1);
3850 206420 : array_iter_setup(&it2, array2);
3851 :
6350 3852 504051 : for (i = 0; i < nitems; i++)
3853 : {
7188 bruce 3854 ECB : Datum elt1;
3855 : Datum elt2;
6352 tgl 3856 : bool isnull1;
3857 : bool isnull2;
7188 bruce 3858 : bool oprresult;
7226 tgl 3859 :
6352 3860 : /* Get elements, checking for NULL */
2887 tgl 3861 GIC 328284 : elt1 = array_iter_next(&it1, &isnull1, i,
2887 tgl 3862 ECB : typlen, typbyval, typalign);
2887 tgl 3863 CBC 328284 : elt2 = array_iter_next(&it2, &isnull2, i,
2887 tgl 3864 ECB : typlen, typbyval, typalign);
7226 3865 :
6352 3866 : /*
3867 : * We consider two NULLs equal; NULL and not-NULL are unequal.
3868 : */
6352 tgl 3869 CBC 328284 : if (isnull1 && isnull2)
3870 11 : continue;
3871 328273 : if (isnull1 || isnull2)
6352 tgl 3872 ECB : {
6352 tgl 3873 GIC 54 : result = false;
6352 tgl 3874 CBC 30653 : break;
6352 tgl 3875 ECB : }
7226 3876 :
3877 : /*
1083 3878 : * Apply the operator to the element pair; treat NULL as false
3879 : */
1534 andres 3880 CBC 328219 : locfcinfo->args[0].value = elt1;
3881 328219 : locfcinfo->args[0].isnull = false;
3882 328219 : locfcinfo->args[1].value = elt2;
3883 328219 : locfcinfo->args[1].isnull = false;
3884 328219 : locfcinfo->isnull = false;
1534 andres 3885 GIC 328219 : oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
1083 tgl 3886 CBC 328219 : if (locfcinfo->isnull || !oprresult)
7226 tgl 3887 ECB : {
7226 tgl 3888 CBC 30599 : result = false;
3889 30599 : break;
7226 tgl 3890 ECB : }
3891 : }
3892 : }
8335 3893 :
8296 3894 : /* Avoid leaking memory when handed toasted input. */
2887 tgl 3895 CBC 265403 : AARR_FREE_IF_COPY(array1, 0);
3896 265403 : AARR_FREE_IF_COPY(array2, 1);
3897 :
8296 tgl 3898 GBC 265403 : PG_RETURN_BOOL(result);
9770 scrappy 3899 EUB : }
3900 :
3901 :
3902 : /*-----------------------------------------------------------------------------
3903 : * array-array bool operators:
3904 : * Given two arrays, iterate comparison operators
3905 : * over the array. Uses logic similar to text comparison
7226 tgl 3906 ECB : * functions, except element-by-element instead of
3907 : * character-by-character.
3908 : *----------------------------------------------------------------------------
3909 : */
3910 :
3911 : Datum
7226 tgl 3912 GIC 450 : array_ne(PG_FUNCTION_ARGS)
3913 : {
3914 450 : PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo)));
3915 : }
7229 bruce 3916 ECB :
3917 : Datum
7226 tgl 3918 GIC 2153 : array_lt(PG_FUNCTION_ARGS)
3919 : {
3920 2153 : PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
3921 : }
3922 :
7226 tgl 3923 ECB : Datum
7226 tgl 3924 CBC 9 : array_gt(PG_FUNCTION_ARGS)
3925 : {
3926 9 : PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
3927 : }
7226 tgl 3928 ECB :
3929 : Datum
7226 tgl 3930 GIC 9 : array_le(PG_FUNCTION_ARGS)
7226 tgl 3931 ECB : {
7226 tgl 3932 GIC 9 : PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
7226 tgl 3933 ECB : }
3934 :
7226 tgl 3935 EUB : Datum
7226 tgl 3936 GBC 9 : array_ge(PG_FUNCTION_ARGS)
3937 : {
7226 tgl 3938 GIC 9 : PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
3939 : }
7226 tgl 3940 ECB :
3941 : Datum
7226 tgl 3942 GIC 5590323 : btarraycmp(PG_FUNCTION_ARGS)
7226 tgl 3943 ECB : {
7226 tgl 3944 CBC 5590323 : PG_RETURN_INT32(array_cmp(fcinfo));
7228 bruce 3945 ECB : }
3946 :
3947 : /*
3948 : * array_cmp()
7226 tgl 3949 EUB : * Internal comparison function for arrays.
3950 : *
3951 : * Returns -1, 0 or 1
3952 : */
3953 : static int
7226 tgl 3954 GIC 5592776 : array_cmp(FunctionCallInfo fcinfo)
3955 : {
1534 andres 3956 5592776 : LOCAL_FCINFO(locfcinfo, 2);
2029 tgl 3957 5592776 : AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
3958 5592776 : AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
4443 peter_e 3959 5592776 : Oid collation = PG_GET_COLLATION();
2887 tgl 3960 5592776 : int ndims1 = AARR_NDIM(array1);
3961 5592776 : int ndims2 = AARR_NDIM(array2);
3962 5592776 : int *dims1 = AARR_DIMS(array1);
2887 tgl 3963 CBC 5592776 : int *dims2 = AARR_DIMS(array2);
7177 tgl 3964 GIC 5592776 : int nitems1 = ArrayGetNItems(ndims1, dims1);
7177 tgl 3965 CBC 5592776 : int nitems2 = ArrayGetNItems(ndims2, dims2);
2887 3966 5592776 : Oid element_type = AARR_ELEMTYPE(array1);
7226 3967 5592776 : int result = 0;
7175 tgl 3968 ECB : TypeCacheEntry *typentry;
7226 3969 : int typlen;
3970 : bool typbyval;
3971 : char typalign;
7177 3972 : int min_nitems;
2887 3973 : array_iter it1;
3974 : array_iter it2;
7226 3975 : int i;
3976 :
2887 tgl 3977 GIC 5592776 : if (element_type != AARR_ELEMTYPE(array2))
7196 3978 3 : ereport(ERROR,
3979 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3980 : errmsg("cannot compare arrays of different element types")));
3981 :
3982 : /*
3983 : * We arrange to look up the comparison function only once per series of
3984 : * calls, assuming the element type doesn't change underneath us. The
3985 : * typcache is used so that we have no memory leakage when being used as
6385 bruce 3986 ECB : * an index support function.
7226 tgl 3987 EUB : */
7175 tgl 3988 GIC 5592773 : typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3989 5592773 : if (typentry == NULL ||
4380 3990 5589963 : typentry->type_id != element_type)
3991 : {
7175 tgl 3992 CBC 2810 : typentry = lookup_type_cache(element_type,
7175 tgl 3993 ECB : TYPECACHE_CMP_PROC_FINFO);
7175 tgl 3994 CBC 2810 : if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
7175 tgl 3995 LBC 0 : ereport(ERROR,
3996 : (errcode(ERRCODE_UNDEFINED_FUNCTION),
3997 : errmsg("could not identify a comparison function for type %s",
3998 : format_type_be(element_type))));
7175 tgl 3999 GIC 2810 : fcinfo->flinfo->fn_extra = (void *) typentry;
4000 : }
4001 5592773 : typlen = typentry->typlen;
4002 5592773 : typbyval = typentry->typbyval;
4003 5592773 : typalign = typentry->typalign;
7226 tgl 4004 ECB :
7175 4005 : /*
4006 : * apply the operator to each pair of array elements.
4007 : */
1534 andres 4008 CBC 5592773 : InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
4009 : collation, NULL, NULL);
7226 tgl 4010 ECB :
7177 tgl 4011 EUB : /* Loop over source data */
6350 tgl 4012 GIC 5592773 : min_nitems = Min(nitems1, nitems2);
2887 4013 5592773 : array_iter_setup(&it1, array1);
4014 5592773 : array_iter_setup(&it2, array2);
6352 tgl 4015 ECB :
7177 tgl 4016 GIC 12157929 : for (i = 0; i < min_nitems; i++)
7177 tgl 4017 ECB : {
4018 : Datum elt1;
4019 : Datum elt2;
4020 : bool isnull1;
4021 : bool isnull2;
4022 : int32 cmpresult;
4023 :
6352 4024 : /* Get elements, checking for NULL */
2887 tgl 4025 GIC 10080939 : elt1 = array_iter_next(&it1, &isnull1, i, typlen, typbyval, typalign);
4026 10080939 : elt2 = array_iter_next(&it2, &isnull2, i, typlen, typbyval, typalign);
4027 :
6352 tgl 4028 ECB : /*
4029 : * We consider two NULLs equal; NULL > not-NULL.
4030 : */
6352 tgl 4031 GIC 10080939 : if (isnull1 && isnull2)
6352 tgl 4032 CBC 6565156 : continue;
6352 tgl 4033 GIC 10080934 : if (isnull1)
4034 : {
4035 : /* arg1 is greater than arg2 */
4036 80 : result = 1;
4037 3515783 : break;
4038 : }
4039 10080854 : if (isnull2)
4040 : {
6352 tgl 4041 ECB : /* arg1 is less than arg2 */
6352 tgl 4042 GIC 150 : result = -1;
6352 tgl 4043 CBC 150 : break;
4044 : }
4045 :
4046 : /* Compare the pair of elements */
1534 andres 4047 GIC 10080704 : locfcinfo->args[0].value = elt1;
4048 10080704 : locfcinfo->args[0].isnull = false;
1534 andres 4049 CBC 10080704 : locfcinfo->args[1].value = elt2;
4050 10080704 : locfcinfo->args[1].isnull = false;
4051 10080704 : cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
4052 :
1083 tgl 4053 ECB : /* We don't expect comparison support functions to return null */
1083 tgl 4054 CBC 10080704 : Assert(!locfcinfo->isnull);
4055 :
7175 tgl 4056 GIC 10080704 : if (cmpresult == 0)
4057 6565151 : continue; /* equal */
4058 :
4059 3515553 : if (cmpresult < 0)
7226 tgl 4060 ECB : {
7177 4061 : /* arg1 is less than arg2 */
7177 tgl 4062 CBC 1910452 : result = -1;
4063 1910452 : break;
7177 tgl 4064 ECB : }
4065 : else
4066 : {
4067 : /* arg1 is greater than arg2 */
7177 tgl 4068 CBC 1605101 : result = 1;
4069 1605101 : break;
4070 : }
4071 : }
4072 :
4073 : /*
4074 : * If arrays contain same data (up to end of shorter one), apply
3260 bruce 4075 ECB : * additional rules to sort by dimensionality. The relative significance
6347 4076 : * of the different bits of information is historical; mainly we just care
4077 : * that we don't say "equal" for arrays of different dimensionality.
6350 tgl 4078 : */
6350 tgl 4079 GIC 5592773 : if (result == 0)
4080 : {
4081 2076990 : if (nitems1 != nitems2)
4082 295873 : result = (nitems1 < nitems2) ? -1 : 1;
4083 1781117 : else if (ndims1 != ndims2)
6350 tgl 4084 UIC 0 : result = (ndims1 < ndims2) ? -1 : 1;
4085 : else
4086 : {
2887 tgl 4087 GIC 3562213 : for (i = 0; i < ndims1; i++)
4088 : {
6350 4089 1781096 : if (dims1[i] != dims2[i])
4090 : {
6350 tgl 4091 UIC 0 : result = (dims1[i] < dims2[i]) ? -1 : 1;
6350 tgl 4092 LBC 0 : break;
4093 : }
6350 tgl 4094 ECB : }
2887 tgl 4095 GIC 1781117 : if (result == 0)
4096 : {
4097 1781117 : int *lbound1 = AARR_LBOUND(array1);
2887 tgl 4098 CBC 1781117 : int *lbound2 = AARR_LBOUND(array2);
4099 :
4100 3562213 : for (i = 0; i < ndims1; i++)
4101 : {
2887 tgl 4102 GIC 1781096 : if (lbound1[i] != lbound2[i])
4103 : {
2887 tgl 4104 LBC 0 : result = (lbound1[i] < lbound2[i]) ? -1 : 1;
2887 tgl 4105 UIC 0 : break;
2887 tgl 4106 ECB : }
4107 : }
4108 : }
4109 : }
6350 4110 : }
4111 :
7226 4112 : /* Avoid leaking memory when handed toasted input. */
2887 tgl 4113 GIC 5592773 : AARR_FREE_IF_COPY(array1, 0);
4114 5592773 : AARR_FREE_IF_COPY(array2, 1);
4115 :
7226 tgl 4116 CBC 5592773 : return result;
4117 : }
7226 tgl 4118 ECB :
4119 :
4120 : /*-----------------------------------------------------------------------------
4121 : * array hashing
4544 4122 : * Hash the elements and combine the results.
4123 : *----------------------------------------------------------------------------
4124 : */
4125 :
4126 : Datum
4544 tgl 4127 GIC 27242 : hash_array(PG_FUNCTION_ARGS)
4128 : {
1534 andres 4129 27242 : LOCAL_FCINFO(locfcinfo, 1);
2029 tgl 4130 27242 : AnyArrayType *array = PG_GETARG_ANY_ARRAY_P(0);
2887 4131 27242 : int ndims = AARR_NDIM(array);
4132 27242 : int *dims = AARR_DIMS(array);
4133 27242 : Oid element_type = AARR_ELEMTYPE(array);
4339 rhaas 4134 CBC 27242 : uint32 result = 1;
4135 : int nitems;
4544 tgl 4136 ECB : TypeCacheEntry *typentry;
4137 : int typlen;
4138 : bool typbyval;
4139 : char typalign;
4140 : int i;
2887 4141 : array_iter iter;
4544 4142 :
4143 : /*
4144 : * We arrange to look up the hash function only once per series of calls,
4145 : * assuming the element type doesn't change underneath us. The typcache
4146 : * is used so that we have no memory leakage when being used as an index
4147 : * support function.
4148 : */
4544 tgl 4149 GIC 27242 : typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
4150 27242 : if (typentry == NULL ||
4151 27013 : typentry->type_id != element_type)
4152 : {
4153 229 : typentry = lookup_type_cache(element_type,
4154 : TYPECACHE_HASH_PROC_FINFO);
578 peter 4155 229 : if (!OidIsValid(typentry->hash_proc_finfo.fn_oid) && element_type != RECORDOID)
4544 tgl 4156 3 : ereport(ERROR,
4544 tgl 4157 ECB : (errcode(ERRCODE_UNDEFINED_FUNCTION),
4158 : errmsg("could not identify a hash function for type %s",
4159 : format_type_be(element_type))));
4160 :
4161 : /*
4162 : * The type cache doesn't believe that record is hashable (see
4163 : * cache_record_field_properties()), but since we're here, we're
4164 : * committed to hashing, so we can assume it does. Worst case, if any
4165 : * components of the record don't support hashing, we will fail at
4166 : * execution.
4167 : */
578 peter 4168 CBC 226 : if (element_type == RECORDOID)
578 peter 4169 ECB : {
4170 : MemoryContext oldcontext;
4171 : TypeCacheEntry *record_typentry;
4172 :
571 peter 4173 GIC 9 : oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
578 peter 4174 ECB :
578 peter 4175 EUB : /*
4176 : * Make fake type cache entry structure. Note that we can't just
4177 : * modify typentry, since that points directly into the type
4178 : * cache.
578 peter 4179 ECB : */
571 peter 4180 GIC 9 : record_typentry = palloc0(sizeof(*record_typentry));
571 peter 4181 CBC 9 : record_typentry->type_id = element_type;
578 peter 4182 ECB :
4183 : /* fill in what we need below */
578 peter 4184 GIC 9 : record_typentry->typlen = typentry->typlen;
4185 9 : record_typentry->typbyval = typentry->typbyval;
4186 9 : record_typentry->typalign = typentry->typalign;
4187 9 : fmgr_info(F_HASH_RECORD, &record_typentry->hash_proc_finfo);
578 peter 4188 ECB :
578 peter 4189 GIC 9 : MemoryContextSwitchTo(oldcontext);
4190 :
4191 9 : typentry = record_typentry;
578 peter 4192 ECB : }
4193 :
4544 tgl 4194 CBC 226 : fcinfo->flinfo->fn_extra = (void *) typentry;
4195 : }
578 peter 4196 ECB :
4544 tgl 4197 GIC 27239 : typlen = typentry->typlen;
4198 27239 : typbyval = typentry->typbyval;
4199 27239 : typalign = typentry->typalign;
4200 :
4201 : /*
4202 : * apply the hash function to each array element.
4203 : */
1534 andres 4204 27239 : InitFunctionCallInfoData(*locfcinfo, &typentry->hash_proc_finfo, 1,
1479 peter 4205 ECB : PG_GET_COLLATION(), NULL, NULL);
4544 tgl 4206 :
4207 : /* Loop over source data */
4544 tgl 4208 GIC 27239 : nitems = ArrayGetNItems(ndims, dims);
2887 4209 27239 : array_iter_setup(&iter, array);
4210 :
4544 tgl 4211 CBC 72425 : for (i = 0; i < nitems; i++)
4544 tgl 4212 ECB : {
2887 4213 : Datum elt;
4214 : bool isnull;
4215 : uint32 elthash;
4544 4216 :
4217 : /* Get element, checking for NULL */
2887 tgl 4218 GIC 45186 : elt = array_iter_next(&iter, &isnull, i, typlen, typbyval, typalign);
2887 tgl 4219 ECB :
2887 tgl 4220 GIC 45186 : if (isnull)
4221 : {
4544 tgl 4222 ECB : /* Treat nulls as having hashvalue 0 */
4544 tgl 4223 LBC 0 : elthash = 0;
4224 : }
4225 : else
4226 : {
4544 tgl 4227 ECB : /* Apply the hash function */
1534 andres 4228 CBC 45186 : locfcinfo->args[0].value = elt;
4229 45186 : locfcinfo->args[0].isnull = false;
4230 45186 : elthash = DatumGetUInt32(FunctionCallInvoke(locfcinfo));
1083 tgl 4231 ECB : /* We don't expect hash functions to return null */
1083 tgl 4232 GIC 45186 : Assert(!locfcinfo->isnull);
4233 : }
4544 tgl 4234 ECB :
4235 : /*
4339 rhaas 4236 : * Combine hash values of successive elements by multiplying the
4237 : * current value by 31 and adding on the new element's hash value.
4238 : *
4239 : * The result is a sum in which each element's hash value is
4240 : * multiplied by a different power of 31. This is modulo 2^32
4241 : * arithmetic, and the powers of 31 modulo 2^32 form a cyclic group of
4242 : * order 2^27. So for arrays of up to 2^27 elements, each element's
4243 : * hash value is multiplied by a different (odd) number, resulting in
4244 : * a good mixing of all the elements' hash values.
4245 : */
4339 rhaas 4246 GIC 45186 : result = (result << 5) - result + elthash;
4247 : }
4544 tgl 4248 ECB :
4249 : /* Avoid leaking memory when handed toasted input. */
2887 tgl 4250 GIC 27239 : AARR_FREE_IF_COPY(array, 0);
4251 :
4544 4252 27239 : PG_RETURN_UINT32(result);
4253 : }
4254 :
4255 : /*
4256 : * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
4257 : * Otherwise, similar to hash_array.
4258 : */
2047 rhaas 4259 ECB : Datum
2047 rhaas 4260 GIC 72 : hash_array_extended(PG_FUNCTION_ARGS)
2047 rhaas 4261 ECB : {
1534 andres 4262 CBC 72 : LOCAL_FCINFO(locfcinfo, 2);
2029 tgl 4263 72 : AnyArrayType *array = PG_GETARG_ANY_ARRAY_P(0);
2047 rhaas 4264 GBC 72 : uint64 seed = PG_GETARG_INT64(1);
2047 rhaas 4265 GIC 72 : int ndims = AARR_NDIM(array);
4266 72 : int *dims = AARR_DIMS(array);
2047 rhaas 4267 CBC 72 : Oid element_type = AARR_ELEMTYPE(array);
2047 rhaas 4268 GIC 72 : uint64 result = 1;
2047 rhaas 4269 ECB : int nitems;
4270 : TypeCacheEntry *typentry;
2047 rhaas 4271 EUB : int typlen;
4272 : bool typbyval;
4273 : char typalign;
4274 : int i;
2047 rhaas 4275 ECB : array_iter iter;
4276 :
2047 rhaas 4277 CBC 72 : typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
4278 72 : if (typentry == NULL ||
2047 rhaas 4279 GIC 42 : typentry->type_id != element_type)
2047 rhaas 4280 ECB : {
2047 rhaas 4281 GIC 30 : typentry = lookup_type_cache(element_type,
2047 rhaas 4282 ECB : TYPECACHE_HASH_EXTENDED_PROC_FINFO);
2047 rhaas 4283 GIC 30 : if (!OidIsValid(typentry->hash_extended_proc_finfo.fn_oid))
2047 rhaas 4284 GBC 3 : ereport(ERROR,
2047 rhaas 4285 EUB : (errcode(ERRCODE_UNDEFINED_FUNCTION),
4286 : errmsg("could not identify an extended hash function for type %s",
4287 : format_type_be(element_type))));
2047 rhaas 4288 GIC 27 : fcinfo->flinfo->fn_extra = (void *) typentry;
4289 : }
4290 69 : typlen = typentry->typlen;
4291 69 : typbyval = typentry->typbyval;
4292 69 : typalign = typentry->typalign;
2047 rhaas 4293 ECB :
1534 andres 4294 CBC 69 : InitFunctionCallInfoData(*locfcinfo, &typentry->hash_extended_proc_finfo, 2,
4295 : PG_GET_COLLATION(), NULL, NULL);
2047 rhaas 4296 ECB :
4297 : /* Loop over source data */
2047 rhaas 4298 GIC 69 : nitems = ArrayGetNItems(ndims, dims);
4299 69 : array_iter_setup(&iter, array);
4300 :
4301 228 : for (i = 0; i < nitems; i++)
4302 : {
4303 : Datum elt;
4304 : bool isnull;
4305 : uint64 elthash;
4306 :
2047 rhaas 4307 ECB : /* Get element, checking for NULL */
2047 rhaas 4308 GIC 159 : elt = array_iter_next(&iter, &isnull, i, typlen, typbyval, typalign);
2047 rhaas 4309 ECB :
2047 rhaas 4310 CBC 159 : if (isnull)
2047 rhaas 4311 ECB : {
2047 rhaas 4312 LBC 0 : elthash = 0;
2047 rhaas 4313 ECB : }
4314 : else
4315 : {
4316 : /* Apply the hash function */
1534 andres 4317 GIC 159 : locfcinfo->args[0].value = elt;
4318 159 : locfcinfo->args[0].isnull = false;
4319 159 : locfcinfo->args[1].value = Int64GetDatum(seed);
4320 159 : locfcinfo->args[1].isnull = false;
4321 159 : elthash = DatumGetUInt64(FunctionCallInvoke(locfcinfo));
4322 : /* We don't expect hash functions to return null */
1083 tgl 4323 159 : Assert(!locfcinfo->isnull);
4324 : }
4325 :
2047 rhaas 4326 159 : result = (result << 5) - result + elthash;
4327 : }
4328 :
2047 rhaas 4329 CBC 69 : AARR_FREE_IF_COPY(array, 0);
2047 rhaas 4330 ECB :
2047 rhaas 4331 CBC 69 : PG_RETURN_UINT64(result);
4332 : }
2047 rhaas 4333 ECB :
4334 :
6055 tgl 4335 : /*-----------------------------------------------------------------------------
4336 : * array overlap/containment comparisons
4337 : * These use the same methods of comparing array elements as array_eq.
4338 : * We consider only the elements of the arrays, ignoring dimensionality.
4339 : *----------------------------------------------------------------------------
4340 : */
4341 :
4342 : /*
4343 : * array_contain_compare :
4344 : * compares two arrays for overlap/containment
4345 : *
4346 : * When matchall is true, return true if all members of array1 are in array2.
4347 : * When matchall is false, return true if any members of array1 are in array2.
4348 : */
4349 : static bool
2887 tgl 4350 GIC 11226 : array_contain_compare(AnyArrayType *array1, AnyArrayType *array2, Oid collation,
4351 : bool matchall, void **fn_extra)
4352 : {
1534 andres 4353 CBC 11226 : LOCAL_FCINFO(locfcinfo, 2);
6055 tgl 4354 GIC 11226 : bool result = matchall;
2887 4355 11226 : Oid element_type = AARR_ELEMTYPE(array1);
4356 : TypeCacheEntry *typentry;
4357 : int nelems1;
4358 : Datum *values2;
4359 : bool *nulls2;
6055 tgl 4360 ECB : int nelems2;
4361 : int typlen;
4362 : bool typbyval;
4363 : char typalign;
4364 : int i;
4365 : int j;
2887 4366 : array_iter it1;
6055 4367 :
2887 tgl 4368 GIC 11226 : if (element_type != AARR_ELEMTYPE(array2))
6055 tgl 4369 LBC 0 : ereport(ERROR,
4370 : (errcode(ERRCODE_DATATYPE_MISMATCH),
6055 tgl 4371 ECB : errmsg("cannot compare arrays of different element types")));
4372 :
4373 : /*
4374 : * We arrange to look up the equality function only once per series of
4375 : * calls, assuming the element type doesn't change underneath us. The
4376 : * typcache is used so that we have no memory leakage when being used as
6031 bruce 4377 : * an index support function.
6055 tgl 4378 : */
6055 tgl 4379 CBC 11226 : typentry = (TypeCacheEntry *) *fn_extra;
6055 tgl 4380 GIC 11226 : if (typentry == NULL ||
4381 10971 : typentry->type_id != element_type)
4382 : {
4383 255 : typentry = lookup_type_cache(element_type,
6055 tgl 4384 ECB : TYPECACHE_EQ_OPR_FINFO);
6055 tgl 4385 GIC 255 : if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
6055 tgl 4386 UIC 0 : ereport(ERROR,
4387 : (errcode(ERRCODE_UNDEFINED_FUNCTION),
2118 tgl 4388 ECB : errmsg("could not identify an equality operator for type %s",
4389 : format_type_be(element_type))));
6055 tgl 4390 GIC 255 : *fn_extra = (void *) typentry;
6055 tgl 4391 ECB : }
6055 tgl 4392 GIC 11226 : typlen = typentry->typlen;
4393 11226 : typbyval = typentry->typbyval;
4394 11226 : typalign = typentry->typalign;
4395 :
4396 : /*
4397 : * Since we probably will need to scan array2 multiple times, it's
6055 tgl 4398 ECB : * worthwhile to use deconstruct_array on it. We scan array1 the hard way
4399 : * however, since we very likely won't need to look at all of it.
4400 : */
2887 tgl 4401 GIC 11226 : if (VARATT_IS_EXPANDED_HEADER(array2))
4402 : {
2887 tgl 4403 EUB : /* This should be safe even if input is read-only */
2887 tgl 4404 GIC 2556 : deconstruct_expanded_array(&(array2->xpn));
4405 2556 : values2 = array2->xpn.dvalues;
4406 2556 : nulls2 = array2->xpn.dnulls;
4407 2556 : nelems2 = array2->xpn.nelems;
2887 tgl 4408 ECB : }
4409 : else
1379 noah 4410 CBC 8670 : deconstruct_array((ArrayType *) array2,
4411 : element_type, typlen, typbyval, typalign,
2887 tgl 4412 ECB : &values2, &nulls2, &nelems2);
4413 :
4414 : /*
4415 : * Apply the comparison operator to each pair of array elements.
4416 : */
1534 andres 4417 GIC 11226 : InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
4418 : collation, NULL, NULL);
4419 :
4420 : /* Loop over source data */
2887 tgl 4421 11226 : nelems1 = ArrayGetNItems(AARR_NDIM(array1), AARR_DIMS(array1));
4422 11226 : array_iter_setup(&it1, array1);
4423 :
6055 4424 210161 : for (i = 0; i < nelems1; i++)
4425 : {
6055 tgl 4426 ECB : Datum elt1;
4427 : bool isnull1;
4428 :
4429 : /* Get element, checking for NULL */
2887 tgl 4430 CBC 203658 : elt1 = array_iter_next(&it1, &isnull1, i, typlen, typbyval, typalign);
4431 :
6055 tgl 4432 ECB : /*
4433 : * We assume that the comparison operator is strict, so a NULL can't
4434 : * match anything. XXX this diverges from the "NULL=NULL" behavior of
4435 : * array_eq, should we act like that?
4436 : */
6055 tgl 4437 GIC 203658 : if (isnull1)
4438 : {
4439 660 : if (matchall)
6055 tgl 4440 ECB : {
6055 tgl 4441 GIC 630 : result = false;
6055 tgl 4442 CBC 4723 : break;
6055 tgl 4443 ECB : }
6055 tgl 4444 CBC 30 : continue;
6055 tgl 4445 ECB : }
4446 :
6055 tgl 4447 CBC 9208167 : for (j = 0; j < nelems2; j++)
6055 tgl 4448 ECB : {
6055 tgl 4449 GIC 9188465 : Datum elt2 = values2[j];
2879 4450 9188465 : bool isnull2 = nulls2 ? nulls2[j] : false;
4451 : bool oprresult;
4452 :
6055 4453 9188465 : if (isnull2)
4454 3606 : continue; /* can't match */
4455 :
4456 : /*
1083 tgl 4457 ECB : * Apply the operator to the element pair; treat NULL as false
6055 4458 : */
1534 andres 4459 CBC 9184859 : locfcinfo->args[0].value = elt1;
1534 andres 4460 GIC 9184859 : locfcinfo->args[0].isnull = false;
1534 andres 4461 CBC 9184859 : locfcinfo->args[1].value = elt2;
1534 andres 4462 GIC 9184859 : locfcinfo->args[1].isnull = false;
1534 andres 4463 CBC 9184859 : locfcinfo->isnull = false;
4464 9184859 : oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
1083 tgl 4465 GIC 9184859 : if (!locfcinfo->isnull && oprresult)
6055 4466 183296 : break;
4467 : }
6055 tgl 4468 ECB :
6055 tgl 4469 GIC 202998 : if (j < nelems2)
6055 tgl 4470 ECB : {
4471 : /* found a match for elt1 */
6055 tgl 4472 CBC 183296 : if (!matchall)
4473 : {
4474 114 : result = true;
6055 tgl 4475 GIC 114 : break;
4476 : }
4477 : }
6055 tgl 4478 ECB : else
4479 : {
4480 : /* no match for elt1 */
6055 tgl 4481 CBC 19702 : if (matchall)
4482 : {
6055 tgl 4483 GIC 3979 : result = false;
4484 3979 : break;
4485 : }
4486 : }
4487 : }
6055 tgl 4488 ECB :
6055 tgl 4489 GIC 11226 : return result;
6055 tgl 4490 ECB : }
4491 :
6055 tgl 4492 EUB : Datum
6055 tgl 4493 GIC 3060 : arrayoverlap(PG_FUNCTION_ARGS)
4494 : {
2029 4495 3060 : AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
4496 3060 : AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
4380 tgl 4497 CBC 3060 : Oid collation = PG_GET_COLLATION();
6055 tgl 4498 ECB : bool result;
4499 :
4380 tgl 4500 CBC 3060 : result = array_contain_compare(array1, array2, collation, false,
6055 4501 3060 : &fcinfo->flinfo->fn_extra);
4502 :
6055 tgl 4503 ECB : /* Avoid leaking memory when handed toasted input. */
2887 tgl 4504 GIC 3060 : AARR_FREE_IF_COPY(array1, 0);
4505 3060 : AARR_FREE_IF_COPY(array2, 1);
6055 tgl 4506 ECB :
6055 tgl 4507 GIC 3060 : PG_RETURN_BOOL(result);
4508 : }
6055 tgl 4509 ECB :
4510 : Datum
6055 tgl 4511 CBC 4908 : arraycontains(PG_FUNCTION_ARGS)
4512 : {
2029 tgl 4513 GIC 4908 : AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
4514 4908 : AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
4380 4515 4908 : Oid collation = PG_GET_COLLATION();
4516 : bool result;
4517 :
4518 4908 : result = array_contain_compare(array2, array1, collation, true,
6055 4519 4908 : &fcinfo->flinfo->fn_extra);
4520 :
4521 : /* Avoid leaking memory when handed toasted input. */
2887 4522 4908 : AARR_FREE_IF_COPY(array1, 0);
4523 4908 : AARR_FREE_IF_COPY(array2, 1);
4524 :
6055 4525 4908 : PG_RETURN_BOOL(result);
4526 : }
4527 :
4528 : Datum
4529 3258 : arraycontained(PG_FUNCTION_ARGS)
6055 tgl 4530 ECB : {
2029 tgl 4531 GIC 3258 : AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
4532 3258 : AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
4380 tgl 4533 CBC 3258 : Oid collation = PG_GET_COLLATION();
6055 tgl 4534 ECB : bool result;
4535 :
4380 tgl 4536 GIC 3258 : result = array_contain_compare(array1, array2, collation, true,
6055 4537 3258 : &fcinfo->flinfo->fn_extra);
4538 :
4539 : /* Avoid leaking memory when handed toasted input. */
2887 4540 3258 : AARR_FREE_IF_COPY(array1, 0);
4541 3258 : AARR_FREE_IF_COPY(array2, 1);
4542 :
6055 4543 3258 : PG_RETURN_BOOL(result);
4544 : }
4545 :
4546 :
4547 : /*-----------------------------------------------------------------------------
4435 tgl 4548 ECB : * Array iteration functions
4435 tgl 4549 EUB : * These functions are used to iterate efficiently through arrays
4550 : *-----------------------------------------------------------------------------
4551 : */
4552 :
4553 : /*
4554 : * array_create_iterator --- set up to iterate through an array
4555 : *
4556 : * If slice_ndim is zero, we will iterate element-by-element; the returned
4557 : * datums are of the array's element type.
4558 : *
4435 tgl 4559 ECB : * If slice_ndim is 1..ARR_NDIM(arr), we will iterate by slices: the
4560 : * returned datums are of the same array type as 'arr', but of size
4561 : * equal to the rightmost N dimensions of 'arr'.
4562 : *
4563 : * The passed-in array must remain valid for the lifetime of the iterator.
4564 : */
4565 : ArrayIterator
2944 alvherre 4566 GBC 165 : array_create_iterator(ArrayType *arr, int slice_ndim, ArrayMetaState *mstate)
4567 : {
4435 tgl 4568 GIC 165 : ArrayIterator iterator = palloc0(sizeof(ArrayIteratorData));
4569 :
4435 tgl 4570 ECB : /*
4571 : * Sanity-check inputs --- caller should have got this right already
4572 : */
4435 tgl 4573 CBC 165 : Assert(PointerIsValid(arr));
4574 165 : if (slice_ndim < 0 || slice_ndim > ARR_NDIM(arr))
4435 tgl 4575 UIC 0 : elog(ERROR, "invalid arguments to array_create_iterator");
4576 :
4577 : /*
4578 : * Remember basic info about the array and its element type
4579 : */
4435 tgl 4580 GIC 165 : iterator->arr = arr;
4435 tgl 4581 CBC 165 : iterator->nullbitmap = ARR_NULLBITMAP(arr);
4435 tgl 4582 GIC 165 : iterator->nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
4583 :
2944 alvherre 4584 CBC 165 : if (mstate != NULL)
2944 alvherre 4585 ECB : {
2944 alvherre 4586 CBC 129 : Assert(mstate->element_type == ARR_ELEMTYPE(arr));
2944 alvherre 4587 ECB :
2944 alvherre 4588 GIC 129 : iterator->typlen = mstate->typlen;
4589 129 : iterator->typbyval = mstate->typbyval;
2944 alvherre 4590 CBC 129 : iterator->typalign = mstate->typalign;
4591 : }
4592 : else
2944 alvherre 4593 GIC 36 : get_typlenbyvalalign(ARR_ELEMTYPE(arr),
4594 : &iterator->typlen,
4595 : &iterator->typbyval,
4596 : &iterator->typalign);
4435 tgl 4597 ECB :
4598 : /*
4599 : * Remember the slicing parameters.
4600 : */
4435 tgl 4601 CBC 165 : iterator->slice_ndim = slice_ndim;
4435 tgl 4602 ECB :
4435 tgl 4603 GIC 165 : if (slice_ndim > 0)
4435 tgl 4604 ECB : {
4605 : /*
4606 : * Get pointers into the array's dims and lbound arrays to represent
4607 : * the dims/lbound arrays of a slice. These are the same as the
4608 : * rightmost N dimensions of the array.
4609 : */
4435 tgl 4610 CBC 18 : iterator->slice_dims = ARR_DIMS(arr) + ARR_NDIM(arr) - slice_ndim;
4435 tgl 4611 GIC 18 : iterator->slice_lbound = ARR_LBOUND(arr) + ARR_NDIM(arr) - slice_ndim;
4612 :
4613 : /*
4614 : * Compute number of elements in a slice.
4615 : */
4616 36 : iterator->slice_len = ArrayGetNItems(slice_ndim,
4435 tgl 4617 CBC 18 : iterator->slice_dims);
4618 :
4435 tgl 4619 ECB : /*
4620 : * Create workspace for building sub-arrays.
4621 : */
4435 tgl 4622 CBC 18 : iterator->slice_values = (Datum *)
4435 tgl 4623 GIC 18 : palloc(iterator->slice_len * sizeof(Datum));
4435 tgl 4624 CBC 18 : iterator->slice_nulls = (bool *)
4435 tgl 4625 GIC 18 : palloc(iterator->slice_len * sizeof(bool));
4626 : }
4435 tgl 4627 ECB :
4628 : /*
4629 : * Initialize our data pointer and linear element number. These will
4630 : * advance through the array during array_iterate().
4631 : */
4435 tgl 4632 GIC 165 : iterator->data_ptr = ARR_DATA_PTR(arr);
4435 tgl 4633 CBC 165 : iterator->current_item = 0;
4435 tgl 4634 ECB :
4435 tgl 4635 GIC 165 : return iterator;
4636 : }
4637 :
4638 : /*
4435 tgl 4639 ECB : * Iterate through the array referenced by 'iterator'.
4640 : *
4641 : * As long as there is another element (or slice), return it into
4642 : * *value / *isnull, and return true. Return false when no more data.
4643 : */
4644 : bool
4435 tgl 4645 CBC 846 : array_iterate(ArrayIterator iterator, Datum *value, bool *isnull)
4435 tgl 4646 ECB : {
4647 : /* Done if we have reached the end of the array */
4435 tgl 4648 GIC 846 : if (iterator->current_item >= iterator->nitems)
4435 tgl 4649 CBC 75 : return false;
4650 :
4435 tgl 4651 GIC 771 : if (iterator->slice_ndim == 0)
4435 tgl 4652 ECB : {
4653 : /*
4654 : * Scalar case: return one element.
4655 : */
4435 tgl 4656 GIC 744 : if (array_get_isnull(iterator->nullbitmap, iterator->current_item++))
4657 : {
4658 12 : *isnull = true;
4659 12 : *value = (Datum) 0;
4660 : }
4435 tgl 4661 ECB : else
4662 : {
4663 : /* non-NULL, so fetch the individual Datum to return */
4435 tgl 4664 CBC 732 : char *p = iterator->data_ptr;
4665 :
4435 tgl 4666 GIC 732 : *isnull = false;
4667 732 : *value = fetch_att(p, iterator->typbyval, iterator->typlen);
4668 :
4435 tgl 4669 ECB : /* Move our data pointer forward to the next element */
4435 tgl 4670 GIC 732 : p = att_addlength_pointer(p, iterator->typlen, p);
4671 732 : p = (char *) att_align_nominal(p, iterator->typalign);
4672 732 : iterator->data_ptr = p;
4435 tgl 4673 ECB : }
4674 : }
4675 : else
4676 : {
4677 : /*
4678 : * Slice case: build and return an array of the requested size.
4679 : */
4680 : ArrayType *result;
4435 tgl 4681 CBC 27 : Datum *values = iterator->slice_values;
4435 tgl 4682 GIC 27 : bool *nulls = iterator->slice_nulls;
4683 27 : char *p = iterator->data_ptr;
4435 tgl 4684 ECB : int i;
4685 :
4435 tgl 4686 GIC 96 : for (i = 0; i < iterator->slice_len; i++)
4435 tgl 4687 ECB : {
4435 tgl 4688 GIC 69 : if (array_get_isnull(iterator->nullbitmap,
4689 69 : iterator->current_item++))
4690 : {
4435 tgl 4691 LBC 0 : nulls[i] = true;
4435 tgl 4692 UIC 0 : values[i] = (Datum) 0;
4435 tgl 4693 ECB : }
4694 : else
4695 : {
4435 tgl 4696 GIC 69 : nulls[i] = false;
4697 69 : values[i] = fetch_att(p, iterator->typbyval, iterator->typlen);
4435 tgl 4698 ECB :
4699 : /* Move our data pointer forward to the next element */
4435 tgl 4700 GIC 69 : p = att_addlength_pointer(p, iterator->typlen, p);
4701 69 : p = (char *) att_align_nominal(p, iterator->typalign);
4435 tgl 4702 ECB : }
4703 : }
4704 :
4435 tgl 4705 CBC 27 : iterator->data_ptr = p;
4706 :
4435 tgl 4707 GIC 27 : result = construct_md_array(values,
4708 : nulls,
4435 tgl 4709 ECB : iterator->slice_ndim,
4710 : iterator->slice_dims,
4711 : iterator->slice_lbound,
4435 tgl 4712 CBC 27 : ARR_ELEMTYPE(iterator->arr),
4713 27 : iterator->typlen,
4435 tgl 4714 GIC 27 : iterator->typbyval,
4715 27 : iterator->typalign);
4435 tgl 4716 ECB :
4435 tgl 4717 CBC 27 : *isnull = false;
4435 tgl 4718 GIC 27 : *value = PointerGetDatum(result);
4719 : }
4435 tgl 4720 ECB :
4435 tgl 4721 CBC 771 : return true;
4722 : }
4435 tgl 4723 ECB :
4724 : /*
4725 : * Release an ArrayIterator data structure
4726 : */
4727 : void
4435 tgl 4728 GIC 129 : array_free_iterator(ArrayIterator iterator)
4729 : {
4730 129 : if (iterator->slice_ndim > 0)
4731 : {
4435 tgl 4732 UIC 0 : pfree(iterator->slice_values);
4733 0 : pfree(iterator->slice_nulls);
4734 : }
4435 tgl 4735 GIC 129 : pfree(iterator);
4736 129 : }
4737 :
4738 :
4739 : /***************************************************************************/
4740 : /******************| Support Routines |*****************/
4741 : /***************************************************************************/
4742 :
4743 : /*
4744 : * Check whether a specific array element is NULL
4745 : *
6352 tgl 4746 ECB : * nullbitmap: pointer to array's null bitmap (NULL if none)
4747 : * offset: 0-based linear element number of array element
4748 : */
4749 : static bool
6352 tgl 4750 GIC 343427 : array_get_isnull(const bits8 *nullbitmap, int offset)
4751 : {
4752 343427 : if (nullbitmap == NULL)
6352 tgl 4753 CBC 343164 : return false; /* assume not null */
4754 263 : if (nullbitmap[offset / 8] & (1 << (offset % 8)))
6352 tgl 4755 GBC 206 : return false; /* not null */
6352 tgl 4756 GIC 57 : return true;
4757 : }
4758 :
4759 : /*
6352 tgl 4760 ECB : * Set a specific array element's null-bitmap entry
4761 : *
4762 : * nullbitmap: pointer to array's null bitmap (mustn't be NULL)
4763 : * offset: 0-based linear element number of array element
4764 : * isNull: null status to set
4765 : */
4766 : static void
6352 tgl 4767 GIC 67 : array_set_isnull(bits8 *nullbitmap, int offset, bool isNull)
6352 tgl 4768 ECB : {
4769 : int bitmask;
4770 :
6352 tgl 4771 GIC 67 : nullbitmap += offset / 8;
4772 67 : bitmask = 1 << (offset % 8);
6352 tgl 4773 CBC 67 : if (isNull)
6352 tgl 4774 GIC 10 : *nullbitmap &= ~bitmask;
4775 : else
4776 57 : *nullbitmap |= bitmask;
4777 67 : }
4778 :
4779 : /*
4780 : * Fetch array element at pointer, converted correctly to a Datum
6352 tgl 4781 ECB : *
4782 : * Caller must have handled case of NULL element
8295 4783 : */
4784 : static Datum
8296 tgl 4785 GIC 342290 : ArrayCast(char *value, bool byval, int len)
4786 : {
8138 4787 342290 : return fetch_att(value, byval, len);
4788 : }
4789 :
8301 tgl 4790 ECB : /*
4791 : * Copy datum to *dest and return total space used (including align padding)
4792 : *
4793 : * Caller must have handled case of NULL element
4794 : */
4795 : static int
8335 tgl 4796 CBC 9020910 : ArrayCastAndSet(Datum src,
9345 bruce 4797 ECB : int typlen,
4798 : bool typbyval,
4799 : char typalign,
4800 : char *dest)
4801 : {
9344 4802 : int inc;
9345 4803 :
9345 bruce 4804 CBC 9020910 : if (typlen > 0)
9345 bruce 4805 ECB : {
9345 bruce 4806 GIC 6680905 : if (typbyval)
8138 tgl 4807 5831760 : store_att_byval(dest, src, typlen);
4808 : else
8335 4809 849145 : memmove(dest, DatumGetPointer(src), typlen);
5847 4810 6680905 : inc = att_align_nominal(typlen, typalign);
4811 : }
9345 bruce 4812 ECB : else
4813 : {
7531 tgl 4814 GIC 2340005 : Assert(!typbyval);
5847 tgl 4815 CBC 2340005 : inc = att_addlength_datum(0, typlen, src);
7531 tgl 4816 GIC 2340005 : memmove(dest, DatumGetPointer(src), inc);
5847 4817 2340005 : inc = att_align_nominal(inc, typalign);
4818 : }
4819 :
8296 4820 9020910 : return inc;
4821 : }
4822 :
4823 : /*
4824 : * Advance ptr over nitems array elements
6352 tgl 4825 ECB : *
4826 : * ptr: starting location in array
4827 : * offset: 0-based linear element number of first element (the one at *ptr)
4828 : * nullbitmap: start of array's null bitmap, or NULL if none
4829 : * nitems: number of array elements to advance over (>= 0)
4830 : * typlen, typbyval, typalign: storage parameters of array element datatype
4831 : *
4832 : * It is caller's responsibility to ensure that nitems is within range
4833 : */
4834 : static char *
6352 tgl 4835 GIC 343463 : array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
6352 tgl 4836 ECB : int typlen, bool typbyval, char typalign)
4837 : {
4838 : int bitmask;
9344 bruce 4839 : int i;
4840 :
4841 : /* easy if fixed-size elements and no NULLs */
6352 tgl 4842 GIC 343463 : if (typlen > 0 && !nullbitmap)
5847 4843 216894 : return ptr + nitems * ((Size) att_align_nominal(typlen, typalign));
7531 tgl 4844 ECB :
4845 : /* seems worth having separate loops for NULL and no-NULLs cases */
6352 tgl 4846 CBC 126569 : if (nullbitmap)
7531 tgl 4847 ECB : {
6352 tgl 4848 GIC 302 : nullbitmap += offset / 8;
4849 302 : bitmask = 1 << (offset % 8);
6352 tgl 4850 ECB :
6352 tgl 4851 CBC 926 : for (i = 0; i < nitems; i++)
6352 tgl 4852 ECB : {
6352 tgl 4853 GIC 624 : if (*nullbitmap & bitmask)
4854 : {
5847 4855 444 : ptr = att_addlength_pointer(ptr, typlen, ptr);
4856 444 : ptr = (char *) att_align_nominal(ptr, typalign);
4857 : }
6352 4858 624 : bitmask <<= 1;
4859 624 : if (bitmask == 0x100)
4860 : {
6352 tgl 4861 CBC 24 : nullbitmap++;
4862 24 : bitmask = 1;
6352 tgl 4863 ECB : }
4864 : }
4865 : }
4866 : else
4867 : {
6352 tgl 4868 CBC 576597 : for (i = 0; i < nitems; i++)
6352 tgl 4869 ECB : {
5847 tgl 4870 GIC 450330 : ptr = att_addlength_pointer(ptr, typlen, ptr);
5847 tgl 4871 GBC 450330 : ptr = (char *) att_align_nominal(ptr, typalign);
6352 tgl 4872 EUB : }
4873 : }
6352 tgl 4874 GIC 126569 : return ptr;
4875 : }
9770 scrappy 4876 ECB :
8295 tgl 4877 : /*
4878 : * Compute total size of the nitems array elements starting at *ptr
4879 : *
6352 4880 : * Parameters same as for array_seek
8295 4881 : */
4882 : static int
6352 tgl 4883 GIC 789 : array_nelems_size(char *ptr, int offset, bits8 *nullbitmap, int nitems,
4884 : int typlen, bool typbyval, char typalign)
9770 scrappy 4885 ECB : {
6352 tgl 4886 GIC 789 : return array_seek(ptr, offset, nullbitmap, nitems,
6352 tgl 4887 CBC 789 : typlen, typbyval, typalign) - ptr;
4888 : }
4889 :
4890 : /*
4891 : * Copy nitems array elements from srcptr to destptr
8295 tgl 4892 ECB : *
6352 4893 : * destptr: starting destination location (must be enough room!)
4894 : * nitems: number of array elements to copy (>= 0)
4895 : * srcptr: starting location in source array
4896 : * offset: 0-based linear element number of first element (the one at *srcptr)
4897 : * nullbitmap: start of source array's null bitmap, or NULL if none
4898 : * typlen, typbyval, typalign: storage parameters of array element datatype
4899 : *
4900 : * Returns number of bytes copied
4901 : *
4902 : * NB: this does not take care of setting up the destination's null bitmap!
4903 : */
4904 : static int
6352 tgl 4905 GIC 555 : array_copy(char *destptr, int nitems,
4906 : char *srcptr, int offset, bits8 *nullbitmap,
4907 : int typlen, bool typbyval, char typalign)
8295 tgl 4908 ECB : {
4909 : int numbytes;
4910 :
6352 tgl 4911 GIC 555 : numbytes = array_nelems_size(srcptr, offset, nullbitmap, nitems,
6352 tgl 4912 EUB : typlen, typbyval, typalign);
6352 tgl 4913 GBC 555 : memcpy(destptr, srcptr, numbytes);
8295 tgl 4914 GIC 555 : return numbytes;
9770 scrappy 4915 ECB : }
4916 :
4917 : /*
4918 : * Copy nitems null-bitmap bits from source to destination
4919 : *
4920 : * destbitmap: start of destination array's null bitmap (mustn't be NULL)
4921 : * destoffset: 0-based linear element number of first dest element
4922 : * srcbitmap: start of source array's null bitmap, or NULL if none
4923 : * srcoffset: 0-based linear element number of first source element
4924 : * nitems: number of bits to copy (>= 0)
4925 : *
4926 : * If srcbitmap is NULL then we assume the source is all-non-NULL and
4927 : * fill 1's into the destination bitmap. Note that only the specified
4928 : * bits in the destination map are changed, not any before or after.
4929 : *
6352 tgl 4930 : * Note: this could certainly be optimized using standard bitblt methods.
4931 : * However, it's not clear that the typical Postgres array has enough elements
3260 bruce 4932 : * to make it worth worrying too much. For the moment, KISS.
6352 tgl 4933 : */
4934 : void
6352 tgl 4935 CBC 15434 : array_bitmap_copy(bits8 *destbitmap, int destoffset,
6352 tgl 4936 ECB : const bits8 *srcbitmap, int srcoffset,
4937 : int nitems)
4938 : {
4939 : int destbitmask,
4940 : destbitval,
4941 : srcbitmask,
4942 : srcbitval;
4943 :
6352 tgl 4944 GIC 15434 : Assert(destbitmap);
4945 15434 : if (nitems <= 0)
4946 81 : return; /* don't risk fetch off end of memory */
6352 tgl 4947 CBC 15353 : destbitmap += destoffset / 8;
6352 tgl 4948 GIC 15353 : destbitmask = 1 << (destoffset % 8);
4949 15353 : destbitval = *destbitmap;
4950 15353 : if (srcbitmap)
6352 tgl 4951 ECB : {
6352 tgl 4952 CBC 7818 : srcbitmap += srcoffset / 8;
4953 7818 : srcbitmask = 1 << (srcoffset % 8);
4954 7818 : srcbitval = *srcbitmap;
6352 tgl 4955 GIC 35156 : while (nitems-- > 0)
6352 tgl 4956 ECB : {
6352 tgl 4957 CBC 27338 : if (srcbitval & srcbitmask)
6352 tgl 4958 GIC 10087 : destbitval |= destbitmask;
4959 : else
4960 17251 : destbitval &= ~destbitmask;
4961 27338 : destbitmask <<= 1;
4962 27338 : if (destbitmask == 0x100)
4963 : {
4964 3150 : *destbitmap++ = destbitval;
6352 tgl 4965 CBC 3150 : destbitmask = 1;
6352 tgl 4966 GIC 3150 : if (nitems > 0)
6352 tgl 4967 CBC 2393 : destbitval = *destbitmap;
4968 : }
6352 tgl 4969 GIC 27338 : srcbitmask <<= 1;
4970 27338 : if (srcbitmask == 0x100)
4971 : {
4972 2387 : srcbitmap++;
4973 2387 : srcbitmask = 1;
4974 2387 : if (nitems > 0)
4975 2369 : srcbitval = *srcbitmap;
6352 tgl 4976 ECB : }
4977 : }
6352 tgl 4978 GIC 7818 : if (destbitmask != 1)
4979 7061 : *destbitmap = destbitval;
4980 : }
4981 : else
4982 : {
4983 15132 : while (nitems-- > 0)
6352 tgl 4984 ECB : {
6352 tgl 4985 GIC 7597 : destbitval |= destbitmask;
6352 tgl 4986 CBC 7597 : destbitmask <<= 1;
4987 7597 : if (destbitmask == 0x100)
4988 : {
4989 1116 : *destbitmap++ = destbitval;
4990 1116 : destbitmask = 1;
6352 tgl 4991 GIC 1116 : if (nitems > 0)
6352 tgl 4992 UIC 0 : destbitval = *destbitmap;
4993 : }
6352 tgl 4994 ECB : }
6352 tgl 4995 CBC 7535 : if (destbitmask != 1)
4996 6419 : *destbitmap = destbitval;
6352 tgl 4997 ECB : }
4998 : }
4999 :
8295 5000 : /*
5001 : * Compute space needed for a slice of an array
5002 : *
5003 : * We assume the caller has verified that the slice coordinates are valid.
5004 : */
5005 : static int
6352 tgl 5006 GIC 141 : array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
5007 : int ndim, int *dim, int *lb,
5008 : int *st, int *endp,
5009 : int typlen, bool typbyval, char typalign)
5010 : {
5011 : int src_offset,
5012 : span[MAXDIM],
5013 : prod[MAXDIM],
5014 : dist[MAXDIM],
9344 bruce 5015 ECB : indx[MAXDIM];
5016 : char *ptr;
5017 : int i,
5018 : j,
5019 : inc;
9344 bruce 5020 GIC 141 : int count = 0;
5021 :
8295 tgl 5022 CBC 141 : mda_get_range(ndim, span, st, endp);
8295 tgl 5023 ECB :
5024 : /* Pretty easy for fixed element length without nulls ... */
6352 tgl 5025 GIC 141 : if (typlen > 0 && !arraynullsptr)
5847 tgl 5026 CBC 102 : return ArrayGetNItems(ndim, span) * att_align_nominal(typlen, typalign);
5027 :
8295 tgl 5028 ECB : /* Else gotta do it the hard way */
6352 tgl 5029 CBC 39 : src_offset = ArrayGetOffset(ndim, dim, lb, st);
6352 tgl 5030 GIC 39 : ptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
7531 tgl 5031 ECB : typlen, typbyval, typalign);
8295 tgl 5032 GIC 39 : mda_get_prod(ndim, dim, prod);
8295 tgl 5033 CBC 39 : mda_get_offset_values(ndim, dist, prod, span);
8295 tgl 5034 GIC 99 : for (i = 0; i < ndim; i++)
8296 tgl 5035 CBC 60 : indx[i] = 0;
8295 5036 39 : j = ndim - 1;
5037 : do
9345 bruce 5038 ECB : {
6352 tgl 5039 CBC 129 : if (dist[j])
5040 : {
6352 tgl 5041 LBC 0 : ptr = array_seek(ptr, src_offset, arraynullsptr, dist[j],
6352 tgl 5042 ECB : typlen, typbyval, typalign);
6352 tgl 5043 UIC 0 : src_offset += dist[j];
5044 : }
6352 tgl 5045 GIC 129 : if (!array_get_isnull(arraynullsptr, src_offset))
5046 : {
5847 5047 123 : inc = att_addlength_pointer(0, typlen, ptr);
5847 tgl 5048 CBC 123 : inc = att_align_nominal(inc, typalign);
6352 tgl 5049 GIC 123 : ptr += inc;
6352 tgl 5050 CBC 123 : count += inc;
6352 tgl 5051 ECB : }
6352 tgl 5052 GIC 129 : src_offset++;
8295 5053 129 : } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
9345 bruce 5054 CBC 39 : return count;
5055 : }
5056 :
5057 : /*
5058 : * Extract a slice of an array into consecutive elements in the destination
5059 : * array.
5060 : *
5061 : * We assume the caller has verified that the slice coordinates are valid,
5062 : * allocated enough storage for the result, and initialized the header
6352 tgl 5063 ECB : * of the new array.
5064 : */
5065 : static void
6352 tgl 5066 CBC 126 : array_extract_slice(ArrayType *newarray,
6352 tgl 5067 ECB : int ndim,
5068 : int *dim,
5069 : int *lb,
5070 : char *arraydataptr,
5071 : bits8 *arraynullsptr,
5072 : int *st,
5073 : int *endp,
5074 : int typlen,
5075 : bool typbyval,
5076 : char typalign)
5077 : {
6352 tgl 5078 GIC 126 : char *destdataptr = ARR_DATA_PTR(newarray);
5079 126 : bits8 *destnullsptr = ARR_NULLBITMAP(newarray);
5080 : char *srcdataptr;
5081 : int src_offset,
5082 : dest_offset,
5083 : prod[MAXDIM],
5084 : span[MAXDIM],
8295 tgl 5085 ECB : dist[MAXDIM],
5086 : indx[MAXDIM];
5087 : int i,
5088 : j,
5089 : inc;
5090 :
6352 tgl 5091 CBC 126 : src_offset = ArrayGetOffset(ndim, dim, lb, st);
6352 tgl 5092 GIC 126 : srcdataptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
6347 bruce 5093 ECB : typlen, typbyval, typalign);
8295 tgl 5094 CBC 126 : mda_get_prod(ndim, dim, prod);
8295 tgl 5095 GIC 126 : mda_get_range(ndim, span, st, endp);
5096 126 : mda_get_offset_values(ndim, dist, prod, span);
5097 318 : for (i = 0; i < ndim; i++)
5098 192 : indx[i] = 0;
6352 5099 126 : dest_offset = 0;
8295 5100 126 : j = ndim - 1;
5101 : do
5102 : {
6352 5103 477 : if (dist[j])
5104 : {
5105 : /* skip unwanted elements */
5106 12 : srcdataptr = array_seek(srcdataptr, src_offset, arraynullsptr,
5107 : dist[j],
5108 : typlen, typbyval, typalign);
5109 12 : src_offset += dist[j];
5110 : }
5111 477 : inc = array_copy(destdataptr, 1,
5112 : srcdataptr, src_offset, arraynullsptr,
5113 : typlen, typbyval, typalign);
5114 477 : if (destnullsptr)
6352 tgl 5115 CBC 90 : array_bitmap_copy(destnullsptr, dest_offset,
5116 : arraynullsptr, src_offset,
5117 : 1);
6352 tgl 5118 GIC 477 : destdataptr += inc;
5119 477 : srcdataptr += inc;
5120 477 : src_offset++;
5121 477 : dest_offset++;
8295 5122 477 : } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
9770 scrappy 5123 126 : }
9770 scrappy 5124 ECB :
8295 tgl 5125 : /*
5126 : * Insert a slice into an array.
5127 : *
6352 5128 : * ndim/dim[]/lb[] are dimensions of the original array. A new array with
3260 bruce 5129 : * those same dimensions is to be constructed. destArray must already
6352 tgl 5130 : * have been allocated and its header initialized.
5131 : *
5132 : * st[]/endp[] identify the slice to be replaced. Elements within the slice
5133 : * volume are taken from consecutive elements of the srcArray; elements
5134 : * outside it are copied from origArray.
8295 5135 : *
5136 : * We assume the caller has verified that the slice coordinates are valid.
5137 : */
5138 : static void
6352 tgl 5139 GIC 15 : array_insert_slice(ArrayType *destArray,
6352 tgl 5140 ECB : ArrayType *origArray,
5141 : ArrayType *srcArray,
5142 : int ndim,
5143 : int *dim,
8295 5144 : int *lb,
5145 : int *st,
5146 : int *endp,
7531 5147 : int typlen,
5148 : bool typbyval,
5149 : char typalign)
9770 scrappy 5150 : {
6352 tgl 5151 GIC 15 : char *destPtr = ARR_DATA_PTR(destArray);
6352 tgl 5152 CBC 15 : char *origPtr = ARR_DATA_PTR(origArray);
5153 15 : char *srcPtr = ARR_DATA_PTR(srcArray);
5154 15 : bits8 *destBitmap = ARR_NULLBITMAP(destArray);
5155 15 : bits8 *origBitmap = ARR_NULLBITMAP(origArray);
6352 tgl 5156 GIC 15 : bits8 *srcBitmap = ARR_NULLBITMAP(srcArray);
5157 15 : int orignitems = ArrayGetNItems(ARR_NDIM(origArray),
6352 tgl 5158 ECB : ARR_DIMS(origArray));
5159 : int dest_offset,
5160 : orig_offset,
5161 : src_offset,
5162 : prod[MAXDIM],
8295 5163 : span[MAXDIM],
5164 : dist[MAXDIM],
5165 : indx[MAXDIM];
9344 bruce 5166 : int i,
8295 tgl 5167 : j,
5168 : inc;
9345 bruce 5169 :
6352 tgl 5170 CBC 15 : dest_offset = ArrayGetOffset(ndim, dim, lb, st);
6352 tgl 5171 ECB : /* copy items before the slice start */
6352 tgl 5172 GBC 15 : inc = array_copy(destPtr, dest_offset,
5173 : origPtr, 0, origBitmap,
5174 : typlen, typbyval, typalign);
8295 tgl 5175 CBC 15 : destPtr += inc;
5176 15 : origPtr += inc;
6352 tgl 5177 GIC 15 : if (destBitmap)
6352 tgl 5178 UIC 0 : array_bitmap_copy(destBitmap, 0, origBitmap, 0, dest_offset);
6352 tgl 5179 GIC 15 : orig_offset = dest_offset;
8295 5180 15 : mda_get_prod(ndim, dim, prod);
5181 15 : mda_get_range(ndim, span, st, endp);
5182 15 : mda_get_offset_values(ndim, dist, prod, span);
5183 51 : for (i = 0; i < ndim; i++)
5184 36 : indx[i] = 0;
6352 5185 15 : src_offset = 0;
8295 tgl 5186 CBC 15 : j = ndim - 1;
5187 : do
5188 : {
5189 : /* Copy/advance over elements between here and next part of slice */
6352 tgl 5190 GIC 39 : if (dist[j])
5191 : {
5192 9 : inc = array_copy(destPtr, dist[j],
5193 : origPtr, orig_offset, origBitmap,
5194 : typlen, typbyval, typalign);
5195 9 : destPtr += inc;
5196 9 : origPtr += inc;
5197 9 : if (destBitmap)
6352 tgl 5198 UIC 0 : array_bitmap_copy(destBitmap, dest_offset,
5199 : origBitmap, orig_offset,
6352 tgl 5200 ECB : dist[j]);
6352 tgl 5201 GIC 9 : dest_offset += dist[j];
6352 tgl 5202 CBC 9 : orig_offset += dist[j];
5203 : }
5204 : /* Copy new element at this slice position */
5205 39 : inc = array_copy(destPtr, 1,
6352 tgl 5206 ECB : srcPtr, src_offset, srcBitmap,
5207 : typlen, typbyval, typalign);
6352 tgl 5208 GIC 39 : if (destBitmap)
6352 tgl 5209 LBC 0 : array_bitmap_copy(destBitmap, dest_offset,
6352 tgl 5210 ECB : srcBitmap, src_offset,
5211 : 1);
8295 tgl 5212 CBC 39 : destPtr += inc;
5213 39 : srcPtr += inc;
6352 5214 39 : dest_offset++;
5215 39 : src_offset++;
8295 tgl 5216 ECB : /* Advance over old element at this slice position */
6352 tgl 5217 GIC 39 : origPtr = array_seek(origPtr, orig_offset, origBitmap, 1,
5218 : typlen, typbyval, typalign);
6352 tgl 5219 CBC 39 : orig_offset++;
8295 tgl 5220 GIC 39 : } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
8295 tgl 5221 EUB :
5222 : /* don't miss any data at the end */
6352 tgl 5223 GBC 15 : array_copy(destPtr, orignitems - orig_offset,
5224 : origPtr, orig_offset, origBitmap,
6352 tgl 5225 ECB : typlen, typbyval, typalign);
6352 tgl 5226 GIC 15 : if (destBitmap)
6352 tgl 5227 LBC 0 : array_bitmap_copy(destBitmap, dest_offset,
6352 tgl 5228 ECB : origBitmap, orig_offset,
5229 : orignitems - orig_offset);
9770 scrappy 5230 CBC 15 : }
5231 :
3057 tgl 5232 ECB : /*
5233 : * initArrayResult - initialize an empty ArrayBuildState
5234 : *
5235 : * element_type is the array element type (must be a valid array element type)
5236 : * rcontext is where to keep working state
5237 : * subcontext is a flag determining whether to use a separate memory context
5238 : *
5239 : * Note: there are two common schemes for using accumArrayResult().
5240 : * In the older scheme, you start with a NULL ArrayBuildState pointer, and
5241 : * call accumArrayResult once per element. In this scheme you end up with
5242 : * a NULL pointer if there were no elements, which you need to special-case.
5243 : * In the newer scheme, call initArrayResult and then call accumArrayResult
5244 : * once per element. In this scheme you always end with a non-NULL pointer
5245 : * that you can pass to makeArrayResult; you get an empty array if there
5246 : * were no elements. This is preferred if an empty array is what you want.
5247 : *
5248 : * It's possible to choose whether to create a separate memory context for the
5249 : * array build state, or whether to allocate it directly within rcontext.
5250 : *
5251 : * When there are many concurrent small states (e.g. array_agg() using hash
5252 : * aggregation of many small groups), using a separate memory context for each
5253 : * one may result in severe memory bloat. In such cases, use the same memory
5254 : * context to initialize all such array build states, and pass
5255 : * subcontext=false.
5256 : *
5257 : * In cases when the array build states have different lifetimes, using a
2969 jdavis 5258 : * single memory context is impractical. Instead, pass subcontext=true so that
5259 : * the array build states can be freed individually.
5260 : */
5261 : ArrayBuildState *
2969 jdavis 5262 GIC 174472 : initArrayResult(Oid element_type, MemoryContext rcontext, bool subcontext)
5263 : {
5264 : /*
5265 : * When using a subcontext, we can afford to start with a somewhat larger
5266 : * initial array size. Without subcontexts, we'd better hope that most of
5267 : * the states stay small ...
5268 : */
76 drowley 5269 GNC 174472 : return initArrayResultWithSize(element_type, rcontext, subcontext,
5270 : subcontext ? 64 : 8);
5271 : }
5272 :
5273 : /*
5274 : * initArrayResultWithSize
5275 : * As initArrayResult, but allow the initial size of the allocated arrays
5276 : * to be specified.
5277 : */
5278 : ArrayBuildState *
5279 174552 : initArrayResultWithSize(Oid element_type, MemoryContext rcontext,
5280 : bool subcontext, int initsize)
5281 : {
5282 : ArrayBuildState *astate;
2969 jdavis 5283 GIC 174552 : MemoryContext arr_context = rcontext;
5284 :
5285 : /* Make a temporary context to hold all the junk */
5286 174552 : if (subcontext)
5287 123744 : arr_context = AllocSetContextCreate(rcontext,
5288 : "accumArrayResult",
2416 tgl 5289 ECB : ALLOCSET_DEFAULT_SIZES);
3057 5290 :
5291 : astate = (ArrayBuildState *)
3057 tgl 5292 CBC 174552 : MemoryContextAlloc(arr_context, sizeof(ArrayBuildState));
5293 174552 : astate->mcontext = arr_context;
2969 jdavis 5294 174552 : astate->private_cxt = subcontext;
76 drowley 5295 GNC 174552 : astate->alen = initsize;
3057 tgl 5296 CBC 174552 : astate->dvalues = (Datum *)
5297 174552 : MemoryContextAlloc(arr_context, astate->alen * sizeof(Datum));
5298 174552 : astate->dnulls = (bool *)
3057 tgl 5299 GIC 174552 : MemoryContextAlloc(arr_context, astate->alen * sizeof(bool));
5300 174552 : astate->nelems = 0;
3057 tgl 5301 CBC 174552 : astate->element_type = element_type;
3057 tgl 5302 GIC 174552 : get_typlenbyvalalign(element_type,
5303 : &astate->typlen,
3057 tgl 5304 ECB : &astate->typbyval,
5305 : &astate->typalign);
5306 :
3057 tgl 5307 CBC 174552 : return astate;
5308 : }
3057 tgl 5309 ECB :
5310 : /*
5311 : * accumArrayResult - accumulate one (more) Datum for an array result
7226 5312 : *
3057 5313 : * astate is working state (can be NULL on first call)
5314 : * dvalue/disnull represent the new Datum to append to the array
5315 : * element_type is the Datum's type (must be a valid array element type)
7226 5316 : * rcontext is where to keep working state
5317 : */
5318 : ArrayBuildState *
7184 bruce 5319 CBC 1422437 : accumArrayResult(ArrayBuildState *astate,
7226 tgl 5320 ECB : Datum dvalue, bool disnull,
5321 : Oid element_type,
5322 : MemoryContext rcontext)
5323 : {
5324 : MemoryContext oldcontext;
5325 :
7226 tgl 5326 GIC 1422437 : if (astate == NULL)
5327 : {
5328 : /* First time through --- initialize */
2969 jdavis 5329 102783 : astate = initArrayResult(element_type, rcontext, true);
5330 : }
5331 : else
5332 : {
7226 tgl 5333 1319654 : Assert(astate->element_type == element_type);
5334 : }
5335 :
3057 5336 1422437 : oldcontext = MemoryContextSwitchTo(astate->mcontext);
3057 tgl 5337 ECB :
5338 : /* enlarge dvalues[]/dnulls[] if needed */
3057 tgl 5339 GIC 1422437 : if (astate->nelems >= astate->alen)
5340 : {
5341 8908 : astate->alen *= 2;
5342 8908 : astate->dvalues = (Datum *)
5343 8908 : repalloc(astate->dvalues, astate->alen * sizeof(Datum));
5344 8908 : astate->dnulls = (bool *)
5345 8908 : repalloc(astate->dnulls, astate->alen * sizeof(bool));
5346 : }
5347 :
5348 : /*
4790 bruce 5349 ECB : * Ensure pass-by-ref stuff is copied into mcontext; and detoast it too if
5350 : * it's varlena. (You might think that detoasting is not needed here
5041 tgl 5351 : * because construct_md_array can detoast the array elements later.
5352 : * However, we must not let construct_md_array modify the ArrayBuildState
4790 bruce 5353 : * because that would mean array_agg_finalfn damages its input, which is
5354 : * verboten. Also, this way frequently saves one copying step.)
5041 tgl 5355 : */
6352 tgl 5356 GIC 1422437 : if (!disnull && !astate->typbyval)
5357 : {
5041 5358 1259423 : if (astate->typlen == -1)
5359 879971 : dvalue = PointerGetDatum(PG_DETOAST_DATUM_COPY(dvalue));
5360 : else
5361 379452 : dvalue = datumCopy(dvalue, astate->typbyval, astate->typlen);
5362 : }
5363 :
6352 5364 1422437 : astate->dvalues[astate->nelems] = dvalue;
5365 1422437 : astate->dnulls[astate->nelems] = disnull;
5366 1422437 : astate->nelems++;
5367 :
7226 tgl 5368 CBC 1422437 : MemoryContextSwitchTo(oldcontext);
5369 :
5370 1422437 : return astate;
5371 : }
5372 :
7226 tgl 5373 ECB : /*
5374 : * makeArrayResult - produce 1-D final result of accumArrayResult
5375 : *
2969 jdavis 5376 EUB : * Note: only releases astate if it was initialized within a separate memory
2969 jdavis 5377 ECB : * context (i.e. using subcontext=true when calling initArrayResult).
5378 : *
3057 tgl 5379 : * astate is working state (must not be NULL)
7226 5380 : * rcontext is where to construct result
5381 : */
5382 : Datum
7184 bruce 5383 CBC 102874 : makeArrayResult(ArrayBuildState *astate,
7226 tgl 5384 ECB : MemoryContext rcontext)
5385 : {
5386 : int ndims;
5387 : int dims[1];
5388 : int lbs[1];
5389 :
3057 5390 : /* If no elements were presented, we want to create an empty array */
3057 tgl 5391 GIC 102874 : ndims = (astate->nelems > 0) ? 1 : 0;
7226 5392 102874 : dims[0] = astate->nelems;
7226 tgl 5393 CBC 102874 : lbs[0] = 1;
7226 tgl 5394 ECB :
2969 jdavis 5395 CBC 205748 : return makeMdArrayResult(astate, ndims, dims, lbs, rcontext,
2969 jdavis 5396 GBC 102874 : astate->private_cxt);
5397 : }
5398 :
7226 tgl 5399 ECB : /*
5400 : * makeMdArrayResult - produce multi-D final result of accumArrayResult
5401 : *
5402 : * beware: no check that specified dimensions match the number of values
5403 : * accumulated.
5404 : *
5405 : * Note: if the astate was not initialized within a separate memory context
2969 jdavis 5406 : * (that is, initArrayResult was called with subcontext=false), then using
2969 jdavis 5407 EUB : * release=true is illegal. Instead, release astate along with the rest of its
5408 : * context when appropriate.
5409 : *
3057 tgl 5410 ECB : * astate is working state (must not be NULL)
7226 5411 : * rcontext is where to construct result
5215 5412 : * release is true if okay to release working state
7226 5413 : */
5414 : Datum
7184 bruce 5415 CBC 174265 : makeMdArrayResult(ArrayBuildState *astate,
5416 : int ndims,
7226 tgl 5417 ECB : int *dims,
5418 : int *lbs,
5419 : MemoryContext rcontext,
5420 : bool release)
5421 : {
5422 : ArrayType *result;
5423 : MemoryContext oldcontext;
5424 :
7226 tgl 5425 EUB : /* Build the final array result in rcontext */
7226 tgl 5426 GIC 174265 : oldcontext = MemoryContextSwitchTo(rcontext);
5427 :
7226 tgl 5428 CBC 174265 : result = construct_md_array(astate->dvalues,
5429 : astate->dnulls,
5430 : ndims,
5431 : dims,
5432 : lbs,
5433 : astate->element_type,
7226 tgl 5434 GIC 174265 : astate->typlen,
5435 174265 : astate->typbyval,
5436 174265 : astate->typalign);
5437 :
5438 174265 : MemoryContextSwitchTo(oldcontext);
5439 :
5440 : /* Clean up all the junk */
5215 5441 174265 : if (release)
5442 : {
2969 jdavis 5443 123600 : Assert(astate->private_cxt);
5215 tgl 5444 123600 : MemoryContextDelete(astate->mcontext);
5445 : }
5446 :
7226 5447 174265 : return PointerGetDatum(result);
5448 : }
5449 :
5450 : /*
5451 : * The following three functions provide essentially the same API as
5452 : * initArrayResult/accumArrayResult/makeArrayResult, but instead of accepting
5453 : * inputs that are array elements, they accept inputs that are arrays and
5454 : * produce an output array having N+1 dimensions. The inputs must all have
5455 : * identical dimensionality as well as element type.
5456 : */
5457 :
5458 : /*
5459 : * initArrayResultArr - initialize an empty ArrayBuildStateArr
3057 tgl 5460 ECB : *
5461 : * array_type is the array type (must be a valid varlena array type)
5462 : * element_type is the type of the array's elements (lookup if InvalidOid)
5463 : * rcontext is where to keep working state
5464 : * subcontext is a flag determining whether to use a separate memory context
5465 : */
5466 : ArrayBuildStateArr *
2969 jdavis 5467 CBC 196 : initArrayResultArr(Oid array_type, Oid element_type, MemoryContext rcontext,
5468 : bool subcontext)
5469 : {
5470 : ArrayBuildStateArr *astate;
2118 tgl 5471 GIC 196 : MemoryContext arr_context = rcontext; /* by default use the parent ctx */
5472 :
5473 : /* Lookup element type, unless element_type already provided */
2887 5474 196 : if (!OidIsValid(element_type))
5475 : {
2969 jdavis 5476 146 : element_type = get_element_type(array_type);
2969 jdavis 5477 ECB :
2969 jdavis 5478 GIC 146 : if (!OidIsValid(element_type))
2969 jdavis 5479 UIC 0 : ereport(ERROR,
5480 : (errcode(ERRCODE_DATATYPE_MISMATCH),
2969 jdavis 5481 ECB : errmsg("data type %s is not an array type",
5482 : format_type_be(array_type))));
5483 : }
3057 tgl 5484 :
5485 : /* Make a temporary context to hold all the junk */
2969 jdavis 5486 GIC 196 : if (subcontext)
5487 6 : arr_context = AllocSetContextCreate(rcontext,
5488 : "accumArrayResultArr",
5489 : ALLOCSET_DEFAULT_SIZES);
3057 tgl 5490 ECB :
5491 : /* Note we initialize all fields to zero */
5492 : astate = (ArrayBuildStateArr *)
3057 tgl 5493 CBC 196 : MemoryContextAllocZero(arr_context, sizeof(ArrayBuildStateArr));
5494 196 : astate->mcontext = arr_context;
2969 jdavis 5495 196 : astate->private_cxt = subcontext;
3057 tgl 5496 ECB :
5497 : /* Save relevant datatype information */
3057 tgl 5498 CBC 196 : astate->array_type = array_type;
5499 196 : astate->element_type = element_type;
3057 tgl 5500 ECB :
3057 tgl 5501 GIC 196 : return astate;
5502 : }
5503 :
5504 : /*
3057 tgl 5505 ECB : * accumArrayResultArr - accumulate one (more) sub-array for an array result
5506 : *
5507 : * astate is working state (can be NULL on first call)
5508 : * dvalue/disnull represent the new sub-array to append to the array
5509 : * array_type is the array type (must be a valid varlena array type)
5510 : * rcontext is where to keep working state
5511 : */
5512 : ArrayBuildStateArr *
3057 tgl 5513 GIC 30099 : accumArrayResultArr(ArrayBuildStateArr *astate,
5514 : Datum dvalue, bool disnull,
5515 : Oid array_type,
5516 : MemoryContext rcontext)
3057 tgl 5517 ECB : {
5518 : ArrayType *arg;
5519 : MemoryContext oldcontext;
5520 : int *dims,
5521 : *lbs,
5522 : ndims,
5523 : nitems,
5524 : ndatabytes;
5525 : char *data;
5526 : int i;
5527 :
5528 : /*
5529 : * We disallow accumulating null subarrays. Another plausible definition
5530 : * is to ignore them, but callers that want that can just skip calling
5531 : * this function.
5532 : */
3057 tgl 5533 GIC 30099 : if (disnull)
3057 tgl 5534 CBC 3 : ereport(ERROR,
5535 : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5536 : errmsg("cannot accumulate null arrays")));
3057 tgl 5537 ECB :
5538 : /* Detoast input array in caller's context */
3057 tgl 5539 CBC 30096 : arg = DatumGetArrayTypeP(dvalue);
3057 tgl 5540 ECB :
3057 tgl 5541 CBC 30096 : if (astate == NULL)
2969 jdavis 5542 LBC 0 : astate = initArrayResultArr(array_type, InvalidOid, rcontext, true);
3057 tgl 5543 ECB : else
3057 tgl 5544 GIC 30096 : Assert(astate->array_type == array_type);
5545 :
5546 30096 : oldcontext = MemoryContextSwitchTo(astate->mcontext);
5547 :
5548 : /* Collect this input's dimensions */
5549 30096 : ndims = ARR_NDIM(arg);
5550 30096 : dims = ARR_DIMS(arg);
5551 30096 : lbs = ARR_LBOUND(arg);
5552 30096 : data = ARR_DATA_PTR(arg);
5553 30096 : nitems = ArrayGetNItems(ndims, dims);
3057 tgl 5554 CBC 30096 : ndatabytes = ARR_SIZE(arg) - ARR_DATA_OFFSET(arg);
5555 :
5556 30096 : if (astate->ndims == 0)
3057 tgl 5557 ECB : {
5558 : /* First input; check/save the dimensionality info */
5559 :
5560 : /* Should we allow empty inputs and just produce an empty output? */
3057 tgl 5561 GIC 113 : if (ndims == 0)
3057 tgl 5562 CBC 3 : ereport(ERROR,
3057 tgl 5563 ECB : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5564 : errmsg("cannot accumulate empty arrays")));
3057 tgl 5565 GIC 110 : if (ndims + 1 > MAXDIM)
3057 tgl 5566 LBC 0 : ereport(ERROR,
5567 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3057 tgl 5568 ECB : errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
5569 : ndims + 1, MAXDIM)));
5570 :
5571 : /*
5572 : * The output array will have n+1 dimensions, with the ones after the
5573 : * first matching the input's dimensions.
5574 : */
3057 tgl 5575 GIC 110 : astate->ndims = ndims + 1;
5576 110 : astate->dims[0] = 0;
5577 110 : memcpy(&astate->dims[1], dims, ndims * sizeof(int));
5578 110 : astate->lbs[0] = 1;
5579 110 : memcpy(&astate->lbs[1], lbs, ndims * sizeof(int));
5580 :
3057 tgl 5581 ECB : /* Allocate at least enough data space for this item */
1096 drowley 5582 GIC 110 : astate->abytes = pg_nextpower2_32(Max(1024, ndatabytes + 1));
3057 tgl 5583 110 : astate->data = (char *) palloc(astate->abytes);
5584 : }
5585 : else
5586 : {
5587 : /* Second or later input: must match first input's dimensionality */
5588 29983 : if (astate->ndims != ndims + 1)
3057 tgl 5589 LBC 0 : ereport(ERROR,
3057 tgl 5590 ECB : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2118 5591 : errmsg("cannot accumulate arrays of different dimensionality")));
3057 tgl 5592 GIC 59963 : for (i = 0; i < ndims; i++)
3057 tgl 5593 ECB : {
3057 tgl 5594 CBC 29983 : if (astate->dims[i + 1] != dims[i] || astate->lbs[i + 1] != lbs[i])
3057 tgl 5595 GIC 3 : ereport(ERROR,
5596 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5597 : errmsg("cannot accumulate arrays of different dimensionality")));
5598 : }
5599 :
5600 : /* Enlarge data space if needed */
5601 29980 : if (astate->nbytes + ndatabytes > astate->abytes)
5602 : {
5603 30 : astate->abytes = Max(astate->abytes * 2,
5604 : astate->nbytes + ndatabytes);
5605 30 : astate->data = (char *) repalloc(astate->data, astate->abytes);
5606 : }
5607 : }
5608 :
5609 : /*
5610 : * Copy the data portion of the sub-array. Note we assume that the
5611 : * advertised data length of the sub-array is properly aligned. We do not
5612 : * have to worry about detoasting elements since whatever's in the
3057 tgl 5613 ECB : * sub-array should be OK already.
5614 : */
3057 tgl 5615 GIC 30090 : memcpy(astate->data + astate->nbytes, data, ndatabytes);
5616 30090 : astate->nbytes += ndatabytes;
5617 :
5618 : /* Deal with null bitmap if needed */
5619 30090 : if (astate->nullbitmap || ARR_HASNULL(arg))
5620 : {
5621 14990 : int newnitems = astate->nitems + nitems;
5622 :
5623 14990 : if (astate->nullbitmap == NULL)
3057 tgl 5624 ECB : {
5625 : /*
5626 : * First input with nulls; we must retrospectively handle any
5627 : * previous inputs by marking all their items non-null.
5628 : */
1096 drowley 5629 GIC 43 : astate->aitems = pg_nextpower2_32(Max(256, newnitems + 1));
3057 tgl 5630 43 : astate->nullbitmap = (bits8 *) palloc((astate->aitems + 7) / 8);
5631 43 : array_bitmap_copy(astate->nullbitmap, 0,
3057 tgl 5632 ECB : NULL, 0,
5633 : astate->nitems);
5634 : }
3057 tgl 5635 GIC 14947 : else if (newnitems > astate->aitems)
3057 tgl 5636 ECB : {
3057 tgl 5637 GIC 30 : astate->aitems = Max(astate->aitems * 2, newnitems);
5638 30 : astate->nullbitmap = (bits8 *)
3057 tgl 5639 CBC 30 : repalloc(astate->nullbitmap, (astate->aitems + 7) / 8);
5640 : }
5641 14990 : array_bitmap_copy(astate->nullbitmap, astate->nitems,
5642 14990 : ARR_NULLBITMAP(arg), 0,
5643 : nitems);
5644 : }
3057 tgl 5645 ECB :
3057 tgl 5646 GIC 30090 : astate->nitems += nitems;
5647 30090 : astate->dims[0] += 1;
5648 :
5649 30090 : MemoryContextSwitchTo(oldcontext);
5650 :
5651 : /* Release detoasted copy if any */
5652 30090 : if ((Pointer) arg != DatumGetPointer(dvalue))
5653 27 : pfree(arg);
5654 :
5655 30090 : return astate;
5656 : }
5657 :
5658 : /*
5659 : * makeArrayResultArr - produce N+1-D final result of accumArrayResultArr
5660 : *
5661 : * astate is working state (must not be NULL)
5662 : * rcontext is where to construct result
5663 : * release is true if okay to release working state
5664 : */
3057 tgl 5665 ECB : Datum
3057 tgl 5666 GIC 87 : makeArrayResultArr(ArrayBuildStateArr *astate,
5667 : MemoryContext rcontext,
5668 : bool release)
3057 tgl 5669 ECB : {
5670 : ArrayType *result;
5671 : MemoryContext oldcontext;
5672 :
5673 : /* Build the final array result in rcontext */
3057 tgl 5674 CBC 87 : oldcontext = MemoryContextSwitchTo(rcontext);
5675 :
5676 87 : if (astate->ndims == 0)
3057 tgl 5677 EUB : {
5678 : /* No inputs, return empty array */
3057 tgl 5679 UIC 0 : result = construct_empty_array(astate->element_type);
5680 : }
5681 : else
5682 : {
5683 : int dataoffset,
3057 tgl 5684 ECB : nbytes;
5685 :
5686 : /* Check for overflow of the array dimensions */
699 tgl 5687 GIC 87 : (void) ArrayGetNItems(astate->ndims, astate->dims);
5688 87 : ArrayCheckBounds(astate->ndims, astate->dims, astate->lbs);
5689 :
5690 : /* Compute required space */
3057 tgl 5691 CBC 87 : nbytes = astate->nbytes;
5692 87 : if (astate->nullbitmap != NULL)
3057 tgl 5693 ECB : {
3057 tgl 5694 GIC 33 : dataoffset = ARR_OVERHEAD_WITHNULLS(astate->ndims, astate->nitems);
5695 33 : nbytes += dataoffset;
3057 tgl 5696 ECB : }
5697 : else
5698 : {
3057 tgl 5699 CBC 54 : dataoffset = 0;
3057 tgl 5700 GIC 54 : nbytes += ARR_OVERHEAD_NONULLS(astate->ndims);
5701 : }
5702 :
5703 87 : result = (ArrayType *) palloc0(nbytes);
5704 87 : SET_VARSIZE(result, nbytes);
5705 87 : result->ndim = astate->ndims;
5706 87 : result->dataoffset = dataoffset;
5707 87 : result->elemtype = astate->element_type;
5708 :
5709 87 : memcpy(ARR_DIMS(result), astate->dims, astate->ndims * sizeof(int));
5710 87 : memcpy(ARR_LBOUND(result), astate->lbs, astate->ndims * sizeof(int));
3057 tgl 5711 CBC 87 : memcpy(ARR_DATA_PTR(result), astate->data, astate->nbytes);
5712 :
3057 tgl 5713 GIC 87 : if (astate->nullbitmap != NULL)
5714 33 : array_bitmap_copy(ARR_NULLBITMAP(result), 0,
5715 33 : astate->nullbitmap, 0,
5716 : astate->nitems);
5717 : }
5718 :
5719 87 : MemoryContextSwitchTo(oldcontext);
5720 :
5721 : /* Clean up all the junk */
5722 87 : if (release)
5723 : {
2969 jdavis 5724 6 : Assert(astate->private_cxt);
3057 tgl 5725 6 : MemoryContextDelete(astate->mcontext);
5726 : }
5727 :
5728 87 : return PointerGetDatum(result);
5729 : }
5730 :
3057 tgl 5731 ECB : /*
5732 : * The following three functions provide essentially the same API as
5733 : * initArrayResult/accumArrayResult/makeArrayResult, but can accept either
5734 : * scalar or array inputs, invoking the appropriate set of functions above.
5735 : */
5736 :
5737 : /*
5738 : * initArrayResultAny - initialize an empty ArrayBuildStateAny
5739 : *
3057 tgl 5740 EUB : * input_type is the input datatype (either element or array type)
5741 : * rcontext is where to keep working state
2969 jdavis 5742 ECB : * subcontext is a flag determining whether to use a separate memory context
5743 : */
3057 tgl 5744 : ArrayBuildStateAny *
2969 jdavis 5745 GIC 19975 : initArrayResultAny(Oid input_type, MemoryContext rcontext, bool subcontext)
5746 : {
3057 tgl 5747 ECB : ArrayBuildStateAny *astate;
3057 tgl 5748 CBC 19975 : Oid element_type = get_element_type(input_type);
3057 tgl 5749 ECB :
3057 tgl 5750 CBC 19975 : if (OidIsValid(element_type))
3057 tgl 5751 ECB : {
5752 : /* Array case */
5753 : ArrayBuildStateArr *arraystate;
5754 :
2969 jdavis 5755 GIC 6 : arraystate = initArrayResultArr(input_type, InvalidOid, rcontext, subcontext);
5756 : astate = (ArrayBuildStateAny *)
3057 tgl 5757 6 : MemoryContextAlloc(arraystate->mcontext,
5758 : sizeof(ArrayBuildStateAny));
3057 tgl 5759 CBC 6 : astate->scalarstate = NULL;
5760 6 : astate->arraystate = arraystate;
5761 : }
5762 : else
3057 tgl 5763 ECB : {
3057 tgl 5764 EUB : /* Scalar case */
5765 : ArrayBuildState *scalarstate;
5766 :
5767 : /* Let's just check that we have a type that can be put into arrays */
3057 tgl 5768 GIC 19969 : Assert(OidIsValid(get_array_type(input_type)));
5769 :
2969 jdavis 5770 19969 : scalarstate = initArrayResult(input_type, rcontext, subcontext);
5771 : astate = (ArrayBuildStateAny *)
3057 tgl 5772 19969 : MemoryContextAlloc(scalarstate->mcontext,
3057 tgl 5773 ECB : sizeof(ArrayBuildStateAny));
3057 tgl 5774 CBC 19969 : astate->scalarstate = scalarstate;
5775 19969 : astate->arraystate = NULL;
3057 tgl 5776 ECB : }
5777 :
3057 tgl 5778 GIC 19975 : return astate;
5779 : }
3057 tgl 5780 ECB :
5781 : /*
5782 : * accumArrayResultAny - accumulate one (more) input for an array result
5783 : *
5784 : * astate is working state (can be NULL on first call)
5785 : * dvalue/disnull represent the new input to append to the array
5786 : * input_type is the input datatype (either element or array type)
3057 tgl 5787 EUB : * rcontext is where to keep working state
5788 : */
5789 : ArrayBuildStateAny *
3057 tgl 5790 CBC 7518 : accumArrayResultAny(ArrayBuildStateAny *astate,
5791 : Datum dvalue, bool disnull,
3057 tgl 5792 ECB : Oid input_type,
5793 : MemoryContext rcontext)
5794 : {
3057 tgl 5795 GIC 7518 : if (astate == NULL)
2969 jdavis 5796 UIC 0 : astate = initArrayResultAny(input_type, rcontext, true);
5797 :
3057 tgl 5798 GIC 7518 : if (astate->scalarstate)
3057 tgl 5799 CBC 7494 : (void) accumArrayResult(astate->scalarstate,
5800 : dvalue, disnull,
3057 tgl 5801 ECB : input_type, rcontext);
5802 : else
3057 tgl 5803 CBC 24 : (void) accumArrayResultArr(astate->arraystate,
5804 : dvalue, disnull,
5805 : input_type, rcontext);
5806 :
3057 tgl 5807 GIC 7518 : return astate;
5808 : }
5809 :
5810 : /*
5811 : * makeArrayResultAny - produce final result of accumArrayResultAny
5812 : *
3057 tgl 5813 ECB : * astate is working state (must not be NULL)
5814 : * rcontext is where to construct result
5815 : * release is true if okay to release working state
5816 : */
5817 : Datum
3057 tgl 5818 GIC 19975 : makeArrayResultAny(ArrayBuildStateAny *astate,
3057 tgl 5819 ECB : MemoryContext rcontext, bool release)
5820 : {
5821 : Datum result;
5822 :
3057 tgl 5823 GIC 19975 : if (astate->scalarstate)
5824 : {
5825 : /* Must use makeMdArrayResult to support "release" parameter */
5826 : int ndims;
3057 tgl 5827 ECB : int dims[1];
5828 : int lbs[1];
5829 :
5830 : /* If no elements were presented, we want to create an empty array */
3057 tgl 5831 GIC 19969 : ndims = (astate->scalarstate->nelems > 0) ? 1 : 0;
5832 19969 : dims[0] = astate->scalarstate->nelems;
3057 tgl 5833 CBC 19969 : lbs[0] = 1;
5834 :
5835 19969 : result = makeMdArrayResult(astate->scalarstate, ndims, dims, lbs,
3057 tgl 5836 ECB : rcontext, release);
5837 : }
5838 : else
5839 : {
3057 tgl 5840 CBC 6 : result = makeArrayResultArr(astate->arraystate,
5841 : rcontext, release);
5842 : }
3057 tgl 5843 GIC 19975 : return result;
3057 tgl 5844 ECB : }
5845 :
5846 :
6614 neilc 5847 : Datum
6614 neilc 5848 GIC 144 : array_larger(PG_FUNCTION_ARGS)
5849 : {
2887 tgl 5850 CBC 144 : if (array_cmp(fcinfo) > 0)
5851 72 : PG_RETURN_DATUM(PG_GETARG_DATUM(0));
5852 : else
5853 69 : PG_RETURN_DATUM(PG_GETARG_DATUM(1));
5854 : }
5855 :
5856 : Datum
6614 neilc 5857 GIC 129 : array_smaller(PG_FUNCTION_ARGS)
5858 : {
2887 tgl 5859 129 : if (array_cmp(fcinfo) < 0)
5860 87 : PG_RETURN_DATUM(PG_GETARG_DATUM(0));
5861 : else
5862 42 : PG_RETURN_DATUM(PG_GETARG_DATUM(1));
5863 : }
5459 alvherre 5864 ECB :
5865 :
5866 : typedef struct generate_subscripts_fctx
5867 : {
5868 : int32 lower;
5869 : int32 upper;
5870 : bool reverse;
5871 : } generate_subscripts_fctx;
5872 :
5873 : /*
5874 : * generate_subscripts(array anyarray, dim int [, reverse bool])
5875 : * Returns all subscripts of the array for any dimension
5876 : */
5459 alvherre 5877 EUB : Datum
5459 alvherre 5878 GIC 2435 : generate_subscripts(PG_FUNCTION_ARGS)
5879 : {
5880 : FuncCallContext *funcctx;
5881 : MemoryContext oldcontext;
5882 : generate_subscripts_fctx *fctx;
5883 :
5884 : /* stuff done only on the first call of the function */
5459 alvherre 5885 CBC 2435 : if (SRF_IS_FIRSTCALL())
5459 alvherre 5886 ECB : {
2029 tgl 5887 GIC 1138 : AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
5050 bruce 5888 1138 : int reqdim = PG_GETARG_INT32(1);
5050 bruce 5889 ECB : int *lb,
5890 : *dimv;
5891 :
5459 alvherre 5892 : /* create a function context for cross-call persistence */
5459 alvherre 5893 CBC 1138 : funcctx = SRF_FIRSTCALL_INIT();
5894 :
5895 : /* Sanity check: does it look like an array at all? */
2887 tgl 5896 GIC 1138 : if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
5459 alvherre 5897 CBC 3 : SRF_RETURN_DONE(funcctx);
5459 alvherre 5898 ECB :
5899 : /* Sanity check: was the requested dim valid */
2887 tgl 5900 GIC 1135 : if (reqdim <= 0 || reqdim > AARR_NDIM(v))
5459 alvherre 5901 LBC 0 : SRF_RETURN_DONE(funcctx);
5459 alvherre 5902 ECB :
5903 : /*
5904 : * switch to memory context appropriate for multiple function calls
5905 : */
5459 alvherre 5906 GIC 1135 : oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
5459 alvherre 5907 CBC 1135 : fctx = (generate_subscripts_fctx *) palloc(sizeof(generate_subscripts_fctx));
5459 alvherre 5908 ECB :
2887 tgl 5909 CBC 1135 : lb = AARR_LBOUND(v);
2887 tgl 5910 GIC 1135 : dimv = AARR_DIMS(v);
5459 alvherre 5911 ECB :
5459 alvherre 5912 CBC 1135 : fctx->lower = lb[reqdim - 1];
5913 1135 : fctx->upper = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
5459 alvherre 5914 GIC 1135 : fctx->reverse = (PG_NARGS() < 3) ? false : PG_GETARG_BOOL(2);
5915 :
5916 1135 : funcctx->user_fctx = fctx;
5459 alvherre 5917 ECB :
5459 alvherre 5918 GIC 1135 : MemoryContextSwitchTo(oldcontext);
5919 : }
5459 alvherre 5920 ECB :
5459 alvherre 5921 GIC 2432 : funcctx = SRF_PERCALL_SETUP();
5459 alvherre 5922 ECB :
5459 alvherre 5923 CBC 2432 : fctx = funcctx->user_fctx;
5924 :
5459 alvherre 5925 GIC 2432 : if (fctx->lower <= fctx->upper)
5459 alvherre 5926 ECB : {
5459 alvherre 5927 GIC 1297 : if (!fctx->reverse)
5928 1297 : SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->lower++));
5929 : else
5459 alvherre 5930 UIC 0 : SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->upper--));
5931 : }
5932 : else
5933 : /* done when there are no more elements left */
5459 alvherre 5934 GIC 1135 : SRF_RETURN_DONE(funcctx);
5935 : }
5936 :
5937 : /*
5938 : * generate_subscripts_nodir
5939 : * Implements the 2-argument version of generate_subscripts
5940 : */
5941 : Datum
5942 2435 : generate_subscripts_nodir(PG_FUNCTION_ARGS)
5459 alvherre 5943 ECB : {
5944 : /* just call the other one -- it can handle both cases */
5459 alvherre 5945 GIC 2435 : return generate_subscripts(fcinfo);
5459 alvherre 5946 ECB : }
5947 :
5380 bruce 5948 : /*
5949 : * array_fill_with_lower_bounds
5950 : * Create and fill array with defined lower bounds.
5951 : */
5952 : Datum
5380 bruce 5953 CBC 33 : array_fill_with_lower_bounds(PG_FUNCTION_ARGS)
5954 : {
5050 bruce 5955 ECB : ArrayType *dims;
5956 : ArrayType *lbs;
5957 : ArrayType *result;
5380 5958 : Oid elmtype;
5959 : Datum value;
5960 : bool isnull;
5961 :
5380 bruce 5962 GIC 33 : if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
5375 tgl 5963 6 : ereport(ERROR,
5964 : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5965 : errmsg("dimension array or low bound array cannot be null")));
5380 bruce 5966 ECB :
5380 bruce 5967 GIC 27 : dims = PG_GETARG_ARRAYTYPE_P(1);
5050 bruce 5968 CBC 27 : lbs = PG_GETARG_ARRAYTYPE_P(2);
5969 :
5380 5970 27 : if (!PG_ARGISNULL(0))
5971 : {
5972 21 : value = PG_GETARG_DATUM(0);
5973 21 : isnull = false;
5974 : }
5975 : else
5380 bruce 5976 ECB : {
5380 bruce 5977 GIC 6 : value = 0;
5978 6 : isnull = true;
5979 : }
5980 :
5375 tgl 5981 27 : elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
5982 27 : if (!OidIsValid(elmtype))
5375 tgl 5983 UIC 0 : elog(ERROR, "could not determine data type of input");
5984 :
5375 tgl 5985 GIC 27 : result = array_fill_internal(dims, lbs, value, isnull, elmtype, fcinfo);
5380 bruce 5986 21 : PG_RETURN_ARRAYTYPE_P(result);
5987 : }
5380 bruce 5988 ECB :
5989 : /*
5990 : * array_fill
5991 : * Create and fill array with default lower bounds.
5992 : */
5993 : Datum
5380 bruce 5994 GBC 36 : array_fill(PG_FUNCTION_ARGS)
5995 : {
5050 bruce 5996 ECB : ArrayType *dims;
5997 : ArrayType *result;
5998 : Oid elmtype;
5999 : Datum value;
6000 : bool isnull;
5380 6001 :
5380 bruce 6002 GIC 36 : if (PG_ARGISNULL(1))
5375 tgl 6003 UIC 0 : ereport(ERROR,
6004 : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
2118 tgl 6005 ECB : errmsg("dimension array or low bound array cannot be null")));
6006 :
5380 bruce 6007 GIC 36 : dims = PG_GETARG_ARRAYTYPE_P(1);
6008 :
6009 36 : if (!PG_ARGISNULL(0))
6010 : {
6011 30 : value = PG_GETARG_DATUM(0);
6012 30 : isnull = false;
6013 : }
6014 : else
6015 : {
5380 bruce 6016 CBC 6 : value = 0;
5380 bruce 6017 GIC 6 : isnull = true;
6018 : }
6019 :
5375 tgl 6020 36 : elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
5375 tgl 6021 CBC 36 : if (!OidIsValid(elmtype))
5375 tgl 6022 UIC 0 : elog(ERROR, "could not determine data type of input");
6023 :
5375 tgl 6024 GIC 36 : result = array_fill_internal(dims, NULL, value, isnull, elmtype, fcinfo);
5380 bruce 6025 30 : PG_RETURN_ARRAYTYPE_P(result);
6026 : }
6027 :
6028 : static ArrayType *
5380 bruce 6029 CBC 24 : create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
5375 tgl 6030 ECB : Oid elmtype, int dataoffset)
5380 bruce 6031 : {
6032 : ArrayType *result;
6033 :
5380 bruce 6034 GIC 24 : result = (ArrayType *) palloc0(nbytes);
6035 24 : SET_VARSIZE(result, nbytes);
6036 24 : result->ndim = ndims;
6037 24 : result->dataoffset = dataoffset;
5380 bruce 6038 CBC 24 : result->elemtype = elmtype;
5380 bruce 6039 GIC 24 : memcpy(ARR_DIMS(result), dimv, ndims * sizeof(int));
6040 24 : memcpy(ARR_LBOUND(result), lbsv, ndims * sizeof(int));
5380 bruce 6041 ECB :
5380 bruce 6042 GIC 24 : return result;
6043 : }
6044 :
6045 : static ArrayType *
5375 tgl 6046 CBC 63 : array_fill_internal(ArrayType *dims, ArrayType *lbs,
6047 : Datum value, bool isnull, Oid elmtype,
5375 tgl 6048 ECB : FunctionCallInfo fcinfo)
5380 bruce 6049 : {
6050 : ArrayType *result;
5050 6051 : int *dimv;
6052 : int *lbsv;
6053 : int ndims;
6054 : int nitems;
6055 : int deflbs[MAXDIM];
6056 : int16 elmlen;
6057 : bool elmbyval;
6058 : char elmalign;
6059 : ArrayMetaState *my_extra;
5380 6060 :
6061 : /*
6062 : * Params checks
6063 : */
2285 tgl 6064 GIC 63 : if (ARR_NDIM(dims) > 1)
5380 bruce 6065 3 : ereport(ERROR,
6066 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
6067 : errmsg("wrong number of array subscripts"),
6068 : errdetail("Dimension array must be one dimensional.")));
6069 :
4473 tgl 6070 60 : if (array_contains_nulls(dims))
5375 6071 3 : ereport(ERROR,
6072 : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
6073 : errmsg("dimension values cannot be null")));
6074 :
5380 bruce 6075 57 : dimv = (int *) ARR_DATA_PTR(dims);
2285 tgl 6076 CBC 57 : ndims = (ARR_NDIM(dims) > 0) ? ARR_DIMS(dims)[0] : 0;
6077 :
5380 bruce 6078 GIC 57 : if (ndims < 0) /* we do allow zero-dimension arrays */
5380 bruce 6079 UIC 0 : ereport(ERROR,
6080 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
6081 : errmsg("invalid number of dimensions: %d", ndims)));
5380 bruce 6082 GIC 57 : if (ndims > MAXDIM)
5380 bruce 6083 LBC 0 : ereport(ERROR,
6084 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
5380 bruce 6085 ECB : errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
6086 : ndims, MAXDIM)));
6087 :
5380 bruce 6088 GIC 57 : if (lbs != NULL)
6089 : {
2285 tgl 6090 27 : if (ARR_NDIM(lbs) > 1)
5380 bruce 6091 LBC 0 : ereport(ERROR,
6092 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
6093 : errmsg("wrong number of array subscripts"),
5050 bruce 6094 ECB : errdetail("Dimension array must be one dimensional.")));
5380 6095 :
4473 tgl 6096 GIC 27 : if (array_contains_nulls(lbs))
5375 tgl 6097 UIC 0 : ereport(ERROR,
5050 bruce 6098 ECB : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5050 bruce 6099 EUB : errmsg("dimension values cannot be null")));
6100 :
2285 tgl 6101 GIC 27 : if (ndims != ((ARR_NDIM(lbs) > 0) ? ARR_DIMS(lbs)[0] : 0))
5380 bruce 6102 6 : ereport(ERROR,
6103 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5050 bruce 6104 ECB : errmsg("wrong number of array subscripts"),
6105 : errdetail("Low bound array has different size than dimensions array.")));
6106 :
5380 bruce 6107 CBC 21 : lbsv = (int *) ARR_DATA_PTR(lbs);
5380 bruce 6108 ECB : }
6109 : else
6110 : {
5050 6111 : int i;
5375 tgl 6112 :
5380 bruce 6113 GIC 210 : for (i = 0; i < MAXDIM; i++)
5380 bruce 6114 CBC 180 : deflbs[i] = 1;
6115 :
6116 30 : lbsv = deflbs;
6117 : }
6118 :
699 tgl 6119 ECB : /* This checks for overflow of the array dimensions */
2285 tgl 6120 GIC 51 : nitems = ArrayGetNItems(ndims, dimv);
699 tgl 6121 CBC 51 : ArrayCheckBounds(ndims, dimv, lbsv);
6122 :
5380 bruce 6123 ECB : /* fast track for empty array */
2285 tgl 6124 GIC 51 : if (nitems <= 0)
5380 bruce 6125 CBC 27 : return construct_empty_array(elmtype);
5380 bruce 6126 ECB :
6127 : /*
5380 bruce 6128 EUB : * We arrange to look up info about element type only once per series of
6129 : * calls, assuming the element type doesn't change underneath us.
6130 : */
5380 bruce 6131 GIC 24 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
5380 bruce 6132 CBC 24 : if (my_extra == NULL)
6133 : {
5380 bruce 6134 GIC 24 : fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
6135 : sizeof(ArrayMetaState));
6136 24 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
6137 24 : my_extra->element_type = InvalidOid;
6138 : }
6139 :
5380 bruce 6140 CBC 24 : if (my_extra->element_type != elmtype)
6141 : {
6142 : /* Get info about element type */
6143 24 : get_typlenbyvalalign(elmtype,
6144 : &my_extra->typlen,
6145 : &my_extra->typbyval,
6146 : &my_extra->typalign);
5380 bruce 6147 GIC 24 : my_extra->element_type = elmtype;
6148 : }
6149 :
6150 24 : elmlen = my_extra->typlen;
5380 bruce 6151 CBC 24 : elmbyval = my_extra->typbyval;
5380 bruce 6152 GIC 24 : elmalign = my_extra->typalign;
6153 :
6154 : /* compute required space */
6155 24 : if (!isnull)
6156 : {
6157 : int i;
6158 : char *p;
6159 : int nbytes;
5375 tgl 6160 ECB : int totbytes;
5380 bruce 6161 :
6162 : /* make sure data is not toasted */
5380 bruce 6163 GIC 12 : if (elmlen == -1)
6164 6 : value = PointerGetDatum(PG_DETOAST_DATUM(value));
5380 bruce 6165 ECB :
5380 bruce 6166 CBC 12 : nbytes = att_addlength_datum(0, elmlen, value);
5380 bruce 6167 GIC 12 : nbytes = att_align_nominal(nbytes, elmalign);
5375 tgl 6168 CBC 12 : Assert(nbytes > 0);
6169 :
6170 12 : totbytes = nbytes * nitems;
5375 tgl 6171 ECB :
6172 : /* check for overflow of multiplication or total request */
5375 tgl 6173 GIC 12 : if (totbytes / nbytes != nitems ||
6174 12 : !AllocSizeIsValid(totbytes))
5380 bruce 6175 LBC 0 : ereport(ERROR,
5380 bruce 6176 ECB : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
6177 : errmsg("array size exceeds the maximum allowed (%d)",
6178 : (int) MaxAllocSize)));
6179 :
5375 tgl 6180 : /*
5375 tgl 6181 EUB : * This addition can't overflow, but it might cause us to go past
6182 : * MaxAllocSize. We leave it to palloc to complain in that case.
5375 tgl 6183 ECB : */
5375 tgl 6184 CBC 12 : totbytes += ARR_OVERHEAD_NONULLS(ndims);
6185 :
5375 tgl 6186 GIC 12 : result = create_array_envelope(ndims, dimv, lbsv, totbytes,
6187 : elmtype, 0);
6188 :
5380 bruce 6189 12 : p = ARR_DATA_PTR(result);
6190 120 : for (i = 0; i < nitems; i++)
6191 108 : p += ArrayCastAndSet(value, elmlen, elmbyval, elmalign, p);
5380 bruce 6192 ECB : }
6193 : else
6194 : {
6195 : int nbytes;
6196 : int dataoffset;
6197 :
5380 bruce 6198 GIC 12 : dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
6199 12 : nbytes = dataoffset;
5380 bruce 6200 ECB :
5380 bruce 6201 GBC 12 : result = create_array_envelope(ndims, dimv, lbsv, nbytes,
6202 : elmtype, dataoffset);
6203 :
6204 : /* create_array_envelope already zeroed the bitmap, so we're done */
5380 bruce 6205 ECB : }
6206 :
5380 bruce 6207 CBC 24 : return result;
6208 : }
5259 tgl 6209 ECB :
6210 :
6211 : /*
6212 : * UNNEST
6213 : */
6214 : Datum
5259 tgl 6215 CBC 281112 : array_unnest(PG_FUNCTION_ARGS)
6216 : {
6217 : typedef struct
5259 tgl 6218 ECB : {
2887 6219 : array_iter iter;
5050 bruce 6220 EUB : int nextelem;
6221 : int numelems;
5050 bruce 6222 ECB : int16 elmlen;
6223 : bool elmbyval;
6224 : char elmalign;
6225 : } array_unnest_fctx;
6226 :
5259 tgl 6227 : FuncCallContext *funcctx;
6228 : array_unnest_fctx *fctx;
6229 : MemoryContext oldcontext;
6230 :
6231 : /* stuff done only on the first call of the function */
5259 tgl 6232 CBC 281112 : if (SRF_IS_FIRSTCALL())
5259 tgl 6233 ECB : {
2887 6234 : AnyArrayType *arr;
5259 6235 :
6236 : /* create a function context for cross-call persistence */
5259 tgl 6237 CBC 65838 : funcctx = SRF_FIRSTCALL_INIT();
5259 tgl 6238 ECB :
6239 : /*
6240 : * switch to memory context appropriate for multiple function calls
6241 : */
5259 tgl 6242 GIC 65838 : oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
6243 :
5182 tgl 6244 ECB : /*
6245 : * Get the array value and detoast if needed. We can't do this
6246 : * earlier because if we have to detoast, we want the detoasted copy
6247 : * to be in multi_call_memory_ctx, so it will go away when we're done
6248 : * and not before. (If no detoast happens, we assume the originally
6249 : * passed array will stick around till then.)
6250 : */
2029 tgl 6251 GIC 65838 : arr = PG_GETARG_ANY_ARRAY_P(0);
6252 :
6253 : /* allocate memory for user context */
5259 6254 65838 : fctx = (array_unnest_fctx *) palloc(sizeof(array_unnest_fctx));
6255 :
6256 : /* initialize state */
2887 6257 65838 : array_iter_setup(&fctx->iter, arr);
5259 6258 65838 : fctx->nextelem = 0;
2887 6259 65838 : fctx->numelems = ArrayGetNItems(AARR_NDIM(arr), AARR_DIMS(arr));
6260 :
6261 65838 : if (VARATT_IS_EXPANDED_HEADER(arr))
2887 tgl 6262 ECB : {
6263 : /* we can just grab the type data from expanded array */
2887 tgl 6264 UIC 0 : fctx->elmlen = arr->xpn.typlen;
6265 0 : fctx->elmbyval = arr->xpn.typbyval;
6266 0 : fctx->elmalign = arr->xpn.typalign;
6267 : }
2887 tgl 6268 ECB : else
2887 tgl 6269 CBC 65838 : get_typlenbyvalalign(AARR_ELEMTYPE(arr),
6270 : &fctx->elmlen,
6271 : &fctx->elmbyval,
6272 : &fctx->elmalign);
5259 tgl 6273 ECB :
5259 tgl 6274 CBC 65838 : funcctx->user_fctx = fctx;
5259 tgl 6275 GIC 65838 : MemoryContextSwitchTo(oldcontext);
5259 tgl 6276 ECB : }
5259 tgl 6277 EUB :
6278 : /* stuff done on every call of the function */
5259 tgl 6279 GIC 281112 : funcctx = SRF_PERCALL_SETUP();
5259 tgl 6280 CBC 281112 : fctx = funcctx->user_fctx;
5259 tgl 6281 EUB :
5259 tgl 6282 GIC 281112 : if (fctx->nextelem < fctx->numelems)
6283 : {
5050 bruce 6284 215274 : int offset = fctx->nextelem++;
6285 : Datum elem;
5259 tgl 6286 ECB :
2887 tgl 6287 GIC 215274 : elem = array_iter_next(&fctx->iter, &fcinfo->isnull, offset,
2887 tgl 6288 CBC 215274 : fctx->elmlen, fctx->elmbyval, fctx->elmalign);
5259 tgl 6289 EUB :
5259 tgl 6290 GIC 215274 : SRF_RETURN_NEXT(funcctx, elem);
6291 : }
6292 : else
6293 : {
5259 tgl 6294 ECB : /* do when there is no more left */
5259 tgl 6295 GBC 65838 : SRF_RETURN_DONE(funcctx);
6296 : }
6297 : }
6298 :
1520 tgl 6299 ECB : /*
6300 : * Planner support function for array_unnest(anyarray)
6301 : */
6302 : Datum
1520 tgl 6303 GIC 7896 : array_unnest_support(PG_FUNCTION_ARGS)
6304 : {
1520 tgl 6305 CBC 7896 : Node *rawreq = (Node *) PG_GETARG_POINTER(0);
1520 tgl 6306 GIC 7896 : Node *ret = NULL;
6307 :
6308 7896 : if (IsA(rawreq, SupportRequestRows))
6309 : {
6310 : /* Try to estimate the number of rows returned */
1520 tgl 6311 CBC 2614 : SupportRequestRows *req = (SupportRequestRows *) rawreq;
1520 tgl 6312 ECB :
1520 tgl 6313 GIC 2614 : if (is_funcclause(req->node)) /* be paranoid */
1520 tgl 6314 ECB : {
1520 tgl 6315 GIC 2611 : List *args = ((FuncExpr *) req->node)->args;
6316 : Node *arg1;
6317 :
1520 tgl 6318 ECB : /* We can use estimated argument values here */
1520 tgl 6319 CBC 2611 : arg1 = estimate_expression_value(req->root, linitial(args));
6320 :
1520 tgl 6321 GIC 2611 : req->rows = estimate_array_length(arg1);
1520 tgl 6322 CBC 2611 : ret = (Node *) req;
1520 tgl 6323 ECB : }
6324 : }
6325 :
1520 tgl 6326 GIC 7896 : PG_RETURN_POINTER(ret);
6327 : }
6328 :
3924 tgl 6329 ECB :
6330 : /*
6331 : * array_replace/array_remove support
6332 : *
6333 : * Find all array entries matching (not distinct from) search/search_isnull,
6334 : * and delete them if remove is true, else replace them with
3260 bruce 6335 : * replace/replace_isnull. Comparisons are done using the specified
6336 : * collation. fcinfo is passed only for caching purposes.
6337 : */
3924 tgl 6338 : static ArrayType *
3924 tgl 6339 GIC 1197 : array_replace_internal(ArrayType *array,
6340 : Datum search, bool search_isnull,
3924 tgl 6341 ECB : Datum replace, bool replace_isnull,
6342 : bool remove, Oid collation,
6343 : FunctionCallInfo fcinfo)
6344 : {
1534 andres 6345 CBC 1197 : LOCAL_FCINFO(locfcinfo, 2);
6346 : ArrayType *result;
6347 : Oid element_type;
3924 tgl 6348 ECB : Datum *values;
6349 : bool *nulls;
6350 : int *dim;
6351 : int ndim;
6352 : int nitems,
6353 : nresult;
6354 : int i;
3924 tgl 6355 GIC 1197 : int32 nbytes = 0;
6356 : int32 dataoffset;
6357 : bool hasnulls;
6358 : int typlen;
6359 : bool typbyval;
6360 : char typalign;
3924 tgl 6361 ECB : char *arraydataptr;
6362 : bits8 *bitmap;
6363 : int bitmask;
3924 tgl 6364 CBC 1197 : bool changed = false;
3924 tgl 6365 ECB : TypeCacheEntry *typentry;
6366 :
3924 tgl 6367 GIC 1197 : element_type = ARR_ELEMTYPE(array);
3924 tgl 6368 CBC 1197 : ndim = ARR_NDIM(array);
3924 tgl 6369 GIC 1197 : dim = ARR_DIMS(array);
6370 1197 : nitems = ArrayGetNItems(ndim, dim);
3924 tgl 6371 ECB :
6372 : /* Return input array unmodified if it is empty */
3924 tgl 6373 GBC 1197 : if (nitems <= 0)
3924 tgl 6374 UIC 0 : return array;
6375 :
6376 : /*
6377 : * We can't remove elements from multi-dimensional arrays, since the
6378 : * result might not be rectangular.
6379 : */
3924 tgl 6380 GIC 1197 : if (remove && ndim > 1)
6381 3 : ereport(ERROR,
3924 tgl 6382 ECB : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6383 : errmsg("removing elements from multidimensional arrays is not supported")));
6384 :
6385 : /*
6386 : * We arrange to look up the equality function only once per series of
6387 : * calls, assuming the element type doesn't change underneath us.
6388 : */
3924 tgl 6389 CBC 1194 : typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3924 tgl 6390 GIC 1194 : if (typentry == NULL ||
6391 920 : typentry->type_id != element_type)
6392 : {
6393 274 : typentry = lookup_type_cache(element_type,
6394 : TYPECACHE_EQ_OPR_FINFO);
6395 274 : if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
3924 tgl 6396 LBC 0 : ereport(ERROR,
3924 tgl 6397 ECB : (errcode(ERRCODE_UNDEFINED_FUNCTION),
6398 : errmsg("could not identify an equality operator for type %s",
2118 6399 : format_type_be(element_type))));
3924 tgl 6400 GIC 274 : fcinfo->flinfo->fn_extra = (void *) typentry;
6401 : }
6402 1194 : typlen = typentry->typlen;
6403 1194 : typbyval = typentry->typbyval;
6404 1194 : typalign = typentry->typalign;
3924 tgl 6405 ECB :
6406 : /*
6407 : * Detoast values if they are toasted. The replacement value must be
6408 : * detoasted for insertion into the result array, while detoasting the
6409 : * search value only once saves cycles.
6410 : */
3924 tgl 6411 GIC 1194 : if (typlen == -1)
6412 : {
3924 tgl 6413 CBC 1173 : if (!search_isnull)
3924 tgl 6414 GIC 1170 : search = PointerGetDatum(PG_DETOAST_DATUM(search));
6415 1173 : if (!replace_isnull)
6416 6 : replace = PointerGetDatum(PG_DETOAST_DATUM(replace));
6417 : }
6418 :
6419 : /* Prepare to apply the comparison operator */
1534 andres 6420 1194 : InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
6421 : collation, NULL, NULL);
6422 :
6423 : /* Allocate temporary arrays for new values */
3924 tgl 6424 1194 : values = (Datum *) palloc(nitems * sizeof(Datum));
6425 1194 : nulls = (bool *) palloc(nitems * sizeof(bool));
6426 :
6427 : /* Loop over source data */
6428 1194 : arraydataptr = ARR_DATA_PTR(array);
6429 1194 : bitmap = ARR_NULLBITMAP(array);
3924 tgl 6430 CBC 1194 : bitmask = 1;
3924 tgl 6431 GIC 1194 : hasnulls = false;
6432 1194 : nresult = 0;
6433 :
6434 2678 : for (i = 0; i < nitems; i++)
3924 tgl 6435 ECB : {
6436 : Datum elt;
6437 : bool isNull;
6438 : bool oprresult;
3924 tgl 6439 GIC 1484 : bool skip = false;
3924 tgl 6440 ECB :
6441 : /* Get source element, checking for NULL */
3924 tgl 6442 GIC 1484 : if (bitmap && (*bitmap & bitmask) == 0)
6443 : {
6444 18 : isNull = true;
6445 : /* If searching for NULL, we have a match */
6446 18 : if (search_isnull)
6447 : {
6448 18 : if (remove)
3924 tgl 6449 ECB : {
3924 tgl 6450 GIC 6 : skip = true;
6451 6 : changed = true;
3924 tgl 6452 ECB : }
3924 tgl 6453 GIC 12 : else if (!replace_isnull)
6454 : {
3924 tgl 6455 CBC 9 : values[nresult] = replace;
6456 9 : isNull = false;
6457 9 : changed = true;
6458 : }
3924 tgl 6459 ECB : }
6460 : }
6461 : else
3924 tgl 6462 EUB : {
3924 tgl 6463 GBC 1466 : isNull = false;
6464 1466 : elt = fetch_att(arraydataptr, typbyval, typlen);
3924 tgl 6465 GIC 1466 : arraydataptr = att_addlength_datum(arraydataptr, typlen, elt);
6466 1466 : arraydataptr = (char *) att_align_nominal(arraydataptr, typalign);
3924 tgl 6467 ECB :
3924 tgl 6468 GIC 1466 : if (search_isnull)
6469 : {
6470 : /* no match possible, keep element */
6471 27 : values[nresult] = elt;
3924 tgl 6472 ECB : }
6473 : else
6474 : {
6475 : /*
6476 : * Apply the operator to the element pair; treat NULL as false
6477 : */
1534 andres 6478 CBC 1439 : locfcinfo->args[0].value = elt;
1534 andres 6479 GIC 1439 : locfcinfo->args[0].isnull = false;
1534 andres 6480 CBC 1439 : locfcinfo->args[1].value = search;
1534 andres 6481 GIC 1439 : locfcinfo->args[1].isnull = false;
1534 andres 6482 CBC 1439 : locfcinfo->isnull = false;
1534 andres 6483 GIC 1439 : oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
1083 tgl 6484 1439 : if (locfcinfo->isnull || !oprresult)
3924 tgl 6485 ECB : {
6486 : /* no match, keep element */
3924 tgl 6487 GIC 1360 : values[nresult] = elt;
3924 tgl 6488 ECB : }
6489 : else
6490 : {
6491 : /* match, so replace or delete */
3924 tgl 6492 GIC 79 : changed = true;
3924 tgl 6493 CBC 79 : if (remove)
3924 tgl 6494 GIC 67 : skip = true;
6495 : else
6496 : {
6497 12 : values[nresult] = replace;
6498 12 : isNull = replace_isnull;
6499 : }
6500 : }
3924 tgl 6501 ECB : }
6502 : }
6503 :
3924 tgl 6504 CBC 1484 : if (!skip)
6505 : {
6506 1411 : nulls[nresult] = isNull;
3924 tgl 6507 GIC 1411 : if (isNull)
6508 6 : hasnulls = true;
3924 tgl 6509 ECB : else
6510 : {
6511 : /* Update total result size */
3924 tgl 6512 GIC 1405 : nbytes = att_addlength_datum(nbytes, typlen, values[nresult]);
3924 tgl 6513 CBC 1405 : nbytes = att_align_nominal(nbytes, typalign);
6514 : /* check for overflow of total request */
3924 tgl 6515 GIC 1405 : if (!AllocSizeIsValid(nbytes))
3924 tgl 6516 UIC 0 : ereport(ERROR,
3924 tgl 6517 ECB : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
6518 : errmsg("array size exceeds the maximum allowed (%d)",
2118 6519 : (int) MaxAllocSize)));
3924 6520 : }
3924 tgl 6521 GIC 1411 : nresult++;
6522 : }
6523 :
3924 tgl 6524 ECB : /* advance bitmap pointer if any */
3924 tgl 6525 GIC 1484 : if (bitmap)
6526 : {
6527 45 : bitmask <<= 1;
6528 45 : if (bitmask == 0x100)
6529 : {
3924 tgl 6530 UIC 0 : bitmap++;
6531 0 : bitmask = 1;
6532 : }
6533 : }
6534 : }
6535 :
6536 : /*
3924 tgl 6537 ECB : * If not changed just return the original array
6538 : */
3924 tgl 6539 GIC 1194 : if (!changed)
6540 : {
6541 1118 : pfree(values);
6542 1118 : pfree(nulls);
3924 tgl 6543 CBC 1118 : return array;
6544 : }
6545 :
6546 : /* If all elements were removed return an empty array */
3600 noah 6547 GIC 76 : if (nresult == 0)
6548 : {
6549 3 : pfree(values);
6550 3 : pfree(nulls);
6551 3 : return construct_empty_array(element_type);
6552 : }
3600 noah 6553 ECB :
6554 : /* Allocate and initialize the result array */
3924 tgl 6555 GIC 73 : if (hasnulls)
6556 : {
6557 3 : dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nresult);
6558 3 : nbytes += dataoffset;
6559 : }
6560 : else
6561 : {
3924 tgl 6562 CBC 70 : dataoffset = 0; /* marker for no null bitmap */
3924 tgl 6563 GIC 70 : nbytes += ARR_OVERHEAD_NONULLS(ndim);
6564 : }
3924 tgl 6565 CBC 73 : result = (ArrayType *) palloc0(nbytes);
6566 73 : SET_VARSIZE(result, nbytes);
6567 73 : result->ndim = ndim;
6568 73 : result->dataoffset = dataoffset;
3924 tgl 6569 GIC 73 : result->elemtype = element_type;
2887 6570 73 : memcpy(ARR_DIMS(result), ARR_DIMS(array), ndim * sizeof(int));
2887 tgl 6571 CBC 73 : memcpy(ARR_LBOUND(result), ARR_LBOUND(array), ndim * sizeof(int));
3924 tgl 6572 EUB :
3924 tgl 6573 GIC 73 : if (remove)
6574 : {
6575 : /* Adjust the result length */
6576 58 : ARR_DIMS(result)[0] = nresult;
6577 : }
3924 tgl 6578 ECB :
6579 : /* Insert data into result array */
3924 tgl 6580 GIC 73 : CopyArrayEls(result,
6581 : values, nulls, nresult,
6582 : typlen, typbyval, typalign,
6583 : false);
6584 :
6585 73 : pfree(values);
6586 73 : pfree(nulls);
3924 tgl 6587 ECB :
3924 tgl 6588 CBC 73 : return result;
3924 tgl 6589 ECB : }
6590 :
6591 : /*
6592 : * Remove any occurrences of an element from an array
6593 : *
3924 tgl 6594 EUB : * If used on a multi-dimensional array this will raise an error.
6595 : */
6596 : Datum
3924 tgl 6597 GIC 61285 : array_remove(PG_FUNCTION_ARGS)
3924 tgl 6598 ECB : {
6599 : ArrayType *array;
3924 tgl 6600 CBC 61285 : Datum search = PG_GETARG_DATUM(1);
6601 61285 : bool search_isnull = PG_ARGISNULL(1);
3924 tgl 6602 ECB :
3924 tgl 6603 GIC 61285 : if (PG_ARGISNULL(0))
6604 60106 : PG_RETURN_NULL();
6605 1179 : array = PG_GETARG_ARRAYTYPE_P(0);
6606 :
6607 1179 : array = array_replace_internal(array,
6608 : search, search_isnull,
3924 tgl 6609 ECB : (Datum) 0, true,
6610 : true, PG_GET_COLLATION(),
6611 : fcinfo);
3924 tgl 6612 CBC 1176 : PG_RETURN_ARRAYTYPE_P(array);
3924 tgl 6613 ECB : }
6614 :
6615 : /*
6616 : * Replace any occurrences of an element in an array
6617 : */
6618 : Datum
3924 tgl 6619 GIC 18 : array_replace(PG_FUNCTION_ARGS)
6620 : {
6621 : ArrayType *array;
3924 tgl 6622 CBC 18 : Datum search = PG_GETARG_DATUM(1);
6623 18 : bool search_isnull = PG_ARGISNULL(1);
3924 tgl 6624 GIC 18 : Datum replace = PG_GETARG_DATUM(2);
6625 18 : bool replace_isnull = PG_ARGISNULL(2);
3924 tgl 6626 ECB :
3924 tgl 6627 CBC 18 : if (PG_ARGISNULL(0))
3924 tgl 6628 LBC 0 : PG_RETURN_NULL();
3924 tgl 6629 CBC 18 : array = PG_GETARG_ARRAYTYPE_P(0);
3924 tgl 6630 ECB :
3924 tgl 6631 GIC 18 : array = array_replace_internal(array,
3924 tgl 6632 ECB : search, search_isnull,
6633 : replace, replace_isnull,
6634 : false, PG_GET_COLLATION(),
6635 : fcinfo);
3924 tgl 6636 GIC 18 : PG_RETURN_ARRAYTYPE_P(array);
3924 tgl 6637 ECB : }
6638 :
6639 : /*
3134 6640 : * Implements width_bucket(anyelement, anyarray).
6641 : *
6642 : * 'thresholds' is an array containing lower bound values for each bucket;
6643 : * these must be sorted from smallest to largest, or bogus results will be
6644 : * produced. If N thresholds are supplied, the output is from 0 to N:
6645 : * 0 is for inputs < first threshold, N is for inputs >= last threshold.
6646 : */
6647 : Datum
3134 tgl 6648 CBC 405 : width_bucket_array(PG_FUNCTION_ARGS)
3134 tgl 6649 ECB : {
3134 tgl 6650 GIC 405 : Datum operand = PG_GETARG_DATUM(0);
3134 tgl 6651 CBC 405 : ArrayType *thresholds = PG_GETARG_ARRAYTYPE_P(1);
3134 tgl 6652 GIC 405 : Oid collation = PG_GET_COLLATION();
3134 tgl 6653 CBC 405 : Oid element_type = ARR_ELEMTYPE(thresholds);
3134 tgl 6654 ECB : int result;
6655 :
6656 : /* Check input */
3134 tgl 6657 GIC 405 : if (ARR_NDIM(thresholds) > 1)
6658 3 : ereport(ERROR,
6659 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
6660 : errmsg("thresholds must be one-dimensional array")));
3134 tgl 6661 ECB :
3134 tgl 6662 CBC 402 : if (array_contains_nulls(thresholds))
6663 3 : ereport(ERROR,
3134 tgl 6664 ECB : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
6665 : errmsg("thresholds array must not contain NULLs")));
6666 :
6667 : /* We have a dedicated implementation for float8 data */
3134 tgl 6668 GIC 399 : if (element_type == FLOAT8OID)
3134 tgl 6669 CBC 183 : result = width_bucket_array_float8(operand, thresholds);
6670 : else
6671 : {
6672 : TypeCacheEntry *typentry;
6673 :
6674 : /* Cache information about the input type */
3134 tgl 6675 GIC 216 : typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3134 tgl 6676 CBC 216 : if (typentry == NULL ||
6677 195 : typentry->type_id != element_type)
3134 tgl 6678 ECB : {
3134 tgl 6679 CBC 21 : typentry = lookup_type_cache(element_type,
3134 tgl 6680 ECB : TYPECACHE_CMP_PROC_FINFO);
3134 tgl 6681 CBC 21 : if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
3134 tgl 6682 LBC 0 : ereport(ERROR,
6683 : (errcode(ERRCODE_UNDEFINED_FUNCTION),
6684 : errmsg("could not identify a comparison function for type %s",
2118 tgl 6685 ECB : format_type_be(element_type))));
3134 tgl 6686 GIC 21 : fcinfo->flinfo->fn_extra = (void *) typentry;
6687 : }
6688 :
6689 : /*
3134 tgl 6690 ECB : * We have separate implementation paths for fixed- and variable-width
6691 : * types, since indexing the array is a lot cheaper in the first case.
6692 : */
3134 tgl 6693 GIC 216 : if (typentry->typlen > 0)
6694 45 : result = width_bucket_array_fixed(operand, thresholds,
3134 tgl 6695 ECB : collation, typentry);
6696 : else
3134 tgl 6697 GIC 171 : result = width_bucket_array_variable(operand, thresholds,
6698 : collation, typentry);
6699 : }
6700 :
6701 : /* Avoid leaking memory when handed toasted input. */
3134 tgl 6702 CBC 399 : PG_FREE_IF_COPY(thresholds, 1);
6703 :
6704 399 : PG_RETURN_INT32(result);
3134 tgl 6705 ECB : }
6706 :
6707 : /*
6708 : * width_bucket_array for float8 data.
6709 : */
6710 : static int
3134 tgl 6711 CBC 183 : width_bucket_array_float8(Datum operand, ArrayType *thresholds)
6712 : {
6713 183 : float8 op = DatumGetFloat8(operand);
3134 tgl 6714 EUB : float8 *thresholds_data;
6715 : int left;
6716 : int right;
6717 :
6718 : /*
3134 tgl 6719 ECB : * Since we know the array contains no NULLs, we can just index it
6720 : * directly.
6721 : */
3134 tgl 6722 GIC 183 : thresholds_data = (float8 *) ARR_DATA_PTR(thresholds);
3134 tgl 6723 ECB :
3134 tgl 6724 GIC 183 : left = 0;
3134 tgl 6725 CBC 183 : right = ArrayGetNItems(ARR_NDIM(thresholds), ARR_DIMS(thresholds));
3134 tgl 6726 ECB :
6727 : /*
3134 tgl 6728 EUB : * If the probe value is a NaN, it's greater than or equal to all possible
6729 : * threshold values (including other NaNs), so we need not search. Note
6730 : * that this would give the same result as searching even if the array
6731 : * contains multiple NaNs (as long as they're correctly sorted), since the
6732 : * loop logic will find the rightmost of multiple equal threshold values.
6733 : */
3134 tgl 6734 GIC 183 : if (isnan(op))
6735 3 : return right;
6736 :
3134 tgl 6737 ECB : /* Find the bucket */
3134 tgl 6738 GIC 567 : while (left < right)
3134 tgl 6739 ECB : {
3134 tgl 6740 CBC 387 : int mid = (left + right) / 2;
3134 tgl 6741 ECB :
3134 tgl 6742 GIC 387 : if (isnan(thresholds_data[mid]) || op < thresholds_data[mid])
6743 168 : right = mid;
6744 : else
3134 tgl 6745 CBC 219 : left = mid + 1;
6746 : }
3134 tgl 6747 ECB :
3134 tgl 6748 CBC 180 : return left;
3134 tgl 6749 ECB : }
6750 :
6751 : /*
6752 : * width_bucket_array for generic fixed-width data types.
6753 : */
6754 : static int
3134 tgl 6755 CBC 45 : width_bucket_array_fixed(Datum operand,
3134 tgl 6756 ECB : ArrayType *thresholds,
6757 : Oid collation,
6758 : TypeCacheEntry *typentry)
6759 : {
1534 andres 6760 CBC 45 : LOCAL_FCINFO(locfcinfo, 2);
3134 tgl 6761 ECB : char *thresholds_data;
3134 tgl 6762 GIC 45 : int typlen = typentry->typlen;
3134 tgl 6763 CBC 45 : bool typbyval = typentry->typbyval;
3134 tgl 6764 ECB : int left;
6765 : int right;
6766 :
6767 : /*
6768 : * Since we know the array contains no NULLs, we can just index it
6769 : * directly.
6770 : */
3134 tgl 6771 CBC 45 : thresholds_data = (char *) ARR_DATA_PTR(thresholds);
6772 :
1534 andres 6773 GIC 45 : InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
3134 tgl 6774 ECB : collation, NULL, NULL);
6775 :
6776 : /* Find the bucket */
3134 tgl 6777 GIC 45 : left = 0;
3134 tgl 6778 CBC 45 : right = ArrayGetNItems(ARR_NDIM(thresholds), ARR_DIMS(thresholds));
3134 tgl 6779 GIC 135 : while (left < right)
6780 : {
6781 90 : int mid = (left + right) / 2;
6782 : char *ptr;
3134 tgl 6783 ECB : int32 cmpresult;
6784 :
3134 tgl 6785 GIC 90 : ptr = thresholds_data + mid * typlen;
3134 tgl 6786 ECB :
1534 andres 6787 GIC 90 : locfcinfo->args[0].value = operand;
6788 90 : locfcinfo->args[0].isnull = false;
6789 90 : locfcinfo->args[1].value = fetch_att(ptr, typbyval, typlen);
6790 90 : locfcinfo->args[1].isnull = false;
6791 :
6792 90 : cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
6793 :
6794 : /* We don't expect comparison support functions to return null */
1083 tgl 6795 CBC 90 : Assert(!locfcinfo->isnull);
6796 :
3134 tgl 6797 GIC 90 : if (cmpresult < 0)
3134 tgl 6798 CBC 45 : right = mid;
3134 tgl 6799 ECB : else
3134 tgl 6800 GIC 45 : left = mid + 1;
3134 tgl 6801 ECB : }
6802 :
3134 tgl 6803 CBC 45 : return left;
6804 : }
3134 tgl 6805 ECB :
6806 : /*
6807 : * width_bucket_array for generic variable-width data types.
6808 : */
6809 : static int
3134 tgl 6810 CBC 171 : width_bucket_array_variable(Datum operand,
6811 : ArrayType *thresholds,
6812 : Oid collation,
6813 : TypeCacheEntry *typentry)
6814 : {
1534 andres 6815 GIC 171 : LOCAL_FCINFO(locfcinfo, 2);
6816 : char *thresholds_data;
3134 tgl 6817 CBC 171 : int typlen = typentry->typlen;
3134 tgl 6818 GIC 171 : bool typbyval = typentry->typbyval;
6819 171 : char typalign = typentry->typalign;
3134 tgl 6820 ECB : int left;
6821 : int right;
6822 :
3134 tgl 6823 CBC 171 : thresholds_data = (char *) ARR_DATA_PTR(thresholds);
6824 :
1534 andres 6825 171 : InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
3134 tgl 6826 EUB : collation, NULL, NULL);
3134 tgl 6827 ECB :
6828 : /* Find the bucket */
3134 tgl 6829 CBC 171 : left = 0;
3134 tgl 6830 GIC 171 : right = ArrayGetNItems(ARR_NDIM(thresholds), ARR_DIMS(thresholds));
6831 534 : while (left < right)
6832 : {
6833 363 : int mid = (left + right) / 2;
3134 tgl 6834 ECB : char *ptr;
6835 : int i;
6836 : int32 cmpresult;
6837 :
6838 : /* Locate mid'th array element by advancing from left element */
3134 tgl 6839 GIC 363 : ptr = thresholds_data;
6840 621 : for (i = left; i < mid; i++)
6841 : {
6842 258 : ptr = att_addlength_pointer(ptr, typlen, ptr);
6843 258 : ptr = (char *) att_align_nominal(ptr, typalign);
6844 : }
6845 :
1534 andres 6846 CBC 363 : locfcinfo->args[0].value = operand;
1534 andres 6847 GIC 363 : locfcinfo->args[0].isnull = false;
1534 andres 6848 CBC 363 : locfcinfo->args[1].value = fetch_att(ptr, typbyval, typlen);
6849 363 : locfcinfo->args[1].isnull = false;
3134 tgl 6850 ECB :
1534 andres 6851 CBC 363 : cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
6852 :
6853 : /* We don't expect comparison support functions to return null */
1083 tgl 6854 GIC 363 : Assert(!locfcinfo->isnull);
1083 tgl 6855 ECB :
3134 tgl 6856 CBC 363 : if (cmpresult < 0)
3134 tgl 6857 GIC 150 : right = mid;
6858 : else
6859 : {
3134 tgl 6860 CBC 213 : left = mid + 1;
3134 tgl 6861 ECB :
6862 : /*
6863 : * Move the thresholds pointer to match new "left" index, so we
6864 : * don't have to seek over those elements again. This trick
6865 : * ensures we do only O(N) array indexing work, not O(N^2).
6866 : */
3134 tgl 6867 CBC 213 : ptr = att_addlength_pointer(ptr, typlen, ptr);
3134 tgl 6868 GIC 213 : thresholds_data = (char *) att_align_nominal(ptr, typalign);
6869 : }
6870 : }
6871 :
6872 171 : return left;
3134 tgl 6873 ECB : }
767 6874 :
6875 : /*
6876 : * Trim the last N elements from an array by building an appropriate slice.
6877 : * Only the first dimension is trimmed.
6878 : */
6879 : Datum
767 tgl 6880 GBC 24 : trim_array(PG_FUNCTION_ARGS)
6881 : {
767 tgl 6882 GIC 24 : ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
6883 24 : int n = PG_GETARG_INT32(1);
252 tgl 6884 CBC 24 : int array_length = (ARR_NDIM(v) > 0) ? ARR_DIMS(v)[0] : 0;
6885 : int16 elmlen;
6886 : bool elmbyval;
6887 : char elmalign;
6888 : int lower[MAXDIM];
6889 : int upper[MAXDIM];
6890 : bool lowerProvided[MAXDIM];
767 tgl 6891 ECB : bool upperProvided[MAXDIM];
6892 : Datum result;
6893 :
6894 : /* Per spec, throw an error if out of bounds */
767 tgl 6895 CBC 24 : if (n < 0 || n > array_length)
767 tgl 6896 GIC 9 : ereport(ERROR,
6897 : (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
6898 : errmsg("number of elements to trim must be between 0 and %d",
6899 : array_length)));
767 tgl 6900 ECB :
6901 : /* Set all the bounds as unprovided except the first upper bound */
767 tgl 6902 CBC 15 : memset(lowerProvided, false, sizeof(lowerProvided));
767 tgl 6903 GIC 15 : memset(upperProvided, false, sizeof(upperProvided));
252 6904 15 : if (ARR_NDIM(v) > 0)
6905 : {
6906 15 : upper[0] = ARR_LBOUND(v)[0] + array_length - n - 1;
6907 15 : upperProvided[0] = true;
6908 : }
767 tgl 6909 ECB :
6910 : /* Fetch the needed information about the element type */
767 tgl 6911 CBC 15 : get_typlenbyvalalign(ARR_ELEMTYPE(v), &elmlen, &elmbyval, &elmalign);
6912 :
6913 : /* Get the slice */
767 tgl 6914 GIC 15 : result = array_get_slice(PointerGetDatum(v), 1,
6915 : upper, lower, upperProvided, lowerProvided,
6916 : -1, elmlen, elmbyval, elmalign);
6917 :
6918 15 : PG_RETURN_DATUM(result);
6919 : }
|