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