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