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 227122 : array_in(PG_FUNCTION_ARGS)
180 : {
181 227122 : char *string = PG_GETARG_CSTRING(0); /* external form */
182 227122 : Oid element_type = PG_GETARG_OID(1); /* type of an array
183 : * element */
184 227122 : int32 typmod = PG_GETARG_INT32(2); /* typmod for array elements */
185 227122 : 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 227122 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
210 227122 : if (my_extra == NULL)
211 : {
212 57898 : fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
213 : sizeof(ArrayMetaState));
214 57898 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
215 57898 : my_extra->element_type = ~element_type;
216 : }
217 :
218 227122 : if (my_extra->element_type != element_type)
219 : {
220 : /*
221 : * Get info about element type, including its input conversion proc
222 : */
223 57898 : 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 57898 : fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
228 57898 : fcinfo->flinfo->fn_mcxt);
229 57898 : my_extra->element_type = element_type;
230 : }
231 227122 : typlen = my_extra->typlen;
232 227122 : typbyval = my_extra->typbyval;
233 227122 : typalign = my_extra->typalign;
234 227122 : typdelim = my_extra->typdelim;
235 227122 : 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 1589854 : for (int i = 0; i < MAXDIM; i++)
243 : {
244 1362732 : dim[i] = -1; /* indicates "not yet known" */
245 1362732 : 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 227122 : p = string;
255 227122 : if (!ReadArrayDimensions(&p, &ndim, dim, lBound, string, escontext))
256 0 : return (Datum) 0;
257 :
258 227080 : if (ndim == 0)
259 : {
260 : /* No array dimensions, so next character should be a left brace */
261 226962 : 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 227066 : 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 24 : return (Datum) 0;
300 :
301 : /* only whitespace is allowed after the closing brace */
302 226892 : 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 226880 : if (nitems == 0)
313 4184 : PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
314 :
315 : /*
316 : * Check for nulls, compute total data space needed
317 : */
318 222696 : hasnulls = false;
319 222696 : nbytes = 0;
320 1168986 : for (int i = 0; i < nitems; i++)
321 : {
322 946290 : if (nulls[i])
323 430 : hasnulls = true;
324 : else
325 : {
326 : /* let's just make sure data is not toasted */
327 945860 : if (typlen == -1)
328 712510 : values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
329 945860 : nbytes = att_addlength_datum(nbytes, typlen, values[i]);
330 945860 : nbytes = att_align_nominal(nbytes, typalign);
331 : /* check for overflow of total request */
332 945860 : 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 222696 : if (hasnulls)
340 : {
341 392 : dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
342 392 : nbytes += dataoffset;
343 : }
344 : else
345 : {
346 222304 : dataoffset = 0; /* marker for no null bitmap */
347 222304 : nbytes += ARR_OVERHEAD_NONULLS(ndim);
348 : }
349 :
350 : /*
351 : * Construct the final array datum
352 : */
353 222696 : retval = (ArrayType *) palloc0(nbytes);
354 222696 : SET_VARSIZE(retval, nbytes);
355 222696 : retval->ndim = ndim;
356 222696 : 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 222696 : retval->elemtype = element_type;
364 222696 : memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
365 222696 : memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
366 :
367 222696 : CopyArrayEls(retval,
368 : values, nulls, nitems,
369 : typlen, typbyval, typalign,
370 : true);
371 :
372 222696 : pfree(values);
373 222696 : pfree(nulls);
374 :
375 222696 : 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 227122 : ReadArrayDimensions(char **srcptr, int *ndim_p, int *dim, int *lBound,
403 : const char *origStr, Node *escontext)
404 : {
405 227122 : 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 227122 : 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 227284 : while (scanner_isspace(*p))
424 12 : p++;
425 227272 : if (*p != '[')
426 227080 : 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 227080 : *srcptr = p;
502 227080 : *ndim_p = ndim;
503 227080 : 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 227066 : 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 227066 : int ndim = *ndim_p;
596 227066 : 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 227066 : maxitems = 16;
609 227066 : values = palloc_array(Datum, maxitems);
610 227066 : nulls = palloc_array(bool, maxitems);
611 :
612 : /* Allocate workspace to hold (string representation of) one element */
613 227066 : 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 227066 : nest_level = 0;
620 227066 : nitems = 0;
621 227066 : ndim_frozen = dimensions_specified;
622 227066 : expect_delim = false;
623 : do
624 : {
625 : ArrayToken tok;
626 :
627 2126866 : tok = ReadArrayToken(srcptr, &elembuf, typdelim, origStr, escontext);
628 :
629 2126836 : switch (tok)
630 : {
631 228430 : case ATOK_LEVEL_START:
632 : /* Can't write left brace where delim is expected */
633 228430 : 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 228424 : 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 228422 : nelems[nest_level] = 0;
647 228422 : nest_level++;
648 228422 : if (nest_level > ndim)
649 : {
650 : /* Can't increase ndim once it's frozen */
651 227460 : if (ndim_frozen)
652 12 : goto dimension_error;
653 227448 : ndim = nest_level;
654 : }
655 228410 : break;
656 :
657 228212 : 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 228212 : 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 228188 : nest_level--;
672 : /* Nested sub-arrays count as elements of outer level */
673 228188 : if (nest_level > 0)
674 1290 : 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 228188 : if (dim[nest_level] < 0)
682 : {
683 : /* Save length of first sub-array of this level */
684 227238 : dim[nest_level] = nelems[nest_level];
685 : }
686 950 : 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 228156 : expect_delim = true;
697 228156 : break;
698 :
699 723720 : case ATOK_DELIM:
700 723720 : 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 723714 : expect_delim = false;
707 723714 : break;
708 :
709 946468 : 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 946468 : 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 946462 : if (nitems >= maxitems)
723 : {
724 2150 : 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 2150 : maxitems = Min(maxitems * 2, MaxArraySize);
730 2150 : values = repalloc_array(values, Datum, maxitems);
731 2150 : nulls = repalloc_array(nulls, bool, maxitems);
732 : }
733 :
734 : /* Read the element's value, or check that NULL is allowed */
735 946462 : if (!InputFunctionCallSafe(inputproc,
736 : (tok == ATOK_ELEM_NULL) ? NULL : elembuf.data,
737 : typioparam, typmod,
738 : escontext,
739 946462 : &values[nitems]))
740 18 : return false;
741 946424 : nulls[nitems] = (tok == ATOK_ELEM_NULL);
742 946424 : 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 946424 : ndim_frozen = true;
750 946424 : if (nest_level != ndim)
751 12 : goto dimension_error;
752 : /* Count the new element */
753 946412 : nelems[nest_level - 1]++;
754 :
755 : /* Must have a delim or a right brace following */
756 946412 : expect_delim = true;
757 946412 : break;
758 :
759 6 : case ATOK_ERROR:
760 6 : return false;
761 : }
762 2126692 : } while (nest_level > 0);
763 :
764 : /* Clean up and return results */
765 226892 : pfree(elembuf.data);
766 :
767 226892 : *ndim_p = ndim;
768 226892 : *nitems_p = nitems;
769 226892 : *values_p = values;
770 226892 : *nulls_p = nulls;
771 226892 : 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 2126866 : ReadArrayToken(char **srcptr, StringInfo elembuf, char typdelim,
797 : const char *origStr, Node *escontext)
798 : {
799 2126866 : char *p = *srcptr;
800 : int dstlen;
801 : bool has_escapes;
802 :
803 2126866 : resetStringInfo(elembuf);
804 :
805 : /* Identify token type. Loop advances over leading whitespace. */
806 : for (;;)
807 : {
808 2149068 : switch (*p)
809 : {
810 0 : case '\0':
811 0 : goto ending_error;
812 228430 : case '{':
813 228430 : *srcptr = p + 1;
814 228430 : return ATOK_LEVEL_START;
815 228212 : case '}':
816 228212 : *srcptr = p + 1;
817 228212 : return ATOK_LEVEL_END;
818 611528 : case '"':
819 611528 : p++;
820 611528 : goto quoted_element;
821 1080898 : default:
822 1080898 : if (*p == typdelim)
823 : {
824 723720 : *srcptr = p + 1;
825 723720 : return ATOK_DELIM;
826 : }
827 357178 : if (scanner_isspace(*p))
828 : {
829 22202 : p++;
830 22202 : continue;
831 : }
832 334976 : goto unquoted_element;
833 : }
834 : }
835 :
836 611528 : quoted_element:
837 : for (;;)
838 : {
839 4430200 : switch (*p)
840 : {
841 0 : case '\0':
842 0 : goto ending_error;
843 142 : case '\\':
844 : /* Skip backslash, copy next character as-is. */
845 142 : p++;
846 142 : if (*p == '\0')
847 0 : goto ending_error;
848 142 : appendStringInfoChar(elembuf, *p++);
849 142 : break;
850 611528 : 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 611564 : while (*(++p) != '\0')
861 : {
862 611564 : if (*p == typdelim || *p == '}' || *p == '{')
863 : {
864 611510 : *srcptr = p;
865 611510 : 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 3818530 : default:
875 3818530 : appendStringInfoChar(elembuf, *p++);
876 3818530 : break;
877 : }
878 : }
879 :
880 334976 : 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 334976 : dstlen = 0;
887 334976 : has_escapes = false;
888 : for (;;)
889 : {
890 1772610 : 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 1772574 : default:
916 : /* End of elem? */
917 1772574 : if (*p == typdelim || *p == '}')
918 : {
919 : /* hack: truncate the output string to dstlen */
920 334958 : elembuf->data[dstlen] = '\0';
921 334958 : elembuf->len = dstlen;
922 334958 : *srcptr = p;
923 : /* Check if it's unquoted "NULL" */
924 669898 : if (Array_nulls && !has_escapes &&
925 334940 : pg_strcasecmp(elembuf->data, "NULL") == 0)
926 430 : return ATOK_ELEM_NULL;
927 : else
928 334528 : return ATOK_ELEM;
929 : }
930 1437616 : appendStringInfoChar(elembuf, *p);
931 1437616 : if (!scanner_isspace(*p))
932 1436624 : dstlen = elembuf->len;
933 1437616 : p++;
934 1437616 : 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 1472540 : 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 1472540 : char *p = ARR_DATA_PTR(array);
971 1472540 : bits8 *bitmap = ARR_NULLBITMAP(array);
972 1472540 : int bitval = 0;
973 1472540 : int bitmask = 1;
974 : int i;
975 :
976 1472540 : if (typbyval)
977 926004 : freedata = false;
978 :
979 9884368 : for (i = 0; i < nitems; i++)
980 : {
981 8411828 : if (nulls && nulls[i])
982 : {
983 33120 : 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 8378708 : bitval |= bitmask;
990 8378708 : p += ArrayCastAndSet(values[i], typlen, typbyval, typalign, p);
991 8378708 : if (freedata)
992 712910 : pfree(DatumGetPointer(values[i]));
993 : }
994 8411828 : if (bitmap)
995 : {
996 785396 : bitmask <<= 1;
997 785396 : if (bitmask == 0x100)
998 : {
999 95134 : *bitmap++ = bitval;
1000 95134 : bitval = 0;
1001 95134 : bitmask = 1;
1002 : }
1003 : }
1004 : }
1005 :
1006 1472540 : if (bitmap && bitmask != 1)
1007 17756 : *bitmap = bitval;
1008 1472540 : }
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 671164 : array_out(PG_FUNCTION_ARGS)
1017 : {
1018 671164 : AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
1019 671164 : 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 671164 : 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 671164 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1055 671164 : if (my_extra == NULL)
1056 : {
1057 22824 : fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1058 : sizeof(ArrayMetaState));
1059 22824 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1060 22824 : my_extra->element_type = ~element_type;
1061 : }
1062 :
1063 671164 : if (my_extra->element_type != element_type)
1064 : {
1065 : /*
1066 : * Get info about element type, including its output conversion proc
1067 : */
1068 25706 : 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 25706 : fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1073 25706 : fcinfo->flinfo->fn_mcxt);
1074 25706 : my_extra->element_type = element_type;
1075 : }
1076 671164 : typlen = my_extra->typlen;
1077 671164 : typbyval = my_extra->typbyval;
1078 671164 : typalign = my_extra->typalign;
1079 671164 : typdelim = my_extra->typdelim;
1080 :
1081 671164 : ndim = AARR_NDIM(v);
1082 671164 : dims = AARR_DIMS(v);
1083 671164 : lb = AARR_LBOUND(v);
1084 671164 : nitems = ArrayGetNItems(ndim, dims);
1085 :
1086 671164 : if (nitems == 0)
1087 : {
1088 2616 : retval = pstrdup("{}");
1089 2616 : 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 1337712 : for (i = 0; i < ndim; i++)
1097 : {
1098 669454 : 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 668548 : values = (char **) palloc(nitems * sizeof(char *));
1111 668548 : needquotes = (bool *) palloc(nitems * sizeof(bool));
1112 668548 : overall_length = 0;
1113 :
1114 668548 : array_iter_setup(&iter, v);
1115 :
1116 2351334 : 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 1682786 : itemvalue = array_iter_next(&iter, &isnull, i,
1124 : typlen, typbyval, typalign);
1125 :
1126 1682786 : if (isnull)
1127 : {
1128 2248 : values[i] = pstrdup("NULL");
1129 2248 : overall_length += 4;
1130 2248 : needquote = false;
1131 : }
1132 : else
1133 : {
1134 1680538 : values[i] = OutputFunctionCall(&my_extra->proc, itemvalue);
1135 :
1136 : /* count data plus backslashes; detect chars needing quotes */
1137 1680538 : if (values[i][0] == '\0')
1138 524 : needquote = true; /* force quotes for empty string */
1139 1680014 : else if (pg_strcasecmp(values[i], "NULL") == 0)
1140 20 : needquote = true; /* force quotes for literal NULL */
1141 : else
1142 1679994 : needquote = false;
1143 :
1144 25938298 : for (tmp = values[i]; *tmp != '\0'; tmp++)
1145 : {
1146 24257760 : char ch = *tmp;
1147 :
1148 24257760 : overall_length += 1;
1149 24257760 : if (ch == '"' || ch == '\\')
1150 : {
1151 3026 : needquote = true;
1152 3026 : overall_length += 1;
1153 : }
1154 48473262 : else if (ch == '{' || ch == '}' || ch == typdelim ||
1155 24218528 : scanner_isspace(ch))
1156 78566 : needquote = true;
1157 : }
1158 : }
1159 :
1160 1682786 : needquotes[i] = needquote;
1161 :
1162 : /* Count the pair of double quotes, if needed */
1163 1682786 : if (needquote)
1164 17740 : overall_length += 2;
1165 : /* and the comma (or other typdelim delimiter) */
1166 1682786 : 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 1338056 : for (i = j = 0, k = 1; i < ndim; i++)
1176 : {
1177 669508 : j += k, k *= dims[i];
1178 : }
1179 668548 : overall_length += 2 * j;
1180 :
1181 : /* Format explicit dimensions if required */
1182 668548 : dims_str[0] = '\0';
1183 668548 : 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 668548 : retval = (char *) palloc(overall_length);
1199 668548 : p = retval;
1200 :
1201 : #define APPENDSTR(str) (strcpy(p, (str)), p += strlen(p))
1202 : #define APPENDCHAR(ch) (*p++ = (ch), *p = '\0')
1203 :
1204 668548 : if (needdims)
1205 290 : APPENDSTR(dims_str);
1206 668548 : APPENDCHAR('{');
1207 1338056 : for (i = 0; i < ndim; i++)
1208 669508 : indx[i] = 0;
1209 668548 : j = 0;
1210 668548 : k = 0;
1211 : do
1212 : {
1213 1685178 : for (i = j; i < ndim - 1; i++)
1214 2392 : APPENDCHAR('{');
1215 :
1216 1682786 : if (needquotes[k])
1217 : {
1218 17740 : APPENDCHAR('"');
1219 447640 : for (tmp = values[k]; *tmp; tmp++)
1220 : {
1221 429900 : char ch = *tmp;
1222 :
1223 429900 : if (ch == '"' || ch == '\\')
1224 3026 : *p++ = '\\';
1225 429900 : *p++ = ch;
1226 : }
1227 17740 : *p = '\0';
1228 17740 : APPENDCHAR('"');
1229 : }
1230 : else
1231 1665046 : APPENDSTR(values[k]);
1232 1682786 : pfree(values[k++]);
1233 :
1234 2353726 : for (i = ndim - 1; i >= 0; i--)
1235 : {
1236 1685178 : if (++(indx[i]) < dims[i])
1237 : {
1238 1014238 : APPENDCHAR(typdelim);
1239 1014238 : break;
1240 : }
1241 : else
1242 : {
1243 670940 : indx[i] = 0;
1244 670940 : APPENDCHAR('}');
1245 : }
1246 : }
1247 1682786 : j = i;
1248 1682786 : } 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 668548 : pfree(values);
1257 668548 : pfree(needquotes);
1258 :
1259 668548 : 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 1674 : array_ndims(PG_FUNCTION_ARGS)
1653 : {
1654 1674 : AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
1655 :
1656 : /* Sanity check: does it look like an array at all? */
1657 1674 : if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
1658 12 : PG_RETURN_NULL();
1659 :
1660 1662 : 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 8366 : array_dims(PG_FUNCTION_ARGS)
1669 : {
1670 8366 : 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 8366 : if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
1685 68 : PG_RETURN_NULL();
1686 :
1687 8298 : dimv = AARR_DIMS(v);
1688 8298 : lb = AARR_LBOUND(v);
1689 :
1690 8298 : p = buf;
1691 16704 : for (i = 0; i < AARR_NDIM(v); i++)
1692 : {
1693 8406 : sprintf(p, "[%d:%d]", lb[i], dimv[i] + lb[i] - 1);
1694 8406 : p += strlen(p);
1695 : }
1696 :
1697 8298 : 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 25248 : array_lower(PG_FUNCTION_ARGS)
1707 : {
1708 25248 : AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
1709 25248 : 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 25248 : if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
1715 0 : PG_RETURN_NULL();
1716 :
1717 : /* Sanity check: was the requested dim valid */
1718 25248 : if (reqdim <= 0 || reqdim > AARR_NDIM(v))
1719 0 : PG_RETURN_NULL();
1720 :
1721 25248 : lb = AARR_LBOUND(v);
1722 25248 : result = lb[reqdim - 1];
1723 :
1724 25248 : 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 25858 : array_upper(PG_FUNCTION_ARGS)
1734 : {
1735 25858 : AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
1736 25858 : 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 25858 : if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
1743 30 : PG_RETURN_NULL();
1744 :
1745 : /* Sanity check: was the requested dim valid */
1746 25828 : if (reqdim <= 0 || reqdim > AARR_NDIM(v))
1747 0 : PG_RETURN_NULL();
1748 :
1749 25828 : lb = AARR_LBOUND(v);
1750 25828 : dimv = AARR_DIMS(v);
1751 :
1752 25828 : result = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
1753 :
1754 25828 : 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 110128 : array_length(PG_FUNCTION_ARGS)
1764 : {
1765 110128 : AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
1766 110128 : 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 110128 : if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
1772 0 : PG_RETURN_NULL();
1773 :
1774 : /* Sanity check: was the requested dim valid */
1775 110128 : if (reqdim <= 0 || reqdim > AARR_NDIM(v))
1776 12 : PG_RETURN_NULL();
1777 :
1778 110116 : dimv = AARR_DIMS(v);
1779 :
1780 110116 : result = dimv[reqdim - 1];
1781 :
1782 110116 : PG_RETURN_INT32(result);
1783 : }
1784 :
1785 : /*
1786 : * array_cardinality:
1787 : * returns the total number of elements in an array
1788 : */
1789 : Datum
1790 2652 : array_cardinality(PG_FUNCTION_ARGS)
1791 : {
1792 2652 : AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
1793 :
1794 2652 : 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 765080 : 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 765080 : if (arraytyplen > 0)
1841 : {
1842 : /*
1843 : * fixed-length arrays -- these are assumed to be 1-d, 0-based
1844 : */
1845 263618 : ndim = 1;
1846 263618 : fixedDim[0] = arraytyplen / elmlen;
1847 263618 : fixedLb[0] = 0;
1848 263618 : dim = fixedDim;
1849 263618 : lb = fixedLb;
1850 263618 : arraydataptr = (char *) DatumGetPointer(arraydatum);
1851 263618 : arraynullsptr = NULL;
1852 : }
1853 501462 : 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 496458 : ArrayType *array = DatumGetArrayTypeP(arraydatum);
1869 :
1870 496458 : ndim = ARR_NDIM(array);
1871 496458 : dim = ARR_DIMS(array);
1872 496458 : lb = ARR_LBOUND(array);
1873 496458 : arraydataptr = ARR_DATA_PTR(array);
1874 496458 : arraynullsptr = ARR_NULLBITMAP(array);
1875 : }
1876 :
1877 : /*
1878 : * Return NULL for invalid subscript
1879 : */
1880 760076 : if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
1881 : {
1882 96 : *isNull = true;
1883 96 : return (Datum) 0;
1884 : }
1885 1467022 : for (i = 0; i < ndim; i++)
1886 : {
1887 760076 : 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 706946 : offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
1898 :
1899 : /*
1900 : * Check for NULL array element
1901 : */
1902 706946 : 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 706874 : *isNull = false;
1912 706874 : retptr = array_seek(arraydataptr, 0, arraynullsptr, offset,
1913 : elmlen, elmbyval, elmalign);
1914 706874 : 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 3968 : 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 3968 : 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 3950 : 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 3950 : if (elmlen == -1 && !isNull)
2271 2422 : dataValue = PointerGetDatum(PG_DETOAST_DATUM(dataValue));
2272 :
2273 3950 : 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 1940 : array = DatumGetArrayTypeP(arraydatum);
2289 :
2290 1940 : 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 1940 : 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 1592 : 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 1586 : memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
2320 1586 : memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
2321 :
2322 1586 : newhasnulls = (ARR_HASNULL(array) || isNull);
2323 1586 : 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 1586 : if (ndim == 1)
2332 : {
2333 1580 : 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 1580 : if (indx[0] >= (dim[0] + lb[0]))
2348 : {
2349 : /* addedafter = indx[0] - (dim[0] + lb[0]) + 1; */
2350 : /* dim[0] += addedafter; */
2351 2418 : if (pg_sub_s32_overflow(indx[0], dim[0] + lb[0], &addedafter) ||
2352 2412 : pg_add_s32_overflow(addedafter, 1, &addedafter) ||
2353 1206 : 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 1206 : 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 1580 : newnitems = ArrayGetNItems(ndim, dim);
2380 1580 : ArrayCheckBounds(ndim, dim, lb);
2381 :
2382 : /*
2383 : * Compute sizes of items and areas to copy
2384 : */
2385 1580 : if (newhasnulls)
2386 134 : overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, newnitems);
2387 : else
2388 1446 : overheadlen = ARR_OVERHEAD_NONULLS(ndim);
2389 1580 : oldnitems = ArrayGetNItems(ndim, ARR_DIMS(array));
2390 1580 : oldnullbitmap = ARR_NULLBITMAP(array);
2391 1580 : oldoverheadlen = ARR_DATA_OFFSET(array);
2392 1580 : olddatasize = ARR_SIZE(array) - oldoverheadlen;
2393 1580 : if (addedbefore)
2394 : {
2395 24 : offset = 0;
2396 24 : lenbefore = 0;
2397 24 : olditemlen = 0;
2398 24 : lenafter = olddatasize;
2399 : }
2400 1556 : else if (addedafter)
2401 : {
2402 1206 : offset = oldnitems;
2403 1206 : lenbefore = olddatasize;
2404 1206 : olditemlen = 0;
2405 1206 : 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 1580 : if (isNull)
2424 20 : newitemlen = 0;
2425 : else
2426 : {
2427 1560 : newitemlen = att_addlength_datum(0, elmlen, dataValue);
2428 1560 : newitemlen = att_align_nominal(newitemlen, elmalign);
2429 : }
2430 :
2431 1580 : newsize = overheadlen + lenbefore + newitemlen + lenafter;
2432 :
2433 : /*
2434 : * OK, create the new array and fill in header/dimensions
2435 : */
2436 1580 : newarray = (ArrayType *) palloc0(newsize);
2437 1580 : SET_VARSIZE(newarray, newsize);
2438 1580 : newarray->ndim = ndim;
2439 1580 : newarray->dataoffset = newhasnulls ? overheadlen : 0;
2440 1580 : newarray->elemtype = ARR_ELEMTYPE(array);
2441 1580 : memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
2442 1580 : memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
2443 :
2444 : /*
2445 : * Fill in data
2446 : */
2447 1580 : memcpy((char *) newarray + overheadlen,
2448 : (char *) array + oldoverheadlen,
2449 : lenbefore);
2450 1580 : if (!isNull)
2451 1560 : ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign,
2452 1560 : (char *) newarray + overheadlen + lenbefore);
2453 1580 : memcpy((char *) newarray + overheadlen + lenbefore + newitemlen,
2454 1580 : (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 1580 : 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 1580 : 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 41804 : array_ref(ArrayType *array, int nSubscripts, int *indx,
3147 : int arraytyplen, int elmlen, bool elmbyval, char elmalign,
3148 : bool *isNull)
3149 : {
3150 41804 : 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 942 : array_set(ArrayType *array, int nSubscripts, int *indx,
3164 : Datum dataValue, bool isNull,
3165 : int arraytyplen, int elmlen, bool elmbyval, char elmalign)
3166 : {
3167 942 : 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 21820 : for (i = 0; i < nitems; i++)
3282 : {
3283 : /* Get source element, checking for NULL */
3284 21238 : *transform_source =
3285 21238 : 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 21238 : values[i] = ExecEvalExpr(exprstate, econtext, &nulls[i]);
3290 :
3291 21206 : if (nulls[i])
3292 12 : hasnulls = true;
3293 : else
3294 : {
3295 : /* Ensure data is not toasted */
3296 21194 : if (typlen == -1)
3297 382 : values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
3298 : /* Update total result size */
3299 21194 : nbytes = att_addlength_datum(nbytes, typlen, values[i]);
3300 21194 : nbytes = att_align_nominal(nbytes, typalign);
3301 : /* check for overflow of total request */
3302 21194 : 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 238972 : 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 238972 : dims[0] = nelems;
3369 238972 : lbs[0] = 1;
3370 :
3371 238972 : 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 168624 : construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
3382 : {
3383 : int elmlen;
3384 : bool elmbyval;
3385 : char elmalign;
3386 :
3387 168624 : switch (elmtype)
3388 : {
3389 2900 : case CHAROID:
3390 2900 : elmlen = 1;
3391 2900 : elmbyval = true;
3392 2900 : elmalign = TYPALIGN_CHAR;
3393 2900 : break;
3394 :
3395 8228 : case CSTRINGOID:
3396 8228 : elmlen = -2;
3397 8228 : elmbyval = false;
3398 8228 : elmalign = TYPALIGN_CHAR;
3399 8228 : break;
3400 :
3401 98924 : case FLOAT4OID:
3402 98924 : elmlen = sizeof(float4);
3403 98924 : elmbyval = true;
3404 98924 : elmalign = TYPALIGN_INT;
3405 98924 : break;
3406 :
3407 26094 : case INT2OID:
3408 26094 : elmlen = sizeof(int16);
3409 26094 : elmbyval = true;
3410 26094 : elmalign = TYPALIGN_SHORT;
3411 26094 : break;
3412 :
3413 6058 : case INT4OID:
3414 6058 : elmlen = sizeof(int32);
3415 6058 : elmbyval = true;
3416 6058 : elmalign = TYPALIGN_INT;
3417 6058 : break;
3418 :
3419 4 : case INT8OID:
3420 4 : elmlen = sizeof(int64);
3421 4 : elmbyval = FLOAT8PASSBYVAL;
3422 4 : elmalign = TYPALIGN_DOUBLE;
3423 4 : break;
3424 :
3425 578 : case NAMEOID:
3426 578 : elmlen = NAMEDATALEN;
3427 578 : elmbyval = false;
3428 578 : elmalign = TYPALIGN_CHAR;
3429 578 : break;
3430 :
3431 13742 : case OIDOID:
3432 : case REGTYPEOID:
3433 13742 : elmlen = sizeof(Oid);
3434 13742 : elmbyval = true;
3435 13742 : elmalign = TYPALIGN_INT;
3436 13742 : break;
3437 :
3438 12054 : case TEXTOID:
3439 12054 : elmlen = -1;
3440 12054 : elmbyval = false;
3441 12054 : elmalign = TYPALIGN_INT;
3442 12054 : break;
3443 :
3444 42 : case TIDOID:
3445 42 : elmlen = sizeof(ItemPointerData);
3446 42 : elmbyval = false;
3447 42 : elmalign = TYPALIGN_SHORT;
3448 42 : break;
3449 :
3450 0 : default:
3451 0 : elog(ERROR, "type %u not supported by construct_array_builtin()", elmtype);
3452 : /* keep compiler quiet */
3453 : elmlen = 0;
3454 : elmbyval = false;
3455 : elmalign = 0;
3456 : }
3457 :
3458 168624 : return construct_array(elems, nelems, elmtype, elmlen, elmbyval, elmalign);
3459 : }
3460 :
3461 : /*
3462 : * construct_md_array --- simple method for constructing an array object
3463 : * with arbitrary dimensions and possible NULLs
3464 : *
3465 : * elems: array of Datum items to become the array contents
3466 : * nulls: array of is-null flags (can be NULL if no nulls)
3467 : * ndims: number of dimensions
3468 : * dims: integer array with size of each dimension
3469 : * lbs: integer array with lower bound of each dimension
3470 : * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
3471 : *
3472 : * A palloc'd ndims-D array object is constructed and returned. Note that
3473 : * elem values will be copied into the object even if pass-by-ref type.
3474 : * Also note the result will be 0-D not ndims-D if any dims[i] = 0.
3475 : *
3476 : * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
3477 : * from the system catalogs, given the elmtype. However, the caller is
3478 : * in a better position to cache this info across multiple uses, or even
3479 : * to hard-wire values if the element type is hard-wired.
3480 : */
3481 : ArrayType *
3482 1296532 : construct_md_array(Datum *elems,
3483 : bool *nulls,
3484 : int ndims,
3485 : int *dims,
3486 : int *lbs,
3487 : Oid elmtype, int elmlen, bool elmbyval, char elmalign)
3488 : {
3489 : ArrayType *result;
3490 : bool hasnulls;
3491 : int32 nbytes;
3492 : int32 dataoffset;
3493 : int i;
3494 : int nelems;
3495 :
3496 1296532 : if (ndims < 0) /* we do allow zero-dimension arrays */
3497 0 : ereport(ERROR,
3498 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3499 : errmsg("invalid number of dimensions: %d", ndims)));
3500 1296532 : if (ndims > MAXDIM)
3501 0 : ereport(ERROR,
3502 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3503 : errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
3504 : ndims, MAXDIM)));
3505 :
3506 : /* This checks for overflow of the array dimensions */
3507 1296532 : nelems = ArrayGetNItems(ndims, dims);
3508 1296532 : ArrayCheckBounds(ndims, dims, lbs);
3509 :
3510 : /* if ndims <= 0 or any dims[i] == 0, return empty array */
3511 1296532 : if (nelems <= 0)
3512 50580 : return construct_empty_array(elmtype);
3513 :
3514 : /* compute required space */
3515 1245952 : nbytes = 0;
3516 1245952 : hasnulls = false;
3517 8681274 : for (i = 0; i < nelems; i++)
3518 : {
3519 7435322 : if (nulls && nulls[i])
3520 : {
3521 32672 : hasnulls = true;
3522 32672 : continue;
3523 : }
3524 : /* make sure data is not toasted */
3525 7402650 : if (elmlen == -1)
3526 2237758 : elems[i] = PointerGetDatum(PG_DETOAST_DATUM(elems[i]));
3527 7402650 : nbytes = att_addlength_datum(nbytes, elmlen, elems[i]);
3528 7402650 : nbytes = att_align_nominal(nbytes, elmalign);
3529 : /* check for overflow of total request */
3530 7402650 : if (!AllocSizeIsValid(nbytes))
3531 0 : ereport(ERROR,
3532 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3533 : errmsg("array size exceeds the maximum allowed (%d)",
3534 : (int) MaxAllocSize)));
3535 : }
3536 :
3537 : /* Allocate and initialize result array */
3538 1245952 : if (hasnulls)
3539 : {
3540 17450 : dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nelems);
3541 17450 : nbytes += dataoffset;
3542 : }
3543 : else
3544 : {
3545 1228502 : dataoffset = 0; /* marker for no null bitmap */
3546 1228502 : nbytes += ARR_OVERHEAD_NONULLS(ndims);
3547 : }
3548 1245952 : result = (ArrayType *) palloc0(nbytes);
3549 1245952 : SET_VARSIZE(result, nbytes);
3550 1245952 : result->ndim = ndims;
3551 1245952 : result->dataoffset = dataoffset;
3552 1245952 : result->elemtype = elmtype;
3553 1245952 : memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
3554 1245952 : memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
3555 :
3556 1245952 : CopyArrayEls(result,
3557 : elems, nulls, nelems,
3558 : elmlen, elmbyval, elmalign,
3559 : false);
3560 :
3561 1245952 : return result;
3562 : }
3563 :
3564 : /*
3565 : * construct_empty_array --- make a zero-dimensional array of given type
3566 : */
3567 : ArrayType *
3568 2873882 : construct_empty_array(Oid elmtype)
3569 : {
3570 : ArrayType *result;
3571 :
3572 2873882 : result = (ArrayType *) palloc0(sizeof(ArrayType));
3573 2873882 : SET_VARSIZE(result, sizeof(ArrayType));
3574 2873882 : result->ndim = 0;
3575 2873882 : result->dataoffset = 0;
3576 2873882 : result->elemtype = elmtype;
3577 2873882 : return result;
3578 : }
3579 :
3580 : /*
3581 : * construct_empty_expanded_array: make an empty expanded array
3582 : * given only type information. (metacache can be NULL if not needed.)
3583 : */
3584 : ExpandedArrayHeader *
3585 24 : construct_empty_expanded_array(Oid element_type,
3586 : MemoryContext parentcontext,
3587 : ArrayMetaState *metacache)
3588 : {
3589 24 : ArrayType *array = construct_empty_array(element_type);
3590 : Datum d;
3591 :
3592 24 : d = expand_array(PointerGetDatum(array), parentcontext, metacache);
3593 24 : pfree(array);
3594 24 : return (ExpandedArrayHeader *) DatumGetEOHP(d);
3595 : }
3596 :
3597 : /*
3598 : * deconstruct_array --- simple method for extracting data from an array
3599 : *
3600 : * array: array object to examine (must not be NULL)
3601 : * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
3602 : * elemsp: return value, set to point to palloc'd array of Datum values
3603 : * nullsp: return value, set to point to palloc'd array of isnull markers
3604 : * nelemsp: return value, set to number of extracted values
3605 : *
3606 : * The caller may pass nullsp == NULL if it does not support NULLs in the
3607 : * array. Note that this produces a very uninformative error message,
3608 : * so do it only in cases where a NULL is really not expected.
3609 : *
3610 : * If array elements are pass-by-ref data type, the returned Datums will
3611 : * be pointers into the array object.
3612 : *
3613 : * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
3614 : * from the system catalogs, given the elmtype. However, the caller is
3615 : * in a better position to cache this info across multiple uses, or even
3616 : * to hard-wire values if the element type is hard-wired.
3617 : */
3618 : void
3619 2659470 : deconstruct_array(ArrayType *array,
3620 : Oid elmtype,
3621 : int elmlen, bool elmbyval, char elmalign,
3622 : Datum **elemsp, bool **nullsp, int *nelemsp)
3623 : {
3624 : Datum *elems;
3625 : bool *nulls;
3626 : int nelems;
3627 : char *p;
3628 : bits8 *bitmap;
3629 : int bitmask;
3630 : int i;
3631 :
3632 : Assert(ARR_ELEMTYPE(array) == elmtype);
3633 :
3634 2659470 : nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
3635 2659470 : *elemsp = elems = (Datum *) palloc(nelems * sizeof(Datum));
3636 2659470 : if (nullsp)
3637 1657050 : *nullsp = nulls = (bool *) palloc0(nelems * sizeof(bool));
3638 : else
3639 1002420 : nulls = NULL;
3640 2659470 : *nelemsp = nelems;
3641 :
3642 2659470 : p = ARR_DATA_PTR(array);
3643 2659470 : bitmap = ARR_NULLBITMAP(array);
3644 2659470 : bitmask = 1;
3645 :
3646 42566556 : for (i = 0; i < nelems; i++)
3647 : {
3648 : /* Get source element, checking for NULL */
3649 39907086 : if (bitmap && (*bitmap & bitmask) == 0)
3650 : {
3651 3150 : elems[i] = (Datum) 0;
3652 3150 : if (nulls)
3653 3150 : nulls[i] = true;
3654 : else
3655 0 : ereport(ERROR,
3656 : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
3657 : errmsg("null array element not allowed in this context")));
3658 : }
3659 : else
3660 : {
3661 39903936 : elems[i] = fetch_att(p, elmbyval, elmlen);
3662 39903936 : p = att_addlength_pointer(p, elmlen, p);
3663 39903936 : p = (char *) att_align_nominal(p, elmalign);
3664 : }
3665 :
3666 : /* advance bitmap pointer if any */
3667 39907086 : if (bitmap)
3668 : {
3669 4924 : bitmask <<= 1;
3670 4924 : if (bitmask == 0x100)
3671 : {
3672 66 : bitmap++;
3673 66 : bitmask = 1;
3674 : }
3675 : }
3676 : }
3677 2659470 : }
3678 :
3679 : /*
3680 : * Like deconstruct_array(), where elmtype must be a built-in type, and
3681 : * elmlen/elmbyval/elmalign is looked up from hardcoded data. This is often
3682 : * useful when manipulating arrays from/for system catalogs.
3683 : */
3684 : void
3685 402724 : deconstruct_array_builtin(ArrayType *array,
3686 : Oid elmtype,
3687 : Datum **elemsp, bool **nullsp, int *nelemsp)
3688 : {
3689 : int elmlen;
3690 : bool elmbyval;
3691 : char elmalign;
3692 :
3693 402724 : switch (elmtype)
3694 : {
3695 18 : case CHAROID:
3696 18 : elmlen = 1;
3697 18 : elmbyval = true;
3698 18 : elmalign = TYPALIGN_CHAR;
3699 18 : break;
3700 :
3701 8228 : case CSTRINGOID:
3702 8228 : elmlen = -2;
3703 8228 : elmbyval = false;
3704 8228 : elmalign = TYPALIGN_CHAR;
3705 8228 : break;
3706 :
3707 30 : case FLOAT8OID:
3708 30 : elmlen = sizeof(float8);
3709 30 : elmbyval = FLOAT8PASSBYVAL;
3710 30 : elmalign = TYPALIGN_DOUBLE;
3711 30 : break;
3712 :
3713 4490 : case INT2OID:
3714 4490 : elmlen = sizeof(int16);
3715 4490 : elmbyval = true;
3716 4490 : elmalign = TYPALIGN_SHORT;
3717 4490 : break;
3718 :
3719 230 : case OIDOID:
3720 230 : elmlen = sizeof(Oid);
3721 230 : elmbyval = true;
3722 230 : elmalign = TYPALIGN_INT;
3723 230 : break;
3724 :
3725 389686 : case TEXTOID:
3726 389686 : elmlen = -1;
3727 389686 : elmbyval = false;
3728 389686 : elmalign = TYPALIGN_INT;
3729 389686 : break;
3730 :
3731 42 : case TIDOID:
3732 42 : elmlen = sizeof(ItemPointerData);
3733 42 : elmbyval = false;
3734 42 : elmalign = TYPALIGN_SHORT;
3735 42 : break;
3736 :
3737 0 : default:
3738 0 : elog(ERROR, "type %u not supported by deconstruct_array_builtin()", elmtype);
3739 : /* keep compiler quiet */
3740 : elmlen = 0;
3741 : elmbyval = false;
3742 : elmalign = 0;
3743 : }
3744 :
3745 402724 : deconstruct_array(array, elmtype, elmlen, elmbyval, elmalign, elemsp, nullsp, nelemsp);
3746 402724 : }
3747 :
3748 : /*
3749 : * array_contains_nulls --- detect whether an array has any null elements
3750 : *
3751 : * This gives an accurate answer, whereas testing ARR_HASNULL only tells
3752 : * if the array *might* contain a null.
3753 : */
3754 : bool
3755 70380 : array_contains_nulls(ArrayType *array)
3756 : {
3757 : int nelems;
3758 : bits8 *bitmap;
3759 : int bitmask;
3760 :
3761 : /* Easy answer if there's no null bitmap */
3762 70380 : if (!ARR_HASNULL(array))
3763 70330 : return false;
3764 :
3765 50 : nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
3766 :
3767 50 : bitmap = ARR_NULLBITMAP(array);
3768 :
3769 : /* check whole bytes of the bitmap byte-at-a-time */
3770 50 : while (nelems >= 8)
3771 : {
3772 12 : if (*bitmap != 0xFF)
3773 12 : return true;
3774 0 : bitmap++;
3775 0 : nelems -= 8;
3776 : }
3777 :
3778 : /* check last partial byte */
3779 38 : bitmask = 1;
3780 86 : while (nelems > 0)
3781 : {
3782 86 : if ((*bitmap & bitmask) == 0)
3783 38 : return true;
3784 48 : bitmask <<= 1;
3785 48 : nelems--;
3786 : }
3787 :
3788 0 : return false;
3789 : }
3790 :
3791 :
3792 : /*
3793 : * array_eq :
3794 : * compares two arrays for equality
3795 : * result :
3796 : * returns true if the arrays are equal, false otherwise.
3797 : *
3798 : * Note: we do not use array_cmp here, since equality may be meaningful in
3799 : * datatypes that don't have a total ordering (and hence no btree support).
3800 : */
3801 : Datum
3802 184044 : array_eq(PG_FUNCTION_ARGS)
3803 : {
3804 184044 : LOCAL_FCINFO(locfcinfo, 2);
3805 184044 : AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
3806 184044 : AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
3807 184044 : Oid collation = PG_GET_COLLATION();
3808 184044 : int ndims1 = AARR_NDIM(array1);
3809 184044 : int ndims2 = AARR_NDIM(array2);
3810 184044 : int *dims1 = AARR_DIMS(array1);
3811 184044 : int *dims2 = AARR_DIMS(array2);
3812 184044 : int *lbs1 = AARR_LBOUND(array1);
3813 184044 : int *lbs2 = AARR_LBOUND(array2);
3814 184044 : Oid element_type = AARR_ELEMTYPE(array1);
3815 184044 : bool result = true;
3816 : int nitems;
3817 : TypeCacheEntry *typentry;
3818 : int typlen;
3819 : bool typbyval;
3820 : char typalign;
3821 : array_iter it1;
3822 : array_iter it2;
3823 : int i;
3824 :
3825 184044 : if (element_type != AARR_ELEMTYPE(array2))
3826 0 : ereport(ERROR,
3827 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3828 : errmsg("cannot compare arrays of different element types")));
3829 :
3830 : /* fast path if the arrays do not have the same dimensionality */
3831 184044 : if (ndims1 != ndims2 ||
3832 169264 : memcmp(dims1, dims2, ndims1 * sizeof(int)) != 0 ||
3833 121740 : memcmp(lbs1, lbs2, ndims1 * sizeof(int)) != 0)
3834 62304 : result = false;
3835 : else
3836 : {
3837 : /*
3838 : * We arrange to look up the equality function only once per series of
3839 : * calls, assuming the element type doesn't change underneath us. The
3840 : * typcache is used so that we have no memory leakage when being used
3841 : * as an index support function.
3842 : */
3843 121740 : typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3844 121740 : if (typentry == NULL ||
3845 119670 : typentry->type_id != element_type)
3846 : {
3847 2070 : typentry = lookup_type_cache(element_type,
3848 : TYPECACHE_EQ_OPR_FINFO);
3849 2070 : if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
3850 0 : ereport(ERROR,
3851 : (errcode(ERRCODE_UNDEFINED_FUNCTION),
3852 : errmsg("could not identify an equality operator for type %s",
3853 : format_type_be(element_type))));
3854 2070 : fcinfo->flinfo->fn_extra = (void *) typentry;
3855 : }
3856 121740 : typlen = typentry->typlen;
3857 121740 : typbyval = typentry->typbyval;
3858 121740 : typalign = typentry->typalign;
3859 :
3860 : /*
3861 : * apply the operator to each pair of array elements.
3862 : */
3863 121740 : InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
3864 : collation, NULL, NULL);
3865 :
3866 : /* Loop over source data */
3867 121740 : nitems = ArrayGetNItems(ndims1, dims1);
3868 121740 : array_iter_setup(&it1, array1);
3869 121740 : array_iter_setup(&it2, array2);
3870 :
3871 291838 : for (i = 0; i < nitems; i++)
3872 : {
3873 : Datum elt1;
3874 : Datum elt2;
3875 : bool isnull1;
3876 : bool isnull2;
3877 : bool oprresult;
3878 :
3879 : /* Get elements, checking for NULL */
3880 189426 : elt1 = array_iter_next(&it1, &isnull1, i,
3881 : typlen, typbyval, typalign);
3882 189426 : elt2 = array_iter_next(&it2, &isnull2, i,
3883 : typlen, typbyval, typalign);
3884 :
3885 : /*
3886 : * We consider two NULLs equal; NULL and not-NULL are unequal.
3887 : */
3888 189426 : if (isnull1 && isnull2)
3889 22 : continue;
3890 189404 : if (isnull1 || isnull2)
3891 : {
3892 108 : result = false;
3893 19328 : break;
3894 : }
3895 :
3896 : /*
3897 : * Apply the operator to the element pair; treat NULL as false
3898 : */
3899 189296 : locfcinfo->args[0].value = elt1;
3900 189296 : locfcinfo->args[0].isnull = false;
3901 189296 : locfcinfo->args[1].value = elt2;
3902 189296 : locfcinfo->args[1].isnull = false;
3903 189296 : locfcinfo->isnull = false;
3904 189296 : oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
3905 189296 : if (locfcinfo->isnull || !oprresult)
3906 : {
3907 19220 : result = false;
3908 19220 : break;
3909 : }
3910 : }
3911 : }
3912 :
3913 : /* Avoid leaking memory when handed toasted input. */
3914 184044 : AARR_FREE_IF_COPY(array1, 0);
3915 184044 : AARR_FREE_IF_COPY(array2, 1);
3916 :
3917 184044 : PG_RETURN_BOOL(result);
3918 : }
3919 :
3920 :
3921 : /*-----------------------------------------------------------------------------
3922 : * array-array bool operators:
3923 : * Given two arrays, iterate comparison operators
3924 : * over the array. Uses logic similar to text comparison
3925 : * functions, except element-by-element instead of
3926 : * character-by-character.
3927 : *----------------------------------------------------------------------------
3928 : */
3929 :
3930 : Datum
3931 924 : array_ne(PG_FUNCTION_ARGS)
3932 : {
3933 924 : PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo)));
3934 : }
3935 :
3936 : Datum
3937 18 : array_lt(PG_FUNCTION_ARGS)
3938 : {
3939 18 : PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
3940 : }
3941 :
3942 : Datum
3943 6 : array_gt(PG_FUNCTION_ARGS)
3944 : {
3945 6 : PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
3946 : }
3947 :
3948 : Datum
3949 18 : array_le(PG_FUNCTION_ARGS)
3950 : {
3951 18 : PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
3952 : }
3953 :
3954 : Datum
3955 6 : array_ge(PG_FUNCTION_ARGS)
3956 : {
3957 6 : PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
3958 : }
3959 :
3960 : Datum
3961 7653498 : btarraycmp(PG_FUNCTION_ARGS)
3962 : {
3963 7653498 : PG_RETURN_INT32(array_cmp(fcinfo));
3964 : }
3965 :
3966 : /*
3967 : * array_cmp()
3968 : * Internal comparison function for arrays.
3969 : *
3970 : * Returns -1, 0 or 1
3971 : */
3972 : static int
3973 7654092 : array_cmp(FunctionCallInfo fcinfo)
3974 : {
3975 7654092 : LOCAL_FCINFO(locfcinfo, 2);
3976 7654092 : AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
3977 7654092 : AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
3978 7654092 : Oid collation = PG_GET_COLLATION();
3979 7654092 : int ndims1 = AARR_NDIM(array1);
3980 7654092 : int ndims2 = AARR_NDIM(array2);
3981 7654092 : int *dims1 = AARR_DIMS(array1);
3982 7654092 : int *dims2 = AARR_DIMS(array2);
3983 7654092 : int nitems1 = ArrayGetNItems(ndims1, dims1);
3984 7654092 : int nitems2 = ArrayGetNItems(ndims2, dims2);
3985 7654092 : Oid element_type = AARR_ELEMTYPE(array1);
3986 7654092 : int result = 0;
3987 : TypeCacheEntry *typentry;
3988 : int typlen;
3989 : bool typbyval;
3990 : char typalign;
3991 : int min_nitems;
3992 : array_iter it1;
3993 : array_iter it2;
3994 : int i;
3995 :
3996 7654092 : if (element_type != AARR_ELEMTYPE(array2))
3997 6 : ereport(ERROR,
3998 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3999 : errmsg("cannot compare arrays of different element types")));
4000 :
4001 : /*
4002 : * We arrange to look up the comparison function only once per series of
4003 : * calls, assuming the element type doesn't change underneath us. The
4004 : * typcache is used so that we have no memory leakage when being used as
4005 : * an index support function.
4006 : */
4007 7654086 : typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
4008 7654086 : if (typentry == NULL ||
4009 7652444 : typentry->type_id != element_type)
4010 : {
4011 1642 : typentry = lookup_type_cache(element_type,
4012 : TYPECACHE_CMP_PROC_FINFO);
4013 1642 : if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
4014 0 : ereport(ERROR,
4015 : (errcode(ERRCODE_UNDEFINED_FUNCTION),
4016 : errmsg("could not identify a comparison function for type %s",
4017 : format_type_be(element_type))));
4018 1642 : fcinfo->flinfo->fn_extra = (void *) typentry;
4019 : }
4020 7654086 : typlen = typentry->typlen;
4021 7654086 : typbyval = typentry->typbyval;
4022 7654086 : typalign = typentry->typalign;
4023 :
4024 : /*
4025 : * apply the operator to each pair of array elements.
4026 : */
4027 7654086 : InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
4028 : collation, NULL, NULL);
4029 :
4030 : /* Loop over source data */
4031 7654086 : min_nitems = Min(nitems1, nitems2);
4032 7654086 : array_iter_setup(&it1, array1);
4033 7654086 : array_iter_setup(&it2, array2);
4034 :
4035 15908922 : for (i = 0; i < min_nitems; i++)
4036 : {
4037 : Datum elt1;
4038 : Datum elt2;
4039 : bool isnull1;
4040 : bool isnull2;
4041 : int32 cmpresult;
4042 :
4043 : /* Get elements, checking for NULL */
4044 13874720 : elt1 = array_iter_next(&it1, &isnull1, i, typlen, typbyval, typalign);
4045 13874720 : elt2 = array_iter_next(&it2, &isnull2, i, typlen, typbyval, typalign);
4046 :
4047 : /*
4048 : * We consider two NULLs equal; NULL > not-NULL.
4049 : */
4050 13874720 : if (isnull1 && isnull2)
4051 8254836 : continue;
4052 13874710 : if (isnull1)
4053 : {
4054 : /* arg1 is greater than arg2 */
4055 96 : result = 1;
4056 5619884 : break;
4057 : }
4058 13874614 : if (isnull2)
4059 : {
4060 : /* arg1 is less than arg2 */
4061 180 : result = -1;
4062 180 : break;
4063 : }
4064 :
4065 : /* Compare the pair of elements */
4066 13874434 : locfcinfo->args[0].value = elt1;
4067 13874434 : locfcinfo->args[0].isnull = false;
4068 13874434 : locfcinfo->args[1].value = elt2;
4069 13874434 : locfcinfo->args[1].isnull = false;
4070 13874434 : cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
4071 :
4072 : /* We don't expect comparison support functions to return null */
4073 : Assert(!locfcinfo->isnull);
4074 :
4075 13874434 : if (cmpresult == 0)
4076 8254826 : continue; /* equal */
4077 :
4078 5619608 : if (cmpresult < 0)
4079 : {
4080 : /* arg1 is less than arg2 */
4081 3229782 : result = -1;
4082 3229782 : break;
4083 : }
4084 : else
4085 : {
4086 : /* arg1 is greater than arg2 */
4087 2389826 : result = 1;
4088 2389826 : break;
4089 : }
4090 : }
4091 :
4092 : /*
4093 : * If arrays contain same data (up to end of shorter one), apply
4094 : * additional rules to sort by dimensionality. The relative significance
4095 : * of the different bits of information is historical; mainly we just care
4096 : * that we don't say "equal" for arrays of different dimensionality.
4097 : */
4098 7654086 : if (result == 0)
4099 : {
4100 2034202 : if (nitems1 != nitems2)
4101 132016 : result = (nitems1 < nitems2) ? -1 : 1;
4102 1902186 : else if (ndims1 != ndims2)
4103 0 : result = (ndims1 < ndims2) ? -1 : 1;
4104 : else
4105 : {
4106 3804320 : for (i = 0; i < ndims1; i++)
4107 : {
4108 1902134 : if (dims1[i] != dims2[i])
4109 : {
4110 0 : result = (dims1[i] < dims2[i]) ? -1 : 1;
4111 0 : break;
4112 : }
4113 : }
4114 1902186 : if (result == 0)
4115 : {
4116 1902186 : int *lbound1 = AARR_LBOUND(array1);
4117 1902186 : int *lbound2 = AARR_LBOUND(array2);
4118 :
4119 3804320 : for (i = 0; i < ndims1; i++)
4120 : {
4121 1902134 : if (lbound1[i] != lbound2[i])
4122 : {
4123 0 : result = (lbound1[i] < lbound2[i]) ? -1 : 1;
4124 0 : break;
4125 : }
4126 : }
4127 : }
4128 : }
4129 : }
4130 :
4131 : /* Avoid leaking memory when handed toasted input. */
4132 7654086 : AARR_FREE_IF_COPY(array1, 0);
4133 7654086 : AARR_FREE_IF_COPY(array2, 1);
4134 :
4135 7654086 : return result;
4136 : }
4137 :
4138 :
4139 : /*-----------------------------------------------------------------------------
4140 : * array hashing
4141 : * Hash the elements and combine the results.
4142 : *----------------------------------------------------------------------------
4143 : */
4144 :
4145 : Datum
4146 35794 : hash_array(PG_FUNCTION_ARGS)
4147 : {
4148 35794 : LOCAL_FCINFO(locfcinfo, 1);
4149 35794 : AnyArrayType *array = PG_GETARG_ANY_ARRAY_P(0);
4150 35794 : int ndims = AARR_NDIM(array);
4151 35794 : int *dims = AARR_DIMS(array);
4152 35794 : Oid element_type = AARR_ELEMTYPE(array);
4153 35794 : uint32 result = 1;
4154 : int nitems;
4155 : TypeCacheEntry *typentry;
4156 : int typlen;
4157 : bool typbyval;
4158 : char typalign;
4159 : int i;
4160 : array_iter iter;
4161 :
4162 : /*
4163 : * We arrange to look up the hash function only once per series of calls,
4164 : * assuming the element type doesn't change underneath us. The typcache
4165 : * is used so that we have no memory leakage when being used as an index
4166 : * support function.
4167 : */
4168 35794 : typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
4169 35794 : if (typentry == NULL ||
4170 35476 : typentry->type_id != element_type)
4171 : {
4172 318 : typentry = lookup_type_cache(element_type,
4173 : TYPECACHE_HASH_PROC_FINFO);
4174 318 : if (!OidIsValid(typentry->hash_proc_finfo.fn_oid) && element_type != RECORDOID)
4175 6 : ereport(ERROR,
4176 : (errcode(ERRCODE_UNDEFINED_FUNCTION),
4177 : errmsg("could not identify a hash function for type %s",
4178 : format_type_be(element_type))));
4179 :
4180 : /*
4181 : * The type cache doesn't believe that record is hashable (see
4182 : * cache_record_field_properties()), but since we're here, we're
4183 : * committed to hashing, so we can assume it does. Worst case, if any
4184 : * components of the record don't support hashing, we will fail at
4185 : * execution.
4186 : */
4187 312 : if (element_type == RECORDOID)
4188 : {
4189 : MemoryContext oldcontext;
4190 : TypeCacheEntry *record_typentry;
4191 :
4192 18 : oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
4193 :
4194 : /*
4195 : * Make fake type cache entry structure. Note that we can't just
4196 : * modify typentry, since that points directly into the type
4197 : * cache.
4198 : */
4199 18 : record_typentry = palloc0(sizeof(*record_typentry));
4200 18 : record_typentry->type_id = element_type;
4201 :
4202 : /* fill in what we need below */
4203 18 : record_typentry->typlen = typentry->typlen;
4204 18 : record_typentry->typbyval = typentry->typbyval;
4205 18 : record_typentry->typalign = typentry->typalign;
4206 18 : fmgr_info(F_HASH_RECORD, &record_typentry->hash_proc_finfo);
4207 :
4208 18 : MemoryContextSwitchTo(oldcontext);
4209 :
4210 18 : typentry = record_typentry;
4211 : }
4212 :
4213 312 : fcinfo->flinfo->fn_extra = (void *) typentry;
4214 : }
4215 :
4216 35788 : typlen = typentry->typlen;
4217 35788 : typbyval = typentry->typbyval;
4218 35788 : typalign = typentry->typalign;
4219 :
4220 : /*
4221 : * apply the hash function to each array element.
4222 : */
4223 35788 : InitFunctionCallInfoData(*locfcinfo, &typentry->hash_proc_finfo, 1,
4224 : PG_GET_COLLATION(), NULL, NULL);
4225 :
4226 : /* Loop over source data */
4227 35788 : nitems = ArrayGetNItems(ndims, dims);
4228 35788 : array_iter_setup(&iter, array);
4229 :
4230 107422 : for (i = 0; i < nitems; i++)
4231 : {
4232 : Datum elt;
4233 : bool isnull;
4234 : uint32 elthash;
4235 :
4236 : /* Get element, checking for NULL */
4237 71634 : elt = array_iter_next(&iter, &isnull, i, typlen, typbyval, typalign);
4238 :
4239 71634 : if (isnull)
4240 : {
4241 : /* Treat nulls as having hashvalue 0 */
4242 0 : elthash = 0;
4243 : }
4244 : else
4245 : {
4246 : /* Apply the hash function */
4247 71634 : locfcinfo->args[0].value = elt;
4248 71634 : locfcinfo->args[0].isnull = false;
4249 71634 : elthash = DatumGetUInt32(FunctionCallInvoke(locfcinfo));
4250 : /* We don't expect hash functions to return null */
4251 : Assert(!locfcinfo->isnull);
4252 : }
4253 :
4254 : /*
4255 : * Combine hash values of successive elements by multiplying the
4256 : * current value by 31 and adding on the new element's hash value.
4257 : *
4258 : * The result is a sum in which each element's hash value is
4259 : * multiplied by a different power of 31. This is modulo 2^32
4260 : * arithmetic, and the powers of 31 modulo 2^32 form a cyclic group of
4261 : * order 2^27. So for arrays of up to 2^27 elements, each element's
4262 : * hash value is multiplied by a different (odd) number, resulting in
4263 : * a good mixing of all the elements' hash values.
4264 : */
4265 71634 : result = (result << 5) - result + elthash;
4266 : }
4267 :
4268 : /* Avoid leaking memory when handed toasted input. */
4269 35788 : AARR_FREE_IF_COPY(array, 0);
4270 :
4271 35788 : PG_RETURN_UINT32(result);
4272 : }
4273 :
4274 : /*
4275 : * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
4276 : * Otherwise, similar to hash_array.
4277 : */
4278 : Datum
4279 144 : hash_array_extended(PG_FUNCTION_ARGS)
4280 : {
4281 144 : LOCAL_FCINFO(locfcinfo, 2);
4282 144 : AnyArrayType *array = PG_GETARG_ANY_ARRAY_P(0);
4283 144 : uint64 seed = PG_GETARG_INT64(1);
4284 144 : int ndims = AARR_NDIM(array);
4285 144 : int *dims = AARR_DIMS(array);
4286 144 : Oid element_type = AARR_ELEMTYPE(array);
4287 144 : uint64 result = 1;
4288 : int nitems;
4289 : TypeCacheEntry *typentry;
4290 : int typlen;
4291 : bool typbyval;
4292 : char typalign;
4293 : int i;
4294 : array_iter iter;
4295 :
4296 144 : typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
4297 144 : if (typentry == NULL ||
4298 84 : typentry->type_id != element_type)
4299 : {
4300 60 : typentry = lookup_type_cache(element_type,
4301 : TYPECACHE_HASH_EXTENDED_PROC_FINFO);
4302 60 : if (!OidIsValid(typentry->hash_extended_proc_finfo.fn_oid))
4303 6 : ereport(ERROR,
4304 : (errcode(ERRCODE_UNDEFINED_FUNCTION),
4305 : errmsg("could not identify an extended hash function for type %s",
4306 : format_type_be(element_type))));
4307 54 : fcinfo->flinfo->fn_extra = (void *) typentry;
4308 : }
4309 138 : typlen = typentry->typlen;
4310 138 : typbyval = typentry->typbyval;
4311 138 : typalign = typentry->typalign;
4312 :
4313 138 : InitFunctionCallInfoData(*locfcinfo, &typentry->hash_extended_proc_finfo, 2,
4314 : PG_GET_COLLATION(), NULL, NULL);
4315 :
4316 : /* Loop over source data */
4317 138 : nitems = ArrayGetNItems(ndims, dims);
4318 138 : array_iter_setup(&iter, array);
4319 :
4320 456 : for (i = 0; i < nitems; i++)
4321 : {
4322 : Datum elt;
4323 : bool isnull;
4324 : uint64 elthash;
4325 :
4326 : /* Get element, checking for NULL */
4327 318 : elt = array_iter_next(&iter, &isnull, i, typlen, typbyval, typalign);
4328 :
4329 318 : if (isnull)
4330 : {
4331 0 : elthash = 0;
4332 : }
4333 : else
4334 : {
4335 : /* Apply the hash function */
4336 318 : locfcinfo->args[0].value = elt;
4337 318 : locfcinfo->args[0].isnull = false;
4338 318 : locfcinfo->args[1].value = Int64GetDatum(seed);
4339 318 : locfcinfo->args[1].isnull = false;
4340 318 : elthash = DatumGetUInt64(FunctionCallInvoke(locfcinfo));
4341 : /* We don't expect hash functions to return null */
4342 : Assert(!locfcinfo->isnull);
4343 : }
4344 :
4345 318 : result = (result << 5) - result + elthash;
4346 : }
4347 :
4348 138 : AARR_FREE_IF_COPY(array, 0);
4349 :
4350 138 : PG_RETURN_UINT64(result);
4351 : }
4352 :
4353 :
4354 : /*-----------------------------------------------------------------------------
4355 : * array overlap/containment comparisons
4356 : * These use the same methods of comparing array elements as array_eq.
4357 : * We consider only the elements of the arrays, ignoring dimensionality.
4358 : *----------------------------------------------------------------------------
4359 : */
4360 :
4361 : /*
4362 : * array_contain_compare :
4363 : * compares two arrays for overlap/containment
4364 : *
4365 : * When matchall is true, return true if all members of array1 are in array2.
4366 : * When matchall is false, return true if any members of array1 are in array2.
4367 : */
4368 : static bool
4369 22452 : array_contain_compare(AnyArrayType *array1, AnyArrayType *array2, Oid collation,
4370 : bool matchall, void **fn_extra)
4371 : {
4372 22452 : LOCAL_FCINFO(locfcinfo, 2);
4373 22452 : bool result = matchall;
4374 22452 : Oid element_type = AARR_ELEMTYPE(array1);
4375 : TypeCacheEntry *typentry;
4376 : int nelems1;
4377 : Datum *values2;
4378 : bool *nulls2;
4379 : int nelems2;
4380 : int typlen;
4381 : bool typbyval;
4382 : char typalign;
4383 : int i;
4384 : int j;
4385 : array_iter it1;
4386 :
4387 22452 : if (element_type != AARR_ELEMTYPE(array2))
4388 0 : ereport(ERROR,
4389 : (errcode(ERRCODE_DATATYPE_MISMATCH),
4390 : errmsg("cannot compare arrays of different element types")));
4391 :
4392 : /*
4393 : * We arrange to look up the equality function only once per series of
4394 : * calls, assuming the element type doesn't change underneath us. The
4395 : * typcache is used so that we have no memory leakage when being used as
4396 : * an index support function.
4397 : */
4398 22452 : typentry = (TypeCacheEntry *) *fn_extra;
4399 22452 : if (typentry == NULL ||
4400 21858 : typentry->type_id != element_type)
4401 : {
4402 594 : typentry = lookup_type_cache(element_type,
4403 : TYPECACHE_EQ_OPR_FINFO);
4404 594 : if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
4405 0 : ereport(ERROR,
4406 : (errcode(ERRCODE_UNDEFINED_FUNCTION),
4407 : errmsg("could not identify an equality operator for type %s",
4408 : format_type_be(element_type))));
4409 594 : *fn_extra = (void *) typentry;
4410 : }
4411 22452 : typlen = typentry->typlen;
4412 22452 : typbyval = typentry->typbyval;
4413 22452 : typalign = typentry->typalign;
4414 :
4415 : /*
4416 : * Since we probably will need to scan array2 multiple times, it's
4417 : * worthwhile to use deconstruct_array on it. We scan array1 the hard way
4418 : * however, since we very likely won't need to look at all of it.
4419 : */
4420 22452 : if (VARATT_IS_EXPANDED_HEADER(array2))
4421 : {
4422 : /* This should be safe even if input is read-only */
4423 5112 : deconstruct_expanded_array(&(array2->xpn));
4424 5112 : values2 = array2->xpn.dvalues;
4425 5112 : nulls2 = array2->xpn.dnulls;
4426 5112 : nelems2 = array2->xpn.nelems;
4427 : }
4428 : else
4429 17340 : deconstruct_array((ArrayType *) array2,
4430 : element_type, typlen, typbyval, typalign,
4431 : &values2, &nulls2, &nelems2);
4432 :
4433 : /*
4434 : * Apply the comparison operator to each pair of array elements.
4435 : */
4436 22452 : InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
4437 : collation, NULL, NULL);
4438 :
4439 : /* Loop over source data */
4440 22452 : nelems1 = ArrayGetNItems(AARR_NDIM(array1), AARR_DIMS(array1));
4441 22452 : array_iter_setup(&it1, array1);
4442 :
4443 420322 : for (i = 0; i < nelems1; i++)
4444 : {
4445 : Datum elt1;
4446 : bool isnull1;
4447 :
4448 : /* Get element, checking for NULL */
4449 407316 : elt1 = array_iter_next(&it1, &isnull1, i, typlen, typbyval, typalign);
4450 :
4451 : /*
4452 : * We assume that the comparison operator is strict, so a NULL can't
4453 : * match anything. XXX this diverges from the "NULL=NULL" behavior of
4454 : * array_eq, should we act like that?
4455 : */
4456 407316 : if (isnull1)
4457 : {
4458 1320 : if (matchall)
4459 : {
4460 1260 : result = false;
4461 9446 : break;
4462 : }
4463 60 : continue;
4464 : }
4465 :
4466 18416346 : for (j = 0; j < nelems2; j++)
4467 : {
4468 18376942 : Datum elt2 = values2[j];
4469 18376942 : bool isnull2 = nulls2 ? nulls2[j] : false;
4470 : bool oprresult;
4471 :
4472 18376942 : if (isnull2)
4473 7212 : continue; /* can't match */
4474 :
4475 : /*
4476 : * Apply the operator to the element pair; treat NULL as false
4477 : */
4478 18369730 : locfcinfo->args[0].value = elt1;
4479 18369730 : locfcinfo->args[0].isnull = false;
4480 18369730 : locfcinfo->args[1].value = elt2;
4481 18369730 : locfcinfo->args[1].isnull = false;
4482 18369730 : locfcinfo->isnull = false;
4483 18369730 : oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
4484 18369730 : if (!locfcinfo->isnull && oprresult)
4485 366592 : break;
4486 : }
4487 :
4488 405996 : if (j < nelems2)
4489 : {
4490 : /* found a match for elt1 */
4491 366592 : if (!matchall)
4492 : {
4493 228 : result = true;
4494 228 : break;
4495 : }
4496 : }
4497 : else
4498 : {
4499 : /* no match for elt1 */
4500 39404 : if (matchall)
4501 : {
4502 7958 : result = false;
4503 7958 : break;
4504 : }
4505 : }
4506 : }
4507 :
4508 22452 : return result;
4509 : }
4510 :
4511 : Datum
4512 6120 : arrayoverlap(PG_FUNCTION_ARGS)
4513 : {
4514 6120 : AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
4515 6120 : AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
4516 6120 : Oid collation = PG_GET_COLLATION();
4517 : bool result;
4518 :
4519 6120 : result = array_contain_compare(array1, array2, collation, false,
4520 6120 : &fcinfo->flinfo->fn_extra);
4521 :
4522 : /* Avoid leaking memory when handed toasted input. */
4523 6120 : AARR_FREE_IF_COPY(array1, 0);
4524 6120 : AARR_FREE_IF_COPY(array2, 1);
4525 :
4526 6120 : PG_RETURN_BOOL(result);
4527 : }
4528 :
4529 : Datum
4530 9816 : arraycontains(PG_FUNCTION_ARGS)
4531 : {
4532 9816 : AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
4533 9816 : AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
4534 9816 : Oid collation = PG_GET_COLLATION();
4535 : bool result;
4536 :
4537 9816 : result = array_contain_compare(array2, array1, collation, true,
4538 9816 : &fcinfo->flinfo->fn_extra);
4539 :
4540 : /* Avoid leaking memory when handed toasted input. */
4541 9816 : AARR_FREE_IF_COPY(array1, 0);
4542 9816 : AARR_FREE_IF_COPY(array2, 1);
4543 :
4544 9816 : PG_RETURN_BOOL(result);
4545 : }
4546 :
4547 : Datum
4548 6516 : arraycontained(PG_FUNCTION_ARGS)
4549 : {
4550 6516 : AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
4551 6516 : AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
4552 6516 : Oid collation = PG_GET_COLLATION();
4553 : bool result;
4554 :
4555 6516 : result = array_contain_compare(array1, array2, collation, true,
4556 6516 : &fcinfo->flinfo->fn_extra);
4557 :
4558 : /* Avoid leaking memory when handed toasted input. */
4559 6516 : AARR_FREE_IF_COPY(array1, 0);
4560 6516 : AARR_FREE_IF_COPY(array2, 1);
4561 :
4562 6516 : PG_RETURN_BOOL(result);
4563 : }
4564 :
4565 :
4566 : /*-----------------------------------------------------------------------------
4567 : * Array iteration functions
4568 : * These functions are used to iterate efficiently through arrays
4569 : *-----------------------------------------------------------------------------
4570 : */
4571 :
4572 : /*
4573 : * array_create_iterator --- set up to iterate through an array
4574 : *
4575 : * If slice_ndim is zero, we will iterate element-by-element; the returned
4576 : * datums are of the array's element type.
4577 : *
4578 : * If slice_ndim is 1..ARR_NDIM(arr), we will iterate by slices: the
4579 : * returned datums are of the same array type as 'arr', but of size
4580 : * equal to the rightmost N dimensions of 'arr'.
4581 : *
4582 : * The passed-in array must remain valid for the lifetime of the iterator.
4583 : */
4584 : ArrayIterator
4585 330 : array_create_iterator(ArrayType *arr, int slice_ndim, ArrayMetaState *mstate)
4586 : {
4587 330 : ArrayIterator iterator = palloc0(sizeof(ArrayIteratorData));
4588 :
4589 : /*
4590 : * Sanity-check inputs --- caller should have got this right already
4591 : */
4592 : Assert(PointerIsValid(arr));
4593 330 : if (slice_ndim < 0 || slice_ndim > ARR_NDIM(arr))
4594 0 : elog(ERROR, "invalid arguments to array_create_iterator");
4595 :
4596 : /*
4597 : * Remember basic info about the array and its element type
4598 : */
4599 330 : iterator->arr = arr;
4600 330 : iterator->nullbitmap = ARR_NULLBITMAP(arr);
4601 330 : iterator->nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
4602 :
4603 330 : if (mstate != NULL)
4604 : {
4605 : Assert(mstate->element_type == ARR_ELEMTYPE(arr));
4606 :
4607 258 : iterator->typlen = mstate->typlen;
4608 258 : iterator->typbyval = mstate->typbyval;
4609 258 : iterator->typalign = mstate->typalign;
4610 : }
4611 : else
4612 72 : get_typlenbyvalalign(ARR_ELEMTYPE(arr),
4613 : &iterator->typlen,
4614 : &iterator->typbyval,
4615 : &iterator->typalign);
4616 :
4617 : /*
4618 : * Remember the slicing parameters.
4619 : */
4620 330 : iterator->slice_ndim = slice_ndim;
4621 :
4622 330 : if (slice_ndim > 0)
4623 : {
4624 : /*
4625 : * Get pointers into the array's dims and lbound arrays to represent
4626 : * the dims/lbound arrays of a slice. These are the same as the
4627 : * rightmost N dimensions of the array.
4628 : */
4629 36 : iterator->slice_dims = ARR_DIMS(arr) + ARR_NDIM(arr) - slice_ndim;
4630 36 : iterator->slice_lbound = ARR_LBOUND(arr) + ARR_NDIM(arr) - slice_ndim;
4631 :
4632 : /*
4633 : * Compute number of elements in a slice.
4634 : */
4635 72 : iterator->slice_len = ArrayGetNItems(slice_ndim,
4636 36 : iterator->slice_dims);
4637 :
4638 : /*
4639 : * Create workspace for building sub-arrays.
4640 : */
4641 36 : iterator->slice_values = (Datum *)
4642 36 : palloc(iterator->slice_len * sizeof(Datum));
4643 36 : iterator->slice_nulls = (bool *)
4644 36 : palloc(iterator->slice_len * sizeof(bool));
4645 : }
4646 :
4647 : /*
4648 : * Initialize our data pointer and linear element number. These will
4649 : * advance through the array during array_iterate().
4650 : */
4651 330 : iterator->data_ptr = ARR_DATA_PTR(arr);
4652 330 : iterator->current_item = 0;
4653 :
4654 330 : return iterator;
4655 : }
4656 :
4657 : /*
4658 : * Iterate through the array referenced by 'iterator'.
4659 : *
4660 : * As long as there is another element (or slice), return it into
4661 : * *value / *isnull, and return true. Return false when no more data.
4662 : */
4663 : bool
4664 1692 : array_iterate(ArrayIterator iterator, Datum *value, bool *isnull)
4665 : {
4666 : /* Done if we have reached the end of the array */
4667 1692 : if (iterator->current_item >= iterator->nitems)
4668 150 : return false;
4669 :
4670 1542 : if (iterator->slice_ndim == 0)
4671 : {
4672 : /*
4673 : * Scalar case: return one element.
4674 : */
4675 1488 : if (array_get_isnull(iterator->nullbitmap, iterator->current_item++))
4676 : {
4677 24 : *isnull = true;
4678 24 : *value = (Datum) 0;
4679 : }
4680 : else
4681 : {
4682 : /* non-NULL, so fetch the individual Datum to return */
4683 1464 : char *p = iterator->data_ptr;
4684 :
4685 1464 : *isnull = false;
4686 1464 : *value = fetch_att(p, iterator->typbyval, iterator->typlen);
4687 :
4688 : /* Move our data pointer forward to the next element */
4689 1464 : p = att_addlength_pointer(p, iterator->typlen, p);
4690 1464 : p = (char *) att_align_nominal(p, iterator->typalign);
4691 1464 : iterator->data_ptr = p;
4692 : }
4693 : }
4694 : else
4695 : {
4696 : /*
4697 : * Slice case: build and return an array of the requested size.
4698 : */
4699 : ArrayType *result;
4700 54 : Datum *values = iterator->slice_values;
4701 54 : bool *nulls = iterator->slice_nulls;
4702 54 : char *p = iterator->data_ptr;
4703 : int i;
4704 :
4705 192 : for (i = 0; i < iterator->slice_len; i++)
4706 : {
4707 138 : if (array_get_isnull(iterator->nullbitmap,
4708 138 : iterator->current_item++))
4709 : {
4710 0 : nulls[i] = true;
4711 0 : values[i] = (Datum) 0;
4712 : }
4713 : else
4714 : {
4715 138 : nulls[i] = false;
4716 138 : values[i] = fetch_att(p, iterator->typbyval, iterator->typlen);
4717 :
4718 : /* Move our data pointer forward to the next element */
4719 138 : p = att_addlength_pointer(p, iterator->typlen, p);
4720 138 : p = (char *) att_align_nominal(p, iterator->typalign);
4721 : }
4722 : }
4723 :
4724 54 : iterator->data_ptr = p;
4725 :
4726 54 : result = construct_md_array(values,
4727 : nulls,
4728 : iterator->slice_ndim,
4729 : iterator->slice_dims,
4730 : iterator->slice_lbound,
4731 54 : ARR_ELEMTYPE(iterator->arr),
4732 54 : iterator->typlen,
4733 54 : iterator->typbyval,
4734 54 : iterator->typalign);
4735 :
4736 54 : *isnull = false;
4737 54 : *value = PointerGetDatum(result);
4738 : }
4739 :
4740 1542 : return true;
4741 : }
4742 :
4743 : /*
4744 : * Release an ArrayIterator data structure
4745 : */
4746 : void
4747 258 : array_free_iterator(ArrayIterator iterator)
4748 : {
4749 258 : if (iterator->slice_ndim > 0)
4750 : {
4751 0 : pfree(iterator->slice_values);
4752 0 : pfree(iterator->slice_nulls);
4753 : }
4754 258 : pfree(iterator);
4755 258 : }
4756 :
4757 :
4758 : /***************************************************************************/
4759 : /******************| Support Routines |*****************/
4760 : /***************************************************************************/
4761 :
4762 : /*
4763 : * Check whether a specific array element is NULL
4764 : *
4765 : * nullbitmap: pointer to array's null bitmap (NULL if none)
4766 : * offset: 0-based linear element number of array element
4767 : */
4768 : static bool
4769 709180 : array_get_isnull(const bits8 *nullbitmap, int offset)
4770 : {
4771 709180 : if (nullbitmap == NULL)
4772 708636 : return false; /* assume not null */
4773 544 : if (nullbitmap[offset / 8] & (1 << (offset % 8)))
4774 412 : return false; /* not null */
4775 132 : return true;
4776 : }
4777 :
4778 : /*
4779 : * Set a specific array element's null-bitmap entry
4780 : *
4781 : * nullbitmap: pointer to array's null bitmap (mustn't be NULL)
4782 : * offset: 0-based linear element number of array element
4783 : * isNull: null status to set
4784 : */
4785 : static void
4786 134 : array_set_isnull(bits8 *nullbitmap, int offset, bool isNull)
4787 : {
4788 : int bitmask;
4789 :
4790 134 : nullbitmap += offset / 8;
4791 134 : bitmask = 1 << (offset % 8);
4792 134 : if (isNull)
4793 20 : *nullbitmap &= ~bitmask;
4794 : else
4795 114 : *nullbitmap |= bitmask;
4796 134 : }
4797 :
4798 : /*
4799 : * Fetch array element at pointer, converted correctly to a Datum
4800 : *
4801 : * Caller must have handled case of NULL element
4802 : */
4803 : static Datum
4804 706874 : ArrayCast(char *value, bool byval, int len)
4805 : {
4806 706874 : return fetch_att(value, byval, len);
4807 : }
4808 :
4809 : /*
4810 : * Copy datum to *dest and return total space used (including align padding)
4811 : *
4812 : * Caller must have handled case of NULL element
4813 : */
4814 : static int
4815 11380544 : ArrayCastAndSet(Datum src,
4816 : int typlen,
4817 : bool typbyval,
4818 : char typalign,
4819 : char *dest)
4820 : {
4821 : int inc;
4822 :
4823 11380544 : if (typlen > 0)
4824 : {
4825 8413280 : if (typbyval)
4826 4600966 : store_att_byval(dest, src, typlen);
4827 : else
4828 3812314 : memmove(dest, DatumGetPointer(src), typlen);
4829 8413280 : inc = att_align_nominal(typlen, typalign);
4830 : }
4831 : else
4832 : {
4833 : Assert(!typbyval);
4834 2967264 : inc = att_addlength_datum(0, typlen, src);
4835 2967264 : memmove(dest, DatumGetPointer(src), inc);
4836 2967264 : inc = att_align_nominal(inc, typalign);
4837 : }
4838 :
4839 11380544 : return inc;
4840 : }
4841 :
4842 : /*
4843 : * Advance ptr over nitems array elements
4844 : *
4845 : * ptr: starting location in array
4846 : * offset: 0-based linear element number of first element (the one at *ptr)
4847 : * nullbitmap: start of array's null bitmap, or NULL if none
4848 : * nitems: number of array elements to advance over (>= 0)
4849 : * typlen, typbyval, typalign: storage parameters of array element datatype
4850 : *
4851 : * It is caller's responsibility to ensure that nitems is within range
4852 : */
4853 : static char *
4854 709234 : array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
4855 : int typlen, bool typbyval, char typalign)
4856 : {
4857 : int bitmask;
4858 : int i;
4859 :
4860 : /* easy if fixed-size elements and no NULLs */
4861 709234 : if (typlen > 0 && !nullbitmap)
4862 453142 : return ptr + nitems * ((Size) att_align_nominal(typlen, typalign));
4863 :
4864 : /* seems worth having separate loops for NULL and no-NULLs cases */
4865 256092 : if (nullbitmap)
4866 : {
4867 604 : nullbitmap += offset / 8;
4868 604 : bitmask = 1 << (offset % 8);
4869 :
4870 1852 : for (i = 0; i < nitems; i++)
4871 : {
4872 1248 : if (*nullbitmap & bitmask)
4873 : {
4874 888 : ptr = att_addlength_pointer(ptr, typlen, ptr);
4875 888 : ptr = (char *) att_align_nominal(ptr, typalign);
4876 : }
4877 1248 : bitmask <<= 1;
4878 1248 : if (bitmask == 0x100)
4879 : {
4880 48 : nullbitmap++;
4881 48 : bitmask = 1;
4882 : }
4883 : }
4884 : }
4885 : else
4886 : {
4887 1162880 : for (i = 0; i < nitems; i++)
4888 : {
4889 907392 : ptr = att_addlength_pointer(ptr, typlen, ptr);
4890 907392 : ptr = (char *) att_align_nominal(ptr, typalign);
4891 : }
4892 : }
4893 256092 : return ptr;
4894 : }
4895 :
4896 : /*
4897 : * Compute total size of the nitems array elements starting at *ptr
4898 : *
4899 : * Parameters same as for array_seek
4900 : */
4901 : static int
4902 1578 : array_nelems_size(char *ptr, int offset, bits8 *nullbitmap, int nitems,
4903 : int typlen, bool typbyval, char typalign)
4904 : {
4905 1578 : return array_seek(ptr, offset, nullbitmap, nitems,
4906 1578 : typlen, typbyval, typalign) - ptr;
4907 : }
4908 :
4909 : /*
4910 : * Copy nitems array elements from srcptr to destptr
4911 : *
4912 : * destptr: starting destination location (must be enough room!)
4913 : * nitems: number of array elements to copy (>= 0)
4914 : * srcptr: starting location in source array
4915 : * offset: 0-based linear element number of first element (the one at *srcptr)
4916 : * nullbitmap: start of source array's null bitmap, or NULL if none
4917 : * typlen, typbyval, typalign: storage parameters of array element datatype
4918 : *
4919 : * Returns number of bytes copied
4920 : *
4921 : * NB: this does not take care of setting up the destination's null bitmap!
4922 : */
4923 : static int
4924 1110 : array_copy(char *destptr, int nitems,
4925 : char *srcptr, int offset, bits8 *nullbitmap,
4926 : int typlen, bool typbyval, char typalign)
4927 : {
4928 : int numbytes;
4929 :
4930 1110 : numbytes = array_nelems_size(srcptr, offset, nullbitmap, nitems,
4931 : typlen, typbyval, typalign);
4932 1110 : memcpy(destptr, srcptr, numbytes);
4933 1110 : return numbytes;
4934 : }
4935 :
4936 : /*
4937 : * Copy nitems null-bitmap bits from source to destination
4938 : *
4939 : * destbitmap: start of destination array's null bitmap (mustn't be NULL)
4940 : * destoffset: 0-based linear element number of first dest element
4941 : * srcbitmap: start of source array's null bitmap, or NULL if none
4942 : * srcoffset: 0-based linear element number of first source element
4943 : * nitems: number of bits to copy (>= 0)
4944 : *
4945 : * If srcbitmap is NULL then we assume the source is all-non-NULL and
4946 : * fill 1's into the destination bitmap. Note that only the specified
4947 : * bits in the destination map are changed, not any before or after.
4948 : *
4949 : * Note: this could certainly be optimized using standard bitblt methods.
4950 : * However, it's not clear that the typical Postgres array has enough elements
4951 : * to make it worth worrying too much. For the moment, KISS.
4952 : */
4953 : void
4954 30836 : array_bitmap_copy(bits8 *destbitmap, int destoffset,
4955 : const bits8 *srcbitmap, int srcoffset,
4956 : int nitems)
4957 : {
4958 : int destbitmask,
4959 : destbitval,
4960 : srcbitmask,
4961 : srcbitval;
4962 :
4963 : Assert(destbitmap);
4964 30836 : if (nitems <= 0)
4965 150 : return; /* don't risk fetch off end of memory */
4966 30686 : destbitmap += destoffset / 8;
4967 30686 : destbitmask = 1 << (destoffset % 8);
4968 30686 : destbitval = *destbitmap;
4969 30686 : if (srcbitmap)
4970 : {
4971 15616 : srcbitmap += srcoffset / 8;
4972 15616 : srcbitmask = 1 << (srcoffset % 8);
4973 15616 : srcbitval = *srcbitmap;
4974 63178 : while (nitems-- > 0)
4975 : {
4976 47562 : if (srcbitval & srcbitmask)
4977 16614 : destbitval |= destbitmask;
4978 : else
4979 30948 : destbitval &= ~destbitmask;
4980 47562 : destbitmask <<= 1;
4981 47562 : if (destbitmask == 0x100)
4982 : {
4983 5420 : *destbitmap++ = destbitval;
4984 5420 : destbitmask = 1;
4985 5420 : if (nitems > 0)
4986 3896 : destbitval = *destbitmap;
4987 : }
4988 47562 : srcbitmask <<= 1;
4989 47562 : if (srcbitmask == 0x100)
4990 : {
4991 3894 : srcbitmap++;
4992 3894 : srcbitmask = 1;
4993 3894 : if (nitems > 0)
4994 3858 : srcbitval = *srcbitmap;
4995 : }
4996 : }
4997 15616 : if (destbitmask != 1)
4998 14092 : *destbitmap = destbitval;
4999 : }
5000 : else
5001 : {
5002 30264 : while (nitems-- > 0)
5003 : {
5004 15194 : destbitval |= destbitmask;
5005 15194 : destbitmask <<= 1;
5006 15194 : if (destbitmask == 0x100)
5007 : {
5008 2232 : *destbitmap++ = destbitval;
5009 2232 : destbitmask = 1;
5010 2232 : if (nitems > 0)
5011 0 : destbitval = *destbitmap;
5012 : }
5013 : }
5014 15070 : if (destbitmask != 1)
5015 12838 : *destbitmap = destbitval;
5016 : }
5017 : }
5018 :
5019 : /*
5020 : * Compute space needed for a slice of an array
5021 : *
5022 : * We assume the caller has verified that the slice coordinates are valid.
5023 : */
5024 : static int
5025 282 : array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
5026 : int ndim, int *dim, int *lb,
5027 : int *st, int *endp,
5028 : int typlen, bool typbyval, char typalign)
5029 : {
5030 : int src_offset,
5031 : span[MAXDIM],
5032 : prod[MAXDIM],
5033 : dist[MAXDIM],
5034 : indx[MAXDIM];
5035 : char *ptr;
5036 : int i,
5037 : j,
5038 : inc;
5039 282 : int count = 0;
5040 :
5041 282 : mda_get_range(ndim, span, st, endp);
5042 :
5043 : /* Pretty easy for fixed element length without nulls ... */
5044 282 : if (typlen > 0 && !arraynullsptr)
5045 204 : return ArrayGetNItems(ndim, span) * att_align_nominal(typlen, typalign);
5046 :
5047 : /* Else gotta do it the hard way */
5048 78 : src_offset = ArrayGetOffset(ndim, dim, lb, st);
5049 78 : ptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
5050 : typlen, typbyval, typalign);
5051 78 : mda_get_prod(ndim, dim, prod);
5052 78 : mda_get_offset_values(ndim, dist, prod, span);
5053 198 : for (i = 0; i < ndim; i++)
5054 120 : indx[i] = 0;
5055 78 : j = ndim - 1;
5056 : do
5057 : {
5058 258 : if (dist[j])
5059 : {
5060 0 : ptr = array_seek(ptr, src_offset, arraynullsptr, dist[j],
5061 : typlen, typbyval, typalign);
5062 0 : src_offset += dist[j];
5063 : }
5064 258 : if (!array_get_isnull(arraynullsptr, src_offset))
5065 : {
5066 246 : inc = att_addlength_pointer(0, typlen, ptr);
5067 246 : inc = att_align_nominal(inc, typalign);
5068 246 : ptr += inc;
5069 246 : count += inc;
5070 : }
5071 258 : src_offset++;
5072 258 : } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
5073 78 : return count;
5074 : }
5075 :
5076 : /*
5077 : * Extract a slice of an array into consecutive elements in the destination
5078 : * array.
5079 : *
5080 : * We assume the caller has verified that the slice coordinates are valid,
5081 : * allocated enough storage for the result, and initialized the header
5082 : * of the new array.
5083 : */
5084 : static void
5085 252 : array_extract_slice(ArrayType *newarray,
5086 : int ndim,
5087 : int *dim,
5088 : int *lb,
5089 : char *arraydataptr,
5090 : bits8 *arraynullsptr,
5091 : int *st,
5092 : int *endp,
5093 : int typlen,
5094 : bool typbyval,
5095 : char typalign)
5096 : {
5097 252 : char *destdataptr = ARR_DATA_PTR(newarray);
5098 252 : bits8 *destnullsptr = ARR_NULLBITMAP(newarray);
5099 : char *srcdataptr;
5100 : int src_offset,
5101 : dest_offset,
5102 : prod[MAXDIM],
5103 : span[MAXDIM],
5104 : dist[MAXDIM],
5105 : indx[MAXDIM];
5106 : int i,
5107 : j,
5108 : inc;
5109 :
5110 252 : src_offset = ArrayGetOffset(ndim, dim, lb, st);
5111 252 : srcdataptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
5112 : typlen, typbyval, typalign);
5113 252 : mda_get_prod(ndim, dim, prod);
5114 252 : mda_get_range(ndim, span, st, endp);
5115 252 : mda_get_offset_values(ndim, dist, prod, span);
5116 636 : for (i = 0; i < ndim; i++)
5117 384 : indx[i] = 0;
5118 252 : dest_offset = 0;
5119 252 : j = ndim - 1;
5120 : do
5121 : {
5122 954 : if (dist[j])
5123 : {
5124 : /* skip unwanted elements */
5125 24 : srcdataptr = array_seek(srcdataptr, src_offset, arraynullsptr,
5126 : dist[j],
5127 : typlen, typbyval, typalign);
5128 24 : src_offset += dist[j];
5129 : }
5130 954 : inc = array_copy(destdataptr, 1,
5131 : srcdataptr, src_offset, arraynullsptr,
5132 : typlen, typbyval, typalign);
5133 954 : if (destnullsptr)
5134 180 : array_bitmap_copy(destnullsptr, dest_offset,
5135 : arraynullsptr, src_offset,
5136 : 1);
5137 954 : destdataptr += inc;
5138 954 : srcdataptr += inc;
5139 954 : src_offset++;
5140 954 : dest_offset++;
5141 954 : } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
5142 252 : }
5143 :
5144 : /*
5145 : * Insert a slice into an array.
5146 : *
5147 : * ndim/dim[]/lb[] are dimensions of the original array. A new array with
5148 : * those same dimensions is to be constructed. destArray must already
5149 : * have been allocated and its header initialized.
5150 : *
5151 : * st[]/endp[] identify the slice to be replaced. Elements within the slice
5152 : * volume are taken from consecutive elements of the srcArray; elements
5153 : * outside it are copied from origArray.
5154 : *
5155 : * We assume the caller has verified that the slice coordinates are valid.
5156 : */
5157 : static void
5158 30 : array_insert_slice(ArrayType *destArray,
5159 : ArrayType *origArray,
5160 : ArrayType *srcArray,
5161 : int ndim,
5162 : int *dim,
5163 : int *lb,
5164 : int *st,
5165 : int *endp,
5166 : int typlen,
5167 : bool typbyval,
5168 : char typalign)
5169 : {
5170 30 : char *destPtr = ARR_DATA_PTR(destArray);
5171 30 : char *origPtr = ARR_DATA_PTR(origArray);
5172 30 : char *srcPtr = ARR_DATA_PTR(srcArray);
5173 30 : bits8 *destBitmap = ARR_NULLBITMAP(destArray);
5174 30 : bits8 *origBitmap = ARR_NULLBITMAP(origArray);
5175 30 : bits8 *srcBitmap = ARR_NULLBITMAP(srcArray);
5176 30 : int orignitems = ArrayGetNItems(ARR_NDIM(origArray),
5177 : ARR_DIMS(origArray));
5178 : int dest_offset,
5179 : orig_offset,
5180 : src_offset,
5181 : prod[MAXDIM],
5182 : span[MAXDIM],
5183 : dist[MAXDIM],
5184 : indx[MAXDIM];
5185 : int i,
5186 : j,
5187 : inc;
5188 :
5189 30 : dest_offset = ArrayGetOffset(ndim, dim, lb, st);
5190 : /* copy items before the slice start */
5191 30 : inc = array_copy(destPtr, dest_offset,
5192 : origPtr, 0, origBitmap,
5193 : typlen, typbyval, typalign);
5194 30 : destPtr += inc;
5195 30 : origPtr += inc;
5196 30 : if (destBitmap)
5197 0 : array_bitmap_copy(destBitmap, 0, origBitmap, 0, dest_offset);
5198 30 : orig_offset = dest_offset;
5199 30 : mda_get_prod(ndim, dim, prod);
5200 30 : mda_get_range(ndim, span, st, endp);
5201 30 : mda_get_offset_values(ndim, dist, prod, span);
5202 102 : for (i = 0; i < ndim; i++)
5203 72 : indx[i] = 0;
5204 30 : src_offset = 0;
5205 30 : j = ndim - 1;
5206 : do
5207 : {
5208 : /* Copy/advance over elements between here and next part of slice */
5209 78 : if (dist[j])
5210 : {
5211 18 : inc = array_copy(destPtr, dist[j],
5212 : origPtr, orig_offset, origBitmap,
5213 : typlen, typbyval, typalign);
5214 18 : destPtr += inc;
5215 18 : origPtr += inc;
5216 18 : if (destBitmap)
5217 0 : array_bitmap_copy(destBitmap, dest_offset,
5218 : origBitmap, orig_offset,
5219 : dist[j]);
5220 18 : dest_offset += dist[j];
5221 18 : orig_offset += dist[j];
5222 : }
5223 : /* Copy new element at this slice position */
5224 78 : inc = array_copy(destPtr, 1,
5225 : srcPtr, src_offset, srcBitmap,
5226 : typlen, typbyval, typalign);
5227 78 : if (destBitmap)
5228 0 : array_bitmap_copy(destBitmap, dest_offset,
5229 : srcBitmap, src_offset,
5230 : 1);
5231 78 : destPtr += inc;
5232 78 : srcPtr += inc;
5233 78 : dest_offset++;
5234 78 : src_offset++;
5235 : /* Advance over old element at this slice position */
5236 78 : origPtr = array_seek(origPtr, orig_offset, origBitmap, 1,
5237 : typlen, typbyval, typalign);
5238 78 : orig_offset++;
5239 78 : } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
5240 :
5241 : /* don't miss any data at the end */
5242 30 : array_copy(destPtr, orignitems - orig_offset,
5243 : origPtr, orig_offset, origBitmap,
5244 : typlen, typbyval, typalign);
5245 30 : if (destBitmap)
5246 0 : array_bitmap_copy(destBitmap, dest_offset,
5247 : origBitmap, orig_offset,
5248 : orignitems - orig_offset);
5249 30 : }
5250 :
5251 : /*
5252 : * initArrayResult - initialize an empty ArrayBuildState
5253 : *
5254 : * element_type is the array element type (must be a valid array element type)
5255 : * rcontext is where to keep working state
5256 : * subcontext is a flag determining whether to use a separate memory context
5257 : *
5258 : * Note: there are two common schemes for using accumArrayResult().
5259 : * In the older scheme, you start with a NULL ArrayBuildState pointer, and
5260 : * call accumArrayResult once per element. In this scheme you end up with
5261 : * a NULL pointer if there were no elements, which you need to special-case.
5262 : * In the newer scheme, call initArrayResult and then call accumArrayResult
5263 : * once per element. In this scheme you always end with a non-NULL pointer
5264 : * that you can pass to makeArrayResult; you get an empty array if there
5265 : * were no elements. This is preferred if an empty array is what you want.
5266 : *
5267 : * It's possible to choose whether to create a separate memory context for the
5268 : * array build state, or whether to allocate it directly within rcontext.
5269 : *
5270 : * When there are many concurrent small states (e.g. array_agg() using hash
5271 : * aggregation of many small groups), using a separate memory context for each
5272 : * one may result in severe memory bloat. In such cases, use the same memory
5273 : * context to initialize all such array build states, and pass
5274 : * subcontext=false.
5275 : *
5276 : * In cases when the array build states have different lifetimes, using a
5277 : * single memory context is impractical. Instead, pass subcontext=true so that
5278 : * the array build states can be freed individually.
5279 : */
5280 : ArrayBuildState *
5281 331644 : initArrayResult(Oid element_type, MemoryContext rcontext, bool subcontext)
5282 : {
5283 : /*
5284 : * When using a subcontext, we can afford to start with a somewhat larger
5285 : * initial array size. Without subcontexts, we'd better hope that most of
5286 : * the states stay small ...
5287 : */
5288 331644 : return initArrayResultWithSize(element_type, rcontext, subcontext,
5289 : subcontext ? 64 : 8);
5290 : }
5291 :
5292 : /*
5293 : * initArrayResultWithSize
5294 : * As initArrayResult, but allow the initial size of the allocated arrays
5295 : * to be specified.
5296 : */
5297 : ArrayBuildState *
5298 331764 : initArrayResultWithSize(Oid element_type, MemoryContext rcontext,
5299 : bool subcontext, int initsize)
5300 : {
5301 : ArrayBuildState *astate;
5302 331764 : MemoryContext arr_context = rcontext;
5303 :
5304 : /* Make a temporary context to hold all the junk */
5305 331764 : if (subcontext)
5306 256146 : arr_context = AllocSetContextCreate(rcontext,
5307 : "accumArrayResult",
5308 : ALLOCSET_DEFAULT_SIZES);
5309 :
5310 : astate = (ArrayBuildState *)
5311 331764 : MemoryContextAlloc(arr_context, sizeof(ArrayBuildState));
5312 331764 : astate->mcontext = arr_context;
5313 331764 : astate->private_cxt = subcontext;
5314 331764 : astate->alen = initsize;
5315 331764 : astate->dvalues = (Datum *)
5316 331764 : MemoryContextAlloc(arr_context, astate->alen * sizeof(Datum));
5317 331764 : astate->dnulls = (bool *)
5318 331764 : MemoryContextAlloc(arr_context, astate->alen * sizeof(bool));
5319 331764 : astate->nelems = 0;
5320 331764 : astate->element_type = element_type;
5321 331764 : get_typlenbyvalalign(element_type,
5322 : &astate->typlen,
5323 : &astate->typbyval,
5324 : &astate->typalign);
5325 :
5326 331764 : return astate;
5327 : }
5328 :
5329 : /*
5330 : * accumArrayResult - accumulate one (more) Datum for an array result
5331 : *
5332 : * astate is working state (can be NULL on first call)
5333 : * dvalue/disnull represent the new Datum to append to the array
5334 : * element_type is the Datum's type (must be a valid array element type)
5335 : * rcontext is where to keep working state
5336 : */
5337 : ArrayBuildState *
5338 2736732 : accumArrayResult(ArrayBuildState *astate,
5339 : Datum dvalue, bool disnull,
5340 : Oid element_type,
5341 : MemoryContext rcontext)
5342 : {
5343 : MemoryContext oldcontext;
5344 :
5345 2736732 : if (astate == NULL)
5346 : {
5347 : /* First time through --- initialize */
5348 204418 : astate = initArrayResult(element_type, rcontext, true);
5349 : }
5350 : else
5351 : {
5352 : Assert(astate->element_type == element_type);
5353 : }
5354 :
5355 2736732 : oldcontext = MemoryContextSwitchTo(astate->mcontext);
5356 :
5357 : /* enlarge dvalues[]/dnulls[] if needed */
5358 2736732 : if (astate->nelems >= astate->alen)
5359 : {
5360 18520 : astate->alen *= 2;
5361 : /* give an array-related error if we go past MaxAllocSize */
5362 18520 : if (!AllocSizeIsValid(astate->alen * sizeof(Datum)))
5363 0 : ereport(ERROR,
5364 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
5365 : errmsg("array size exceeds the maximum allowed (%d)",
5366 : (int) MaxAllocSize)));
5367 18520 : astate->dvalues = (Datum *)
5368 18520 : repalloc(astate->dvalues, astate->alen * sizeof(Datum));
5369 18520 : astate->dnulls = (bool *)
5370 18520 : repalloc(astate->dnulls, astate->alen * sizeof(bool));
5371 : }
5372 :
5373 : /*
5374 : * Ensure pass-by-ref stuff is copied into mcontext; and detoast it too if
5375 : * it's varlena. (You might think that detoasting is not needed here
5376 : * because construct_md_array can detoast the array elements later.
5377 : * However, we must not let construct_md_array modify the ArrayBuildState
5378 : * because that would mean array_agg_finalfn damages its input, which is
5379 : * verboten. Also, this way frequently saves one copying step.)
5380 : */
5381 2736732 : if (!disnull && !astate->typbyval)
5382 : {
5383 2225476 : if (astate->typlen == -1)
5384 1599832 : dvalue = PointerGetDatum(PG_DETOAST_DATUM_COPY(dvalue));
5385 : else
5386 625644 : dvalue = datumCopy(dvalue, astate->typbyval, astate->typlen);
5387 : }
5388 :
5389 2736732 : astate->dvalues[astate->nelems] = dvalue;
5390 2736732 : astate->dnulls[astate->nelems] = disnull;
5391 2736732 : astate->nelems++;
5392 :
5393 2736732 : MemoryContextSwitchTo(oldcontext);
5394 :
5395 2736732 : return astate;
5396 : }
5397 :
5398 : /*
5399 : * makeArrayResult - produce 1-D final result of accumArrayResult
5400 : *
5401 : * Note: only releases astate if it was initialized within a separate memory
5402 : * context (i.e. using subcontext=true when calling initArrayResult).
5403 : *
5404 : * astate is working state (must not be NULL)
5405 : * rcontext is where to construct result
5406 : */
5407 : Datum
5408 204600 : makeArrayResult(ArrayBuildState *astate,
5409 : MemoryContext rcontext)
5410 : {
5411 : int ndims;
5412 : int dims[1];
5413 : int lbs[1];
5414 :
5415 : /* If no elements were presented, we want to create an empty array */
5416 204600 : ndims = (astate->nelems > 0) ? 1 : 0;
5417 204600 : dims[0] = astate->nelems;
5418 204600 : lbs[0] = 1;
5419 :
5420 409200 : return makeMdArrayResult(astate, ndims, dims, lbs, rcontext,
5421 204600 : astate->private_cxt);
5422 : }
5423 :
5424 : /*
5425 : * makeMdArrayResult - produce multi-D final result of accumArrayResult
5426 : *
5427 : * beware: no check that specified dimensions match the number of values
5428 : * accumulated.
5429 : *
5430 : * Note: if the astate was not initialized within a separate memory context
5431 : * (that is, initArrayResult was called with subcontext=false), then using
5432 : * release=true is illegal. Instead, release astate along with the rest of its
5433 : * context when appropriate.
5434 : *
5435 : * astate is working state (must not be NULL)
5436 : * rcontext is where to construct result
5437 : * release is true if okay to release working state
5438 : */
5439 : Datum
5440 330972 : makeMdArrayResult(ArrayBuildState *astate,
5441 : int ndims,
5442 : int *dims,
5443 : int *lbs,
5444 : MemoryContext rcontext,
5445 : bool release)
5446 : {
5447 : ArrayType *result;
5448 : MemoryContext oldcontext;
5449 :
5450 : /* Build the final array result in rcontext */
5451 330972 : oldcontext = MemoryContextSwitchTo(rcontext);
5452 :
5453 330972 : result = construct_md_array(astate->dvalues,
5454 : astate->dnulls,
5455 : ndims,
5456 : dims,
5457 : lbs,
5458 : astate->element_type,
5459 330972 : astate->typlen,
5460 330972 : astate->typbyval,
5461 330972 : astate->typalign);
5462 :
5463 330972 : MemoryContextSwitchTo(oldcontext);
5464 :
5465 : /* Clean up all the junk */
5466 330972 : if (release)
5467 : {
5468 : Assert(astate->private_cxt);
5469 255560 : MemoryContextDelete(astate->mcontext);
5470 : }
5471 :
5472 330972 : return PointerGetDatum(result);
5473 : }
5474 :
5475 : /*
5476 : * The following three functions provide essentially the same API as
5477 : * initArrayResult/accumArrayResult/makeArrayResult, but instead of accepting
5478 : * inputs that are array elements, they accept inputs that are arrays and
5479 : * produce an output array having N+1 dimensions. The inputs must all have
5480 : * identical dimensionality as well as element type.
5481 : */
5482 :
5483 : /*
5484 : * initArrayResultArr - initialize an empty ArrayBuildStateArr
5485 : *
5486 : * array_type is the array type (must be a valid varlena array type)
5487 : * element_type is the type of the array's elements (lookup if InvalidOid)
5488 : * rcontext is where to keep working state
5489 : * subcontext is a flag determining whether to use a separate memory context
5490 : */
5491 : ArrayBuildStateArr *
5492 336 : initArrayResultArr(Oid array_type, Oid element_type, MemoryContext rcontext,
5493 : bool subcontext)
5494 : {
5495 : ArrayBuildStateArr *astate;
5496 336 : MemoryContext arr_context = rcontext; /* by default use the parent ctx */
5497 :
5498 : /* Lookup element type, unless element_type already provided */
5499 336 : if (!OidIsValid(element_type))
5500 : {
5501 276 : element_type = get_element_type(array_type);
5502 :
5503 276 : if (!OidIsValid(element_type))
5504 0 : ereport(ERROR,
5505 : (errcode(ERRCODE_DATATYPE_MISMATCH),
5506 : errmsg("data type %s is not an array type",
5507 : format_type_be(array_type))));
5508 : }
5509 :
5510 : /* Make a temporary context to hold all the junk */
5511 336 : if (subcontext)
5512 12 : arr_context = AllocSetContextCreate(rcontext,
5513 : "accumArrayResultArr",
5514 : ALLOCSET_DEFAULT_SIZES);
5515 :
5516 : /* Note we initialize all fields to zero */
5517 : astate = (ArrayBuildStateArr *)
5518 336 : MemoryContextAllocZero(arr_context, sizeof(ArrayBuildStateArr));
5519 336 : astate->mcontext = arr_context;
5520 336 : astate->private_cxt = subcontext;
5521 :
5522 : /* Save relevant datatype information */
5523 336 : astate->array_type = array_type;
5524 336 : astate->element_type = element_type;
5525 :
5526 336 : return astate;
5527 : }
5528 :
5529 : /*
5530 : * accumArrayResultArr - accumulate one (more) sub-array for an array result
5531 : *
5532 : * astate is working state (can be NULL on first call)
5533 : * dvalue/disnull represent the new sub-array to append to the array
5534 : * array_type is the array type (must be a valid varlena array type)
5535 : * rcontext is where to keep working state
5536 : */
5537 : ArrayBuildStateArr *
5538 60270 : accumArrayResultArr(ArrayBuildStateArr *astate,
5539 : Datum dvalue, bool disnull,
5540 : Oid array_type,
5541 : MemoryContext rcontext)
5542 : {
5543 : ArrayType *arg;
5544 : MemoryContext oldcontext;
5545 : int *dims,
5546 : *lbs,
5547 : ndims,
5548 : nitems,
5549 : ndatabytes;
5550 : char *data;
5551 : int i;
5552 :
5553 : /*
5554 : * We disallow accumulating null subarrays. Another plausible definition
5555 : * is to ignore them, but callers that want that can just skip calling
5556 : * this function.
5557 : */
5558 60270 : if (disnull)
5559 6 : ereport(ERROR,
5560 : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5561 : errmsg("cannot accumulate null arrays")));
5562 :
5563 : /* Detoast input array in caller's context */
5564 60264 : arg = DatumGetArrayTypeP(dvalue);
5565 :
5566 60264 : if (astate == NULL)
5567 0 : astate = initArrayResultArr(array_type, InvalidOid, rcontext, true);
5568 : else
5569 : Assert(astate->array_type == array_type);
5570 :
5571 60264 : oldcontext = MemoryContextSwitchTo(astate->mcontext);
5572 :
5573 : /* Collect this input's dimensions */
5574 60264 : ndims = ARR_NDIM(arg);
5575 60264 : dims = ARR_DIMS(arg);
5576 60264 : lbs = ARR_LBOUND(arg);
5577 60264 : data = ARR_DATA_PTR(arg);
5578 60264 : nitems = ArrayGetNItems(ndims, dims);
5579 60264 : ndatabytes = ARR_SIZE(arg) - ARR_DATA_OFFSET(arg);
5580 :
5581 60264 : if (astate->ndims == 0)
5582 : {
5583 : /* First input; check/save the dimensionality info */
5584 :
5585 : /* Should we allow empty inputs and just produce an empty output? */
5586 210 : if (ndims == 0)
5587 6 : ereport(ERROR,
5588 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5589 : errmsg("cannot accumulate empty arrays")));
5590 204 : if (ndims + 1 > MAXDIM)
5591 0 : ereport(ERROR,
5592 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
5593 : errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
5594 : ndims + 1, MAXDIM)));
5595 :
5596 : /*
5597 : * The output array will have n+1 dimensions, with the ones after the
5598 : * first matching the input's dimensions.
5599 : */
5600 204 : astate->ndims = ndims + 1;
5601 204 : astate->dims[0] = 0;
5602 204 : memcpy(&astate->dims[1], dims, ndims * sizeof(int));
5603 204 : astate->lbs[0] = 1;
5604 204 : memcpy(&astate->lbs[1], lbs, ndims * sizeof(int));
5605 :
5606 : /* Allocate at least enough data space for this item */
5607 204 : astate->abytes = pg_nextpower2_32(Max(1024, ndatabytes + 1));
5608 204 : astate->data = (char *) palloc(astate->abytes);
5609 : }
5610 : else
5611 : {
5612 : /* Second or later input: must match first input's dimensionality */
5613 60054 : if (astate->ndims != ndims + 1)
5614 0 : ereport(ERROR,
5615 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5616 : errmsg("cannot accumulate arrays of different dimensionality")));
5617 120102 : for (i = 0; i < ndims; i++)
5618 : {
5619 60054 : if (astate->dims[i + 1] != dims[i] || astate->lbs[i + 1] != lbs[i])
5620 6 : ereport(ERROR,
5621 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5622 : errmsg("cannot accumulate arrays of different dimensionality")));
5623 : }
5624 :
5625 : /* Enlarge data space if needed */
5626 60048 : if (astate->nbytes + ndatabytes > astate->abytes)
5627 : {
5628 60 : astate->abytes = Max(astate->abytes * 2,
5629 : astate->nbytes + ndatabytes);
5630 60 : astate->data = (char *) repalloc(astate->data, astate->abytes);
5631 : }
5632 : }
5633 :
5634 : /*
5635 : * Copy the data portion of the sub-array. Note we assume that the
5636 : * advertised data length of the sub-array is properly aligned. We do not
5637 : * have to worry about detoasting elements since whatever's in the
5638 : * sub-array should be OK already.
5639 : */
5640 60252 : memcpy(astate->data + astate->nbytes, data, ndatabytes);
5641 60252 : astate->nbytes += ndatabytes;
5642 :
5643 : /* Deal with null bitmap if needed */
5644 60252 : if (astate->nullbitmap || ARR_HASNULL(arg))
5645 : {
5646 29988 : int newnitems = astate->nitems + nitems;
5647 :
5648 29988 : if (astate->nullbitmap == NULL)
5649 : {
5650 : /*
5651 : * First input with nulls; we must retrospectively handle any
5652 : * previous inputs by marking all their items non-null.
5653 : */
5654 66 : astate->aitems = pg_nextpower2_32(Max(256, newnitems + 1));
5655 66 : astate->nullbitmap = (bits8 *) palloc((astate->aitems + 7) / 8);
5656 66 : array_bitmap_copy(astate->nullbitmap, 0,
5657 : NULL, 0,
5658 : astate->nitems);
5659 : }
5660 29922 : else if (newnitems > astate->aitems)
5661 : {
5662 60 : astate->aitems = Max(astate->aitems * 2, newnitems);
5663 60 : astate->nullbitmap = (bits8 *)
5664 60 : repalloc(astate->nullbitmap, (astate->aitems + 7) / 8);
5665 : }
5666 29988 : array_bitmap_copy(astate->nullbitmap, astate->nitems,
5667 29988 : ARR_NULLBITMAP(arg), 0,
5668 : nitems);
5669 : }
5670 :
5671 60252 : astate->nitems += nitems;
5672 60252 : astate->dims[0] += 1;
5673 :
5674 60252 : MemoryContextSwitchTo(oldcontext);
5675 :
5676 : /* Release detoasted copy if any */
5677 60252 : if ((Pointer) arg != DatumGetPointer(dvalue))
5678 126 : pfree(arg);
5679 :
5680 60252 : return astate;
5681 : }
5682 :
5683 : /*
5684 : * makeArrayResultArr - produce N+1-D final result of accumArrayResultArr
5685 : *
5686 : * astate is working state (must not be NULL)
5687 : * rcontext is where to construct result
5688 : * release is true if okay to release working state
5689 : */
5690 : Datum
5691 198 : makeArrayResultArr(ArrayBuildStateArr *astate,
5692 : MemoryContext rcontext,
5693 : bool release)
5694 : {
5695 : ArrayType *result;
5696 : MemoryContext oldcontext;
5697 :
5698 : /* Build the final array result in rcontext */
5699 198 : oldcontext = MemoryContextSwitchTo(rcontext);
5700 :
5701 198 : if (astate->ndims == 0)
5702 : {
5703 : /* No inputs, return empty array */
5704 0 : result = construct_empty_array(astate->element_type);
5705 : }
5706 : else
5707 : {
5708 : int dataoffset,
5709 : nbytes;
5710 :
5711 : /* Check for overflow of the array dimensions */
5712 198 : (void) ArrayGetNItems(astate->ndims, astate->dims);
5713 198 : ArrayCheckBounds(astate->ndims, astate->dims, astate->lbs);
5714 :
5715 : /* Compute required space */
5716 198 : nbytes = astate->nbytes;
5717 198 : if (astate->nullbitmap != NULL)
5718 : {
5719 66 : dataoffset = ARR_OVERHEAD_WITHNULLS(astate->ndims, astate->nitems);
5720 66 : nbytes += dataoffset;
5721 : }
5722 : else
5723 : {
5724 132 : dataoffset = 0;
5725 132 : nbytes += ARR_OVERHEAD_NONULLS(astate->ndims);
5726 : }
5727 :
5728 198 : result = (ArrayType *) palloc0(nbytes);
5729 198 : SET_VARSIZE(result, nbytes);
5730 198 : result->ndim = astate->ndims;
5731 198 : result->dataoffset = dataoffset;
5732 198 : result->elemtype = astate->element_type;
5733 :
5734 198 : memcpy(ARR_DIMS(result), astate->dims, astate->ndims * sizeof(int));
5735 198 : memcpy(ARR_LBOUND(result), astate->lbs, astate->ndims * sizeof(int));
5736 198 : memcpy(ARR_DATA_PTR(result), astate->data, astate->nbytes);
5737 :
5738 198 : if (astate->nullbitmap != NULL)
5739 66 : array_bitmap_copy(ARR_NULLBITMAP(result), 0,
5740 66 : astate->nullbitmap, 0,
5741 : astate->nitems);
5742 : }
5743 :
5744 198 : MemoryContextSwitchTo(oldcontext);
5745 :
5746 : /* Clean up all the junk */
5747 198 : if (release)
5748 : {
5749 : Assert(astate->private_cxt);
5750 12 : MemoryContextDelete(astate->mcontext);
5751 : }
5752 :
5753 198 : return PointerGetDatum(result);
5754 : }
5755 :
5756 : /*
5757 : * The following three functions provide essentially the same API as
5758 : * initArrayResult/accumArrayResult/makeArrayResult, but can accept either
5759 : * scalar or array inputs, invoking the appropriate set of functions above.
5760 : */
5761 :
5762 : /*
5763 : * initArrayResultAny - initialize an empty ArrayBuildStateAny
5764 : *
5765 : * input_type is the input datatype (either element or array type)
5766 : * rcontext is where to keep working state
5767 : * subcontext is a flag determining whether to use a separate memory context
5768 : */
5769 : ArrayBuildStateAny *
5770 49324 : initArrayResultAny(Oid input_type, MemoryContext rcontext, bool subcontext)
5771 : {
5772 : ArrayBuildStateAny *astate;
5773 49324 : Oid element_type = get_element_type(input_type);
5774 :
5775 49324 : if (OidIsValid(element_type))
5776 : {
5777 : /* Array case */
5778 : ArrayBuildStateArr *arraystate;
5779 :
5780 12 : arraystate = initArrayResultArr(input_type, InvalidOid, rcontext, subcontext);
5781 : astate = (ArrayBuildStateAny *)
5782 12 : MemoryContextAlloc(arraystate->mcontext,
5783 : sizeof(ArrayBuildStateAny));
5784 12 : astate->scalarstate = NULL;
5785 12 : astate->arraystate = arraystate;
5786 : }
5787 : else
5788 : {
5789 : /* Scalar case */
5790 : ArrayBuildState *scalarstate;
5791 :
5792 : /* Let's just check that we have a type that can be put into arrays */
5793 : Assert(OidIsValid(get_array_type(input_type)));
5794 :
5795 49312 : scalarstate = initArrayResult(input_type, rcontext, subcontext);
5796 : astate = (ArrayBuildStateAny *)
5797 49312 : MemoryContextAlloc(scalarstate->mcontext,
5798 : sizeof(ArrayBuildStateAny));
5799 49312 : astate->scalarstate = scalarstate;
5800 49312 : astate->arraystate = NULL;
5801 : }
5802 :
5803 49324 : return astate;
5804 : }
5805 :
5806 : /*
5807 : * accumArrayResultAny - accumulate one (more) input for an array result
5808 : *
5809 : * astate is working state (can be NULL on first call)
5810 : * dvalue/disnull represent the new input to append to the array
5811 : * input_type is the input datatype (either element or array type)
5812 : * rcontext is where to keep working state
5813 : */
5814 : ArrayBuildStateAny *
5815 16820 : accumArrayResultAny(ArrayBuildStateAny *astate,
5816 : Datum dvalue, bool disnull,
5817 : Oid input_type,
5818 : MemoryContext rcontext)
5819 : {
5820 16820 : if (astate == NULL)
5821 0 : astate = initArrayResultAny(input_type, rcontext, true);
5822 :
5823 16820 : if (astate->scalarstate)
5824 16772 : (void) accumArrayResult(astate->scalarstate,
5825 : dvalue, disnull,
5826 : input_type, rcontext);
5827 : else
5828 48 : (void) accumArrayResultArr(astate->arraystate,
5829 : dvalue, disnull,
5830 : input_type, rcontext);
5831 :
5832 16820 : return astate;
5833 : }
5834 :
5835 : /*
5836 : * makeArrayResultAny - produce final result of accumArrayResultAny
5837 : *
5838 : * astate is working state (must not be NULL)
5839 : * rcontext is where to construct result
5840 : * release is true if okay to release working state
5841 : */
5842 : Datum
5843 49324 : makeArrayResultAny(ArrayBuildStateAny *astate,
5844 : MemoryContext rcontext, bool release)
5845 : {
5846 : Datum result;
5847 :
5848 49324 : if (astate->scalarstate)
5849 : {
5850 : /* Must use makeMdArrayResult to support "release" parameter */
5851 : int ndims;
5852 : int dims[1];
5853 : int lbs[1];
5854 :
5855 : /* If no elements were presented, we want to create an empty array */
5856 49312 : ndims = (astate->scalarstate->nelems > 0) ? 1 : 0;
5857 49312 : dims[0] = astate->scalarstate->nelems;
5858 49312 : lbs[0] = 1;
5859 :
5860 49312 : result = makeMdArrayResult(astate->scalarstate, ndims, dims, lbs,
5861 : rcontext, release);
5862 : }
5863 : else
5864 : {
5865 12 : result = makeArrayResultArr(astate->arraystate,
5866 : rcontext, release);
5867 : }
5868 49324 : return result;
5869 : }
5870 :
5871 :
5872 : Datum
5873 288 : array_larger(PG_FUNCTION_ARGS)
5874 : {
5875 288 : if (array_cmp(fcinfo) > 0)
5876 144 : PG_RETURN_DATUM(PG_GETARG_DATUM(0));
5877 : else
5878 138 : PG_RETURN_DATUM(PG_GETARG_DATUM(1));
5879 : }
5880 :
5881 : Datum
5882 258 : array_smaller(PG_FUNCTION_ARGS)
5883 : {
5884 258 : if (array_cmp(fcinfo) < 0)
5885 174 : PG_RETURN_DATUM(PG_GETARG_DATUM(0));
5886 : else
5887 84 : PG_RETURN_DATUM(PG_GETARG_DATUM(1));
5888 : }
5889 :
5890 :
5891 : typedef struct generate_subscripts_fctx
5892 : {
5893 : int32 lower;
5894 : int32 upper;
5895 : bool reverse;
5896 : } generate_subscripts_fctx;
5897 :
5898 : /*
5899 : * generate_subscripts(array anyarray, dim int [, reverse bool])
5900 : * Returns all subscripts of the array for any dimension
5901 : */
5902 : Datum
5903 4870 : generate_subscripts(PG_FUNCTION_ARGS)
5904 : {
5905 : FuncCallContext *funcctx;
5906 : MemoryContext oldcontext;
5907 : generate_subscripts_fctx *fctx;
5908 :
5909 : /* stuff done only on the first call of the function */
5910 4870 : if (SRF_IS_FIRSTCALL())
5911 : {
5912 2276 : AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
5913 2276 : int reqdim = PG_GETARG_INT32(1);
5914 : int *lb,
5915 : *dimv;
5916 :
5917 : /* create a function context for cross-call persistence */
5918 2276 : funcctx = SRF_FIRSTCALL_INIT();
5919 :
5920 : /* Sanity check: does it look like an array at all? */
5921 2276 : if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
5922 6 : SRF_RETURN_DONE(funcctx);
5923 :
5924 : /* Sanity check: was the requested dim valid */
5925 2270 : if (reqdim <= 0 || reqdim > AARR_NDIM(v))
5926 0 : SRF_RETURN_DONE(funcctx);
5927 :
5928 : /*
5929 : * switch to memory context appropriate for multiple function calls
5930 : */
5931 2270 : oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
5932 2270 : fctx = (generate_subscripts_fctx *) palloc(sizeof(generate_subscripts_fctx));
5933 :
5934 2270 : lb = AARR_LBOUND(v);
5935 2270 : dimv = AARR_DIMS(v);
5936 :
5937 2270 : fctx->lower = lb[reqdim - 1];
5938 2270 : fctx->upper = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
5939 2270 : fctx->reverse = (PG_NARGS() < 3) ? false : PG_GETARG_BOOL(2);
5940 :
5941 2270 : funcctx->user_fctx = fctx;
5942 :
5943 2270 : MemoryContextSwitchTo(oldcontext);
5944 : }
5945 :
5946 4864 : funcctx = SRF_PERCALL_SETUP();
5947 :
5948 4864 : fctx = funcctx->user_fctx;
5949 :
5950 4864 : if (fctx->lower <= fctx->upper)
5951 : {
5952 2594 : if (!fctx->reverse)
5953 2594 : SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->lower++));
5954 : else
5955 0 : SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->upper--));
5956 : }
5957 : else
5958 : /* done when there are no more elements left */
5959 2270 : SRF_RETURN_DONE(funcctx);
5960 : }
5961 :
5962 : /*
5963 : * generate_subscripts_nodir
5964 : * Implements the 2-argument version of generate_subscripts
5965 : */
5966 : Datum
5967 4870 : generate_subscripts_nodir(PG_FUNCTION_ARGS)
5968 : {
5969 : /* just call the other one -- it can handle both cases */
5970 4870 : return generate_subscripts(fcinfo);
5971 : }
5972 :
5973 : /*
5974 : * array_fill_with_lower_bounds
5975 : * Create and fill array with defined lower bounds.
5976 : */
5977 : Datum
5978 66 : array_fill_with_lower_bounds(PG_FUNCTION_ARGS)
5979 : {
5980 : ArrayType *dims;
5981 : ArrayType *lbs;
5982 : ArrayType *result;
5983 : Oid elmtype;
5984 : Datum value;
5985 : bool isnull;
5986 :
5987 66 : if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
5988 12 : ereport(ERROR,
5989 : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5990 : errmsg("dimension array or low bound array cannot be null")));
5991 :
5992 54 : dims = PG_GETARG_ARRAYTYPE_P(1);
5993 54 : lbs = PG_GETARG_ARRAYTYPE_P(2);
5994 :
5995 54 : if (!PG_ARGISNULL(0))
5996 : {
5997 42 : value = PG_GETARG_DATUM(0);
5998 42 : isnull = false;
5999 : }
6000 : else
6001 : {
6002 12 : value = 0;
6003 12 : isnull = true;
6004 : }
6005 :
6006 54 : elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
6007 54 : if (!OidIsValid(elmtype))
6008 0 : elog(ERROR, "could not determine data type of input");
6009 :
6010 54 : result = array_fill_internal(dims, lbs, value, isnull, elmtype, fcinfo);
6011 42 : PG_RETURN_ARRAYTYPE_P(result);
6012 : }
6013 :
6014 : /*
6015 : * array_fill
6016 : * Create and fill array with default lower bounds.
6017 : */
6018 : Datum
6019 90 : array_fill(PG_FUNCTION_ARGS)
6020 : {
6021 : ArrayType *dims;
6022 : ArrayType *result;
6023 : Oid elmtype;
6024 : Datum value;
6025 : bool isnull;
6026 :
6027 90 : if (PG_ARGISNULL(1))
6028 0 : ereport(ERROR,
6029 : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
6030 : errmsg("dimension array or low bound array cannot be null")));
6031 :
6032 90 : dims = PG_GETARG_ARRAYTYPE_P(1);
6033 :
6034 90 : if (!PG_ARGISNULL(0))
6035 : {
6036 78 : value = PG_GETARG_DATUM(0);
6037 78 : isnull = false;
6038 : }
6039 : else
6040 : {
6041 12 : value = 0;
6042 12 : isnull = true;
6043 : }
6044 :
6045 90 : elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
6046 90 : if (!OidIsValid(elmtype))
6047 0 : elog(ERROR, "could not determine data type of input");
6048 :
6049 90 : result = array_fill_internal(dims, NULL, value, isnull, elmtype, fcinfo);
6050 78 : PG_RETURN_ARRAYTYPE_P(result);
6051 : }
6052 :
6053 : static ArrayType *
6054 66 : create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
6055 : Oid elmtype, int dataoffset)
6056 : {
6057 : ArrayType *result;
6058 :
6059 66 : result = (ArrayType *) palloc0(nbytes);
6060 66 : SET_VARSIZE(result, nbytes);
6061 66 : result->ndim = ndims;
6062 66 : result->dataoffset = dataoffset;
6063 66 : result->elemtype = elmtype;
6064 66 : memcpy(ARR_DIMS(result), dimv, ndims * sizeof(int));
6065 66 : memcpy(ARR_LBOUND(result), lbsv, ndims * sizeof(int));
6066 :
6067 66 : return result;
6068 : }
6069 :
6070 : static ArrayType *
6071 144 : array_fill_internal(ArrayType *dims, ArrayType *lbs,
6072 : Datum value, bool isnull, Oid elmtype,
6073 : FunctionCallInfo fcinfo)
6074 : {
6075 : ArrayType *result;
6076 : int *dimv;
6077 : int *lbsv;
6078 : int ndims;
6079 : int nitems;
6080 : int deflbs[MAXDIM];
6081 : int16 elmlen;
6082 : bool elmbyval;
6083 : char elmalign;
6084 : ArrayMetaState *my_extra;
6085 :
6086 : /*
6087 : * Params checks
6088 : */
6089 144 : if (ARR_NDIM(dims) > 1)
6090 6 : ereport(ERROR,
6091 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
6092 : errmsg("wrong number of array subscripts"),
6093 : errdetail("Dimension array must be one dimensional.")));
6094 :
6095 138 : if (array_contains_nulls(dims))
6096 6 : ereport(ERROR,
6097 : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
6098 : errmsg("dimension values cannot be null")));
6099 :
6100 132 : dimv = (int *) ARR_DATA_PTR(dims);
6101 132 : ndims = (ARR_NDIM(dims) > 0) ? ARR_DIMS(dims)[0] : 0;
6102 :
6103 132 : if (ndims < 0) /* we do allow zero-dimension arrays */
6104 0 : ereport(ERROR,
6105 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
6106 : errmsg("invalid number of dimensions: %d", ndims)));
6107 132 : if (ndims > MAXDIM)
6108 0 : ereport(ERROR,
6109 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
6110 : errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
6111 : ndims, MAXDIM)));
6112 :
6113 132 : if (lbs != NULL)
6114 : {
6115 54 : if (ARR_NDIM(lbs) > 1)
6116 0 : ereport(ERROR,
6117 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
6118 : errmsg("wrong number of array subscripts"),
6119 : errdetail("Dimension array must be one dimensional.")));
6120 :
6121 54 : if (array_contains_nulls(lbs))
6122 0 : ereport(ERROR,
6123 : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
6124 : errmsg("dimension values cannot be null")));
6125 :
6126 54 : if (ndims != ((ARR_NDIM(lbs) > 0) ? ARR_DIMS(lbs)[0] : 0))
6127 12 : ereport(ERROR,
6128 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
6129 : errmsg("wrong number of array subscripts"),
6130 : errdetail("Low bound array has different size than dimensions array.")));
6131 :
6132 42 : lbsv = (int *) ARR_DATA_PTR(lbs);
6133 : }
6134 : else
6135 : {
6136 : int i;
6137 :
6138 546 : for (i = 0; i < MAXDIM; i++)
6139 468 : deflbs[i] = 1;
6140 :
6141 78 : lbsv = deflbs;
6142 : }
6143 :
6144 : /* This checks for overflow of the array dimensions */
6145 120 : nitems = ArrayGetNItems(ndims, dimv);
6146 120 : ArrayCheckBounds(ndims, dimv, lbsv);
6147 :
6148 : /* fast track for empty array */
6149 120 : if (nitems <= 0)
6150 54 : return construct_empty_array(elmtype);
6151 :
6152 : /*
6153 : * We arrange to look up info about element type only once per series of
6154 : * calls, assuming the element type doesn't change underneath us.
6155 : */
6156 66 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
6157 66 : if (my_extra == NULL)
6158 : {
6159 66 : fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
6160 : sizeof(ArrayMetaState));
6161 66 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
6162 66 : my_extra->element_type = InvalidOid;
6163 : }
6164 :
6165 66 : if (my_extra->element_type != elmtype)
6166 : {
6167 : /* Get info about element type */
6168 66 : get_typlenbyvalalign(elmtype,
6169 : &my_extra->typlen,
6170 : &my_extra->typbyval,
6171 : &my_extra->typalign);
6172 66 : my_extra->element_type = elmtype;
6173 : }
6174 :
6175 66 : elmlen = my_extra->typlen;
6176 66 : elmbyval = my_extra->typbyval;
6177 66 : elmalign = my_extra->typalign;
6178 :
6179 : /* compute required space */
6180 66 : if (!isnull)
6181 : {
6182 : int i;
6183 : char *p;
6184 : int nbytes;
6185 : int totbytes;
6186 :
6187 : /* make sure data is not toasted */
6188 42 : if (elmlen == -1)
6189 12 : value = PointerGetDatum(PG_DETOAST_DATUM(value));
6190 :
6191 42 : nbytes = att_addlength_datum(0, elmlen, value);
6192 42 : nbytes = att_align_nominal(nbytes, elmalign);
6193 : Assert(nbytes > 0);
6194 :
6195 42 : totbytes = nbytes * nitems;
6196 :
6197 : /* check for overflow of multiplication or total request */
6198 42 : if (totbytes / nbytes != nitems ||
6199 42 : !AllocSizeIsValid(totbytes))
6200 0 : ereport(ERROR,
6201 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
6202 : errmsg("array size exceeds the maximum allowed (%d)",
6203 : (int) MaxAllocSize)));
6204 :
6205 : /*
6206 : * This addition can't overflow, but it might cause us to go past
6207 : * MaxAllocSize. We leave it to palloc to complain in that case.
6208 : */
6209 42 : totbytes += ARR_OVERHEAD_NONULLS(ndims);
6210 :
6211 42 : result = create_array_envelope(ndims, dimv, lbsv, totbytes,
6212 : elmtype, 0);
6213 :
6214 42 : p = ARR_DATA_PTR(result);
6215 3000306 : for (i = 0; i < nitems; i++)
6216 3000264 : p += ArrayCastAndSet(value, elmlen, elmbyval, elmalign, p);
6217 : }
6218 : else
6219 : {
6220 : int nbytes;
6221 : int dataoffset;
6222 :
6223 24 : dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
6224 24 : nbytes = dataoffset;
6225 :
6226 24 : result = create_array_envelope(ndims, dimv, lbsv, nbytes,
6227 : elmtype, dataoffset);
6228 :
6229 : /* create_array_envelope already zeroed the bitmap, so we're done */
6230 : }
6231 :
6232 66 : return result;
6233 : }
6234 :
6235 :
6236 : /*
6237 : * UNNEST
6238 : */
6239 : Datum
6240 491262 : array_unnest(PG_FUNCTION_ARGS)
6241 : {
6242 : typedef struct
6243 : {
6244 : array_iter iter;
6245 : int nextelem;
6246 : int numelems;
6247 : int16 elmlen;
6248 : bool elmbyval;
6249 : char elmalign;
6250 : } array_unnest_fctx;
6251 :
6252 : FuncCallContext *funcctx;
6253 : array_unnest_fctx *fctx;
6254 : MemoryContext oldcontext;
6255 :
6256 : /* stuff done only on the first call of the function */
6257 491262 : if (SRF_IS_FIRSTCALL())
6258 : {
6259 : AnyArrayType *arr;
6260 :
6261 : /* create a function context for cross-call persistence */
6262 79054 : funcctx = SRF_FIRSTCALL_INIT();
6263 :
6264 : /*
6265 : * switch to memory context appropriate for multiple function calls
6266 : */
6267 79054 : oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
6268 :
6269 : /*
6270 : * Get the array value and detoast if needed. We can't do this
6271 : * earlier because if we have to detoast, we want the detoasted copy
6272 : * to be in multi_call_memory_ctx, so it will go away when we're done
6273 : * and not before. (If no detoast happens, we assume the originally
6274 : * passed array will stick around till then.)
6275 : */
6276 79054 : arr = PG_GETARG_ANY_ARRAY_P(0);
6277 :
6278 : /* allocate memory for user context */
6279 79054 : fctx = (array_unnest_fctx *) palloc(sizeof(array_unnest_fctx));
6280 :
6281 : /* initialize state */
6282 79054 : array_iter_setup(&fctx->iter, arr);
6283 79054 : fctx->nextelem = 0;
6284 79054 : fctx->numelems = ArrayGetNItems(AARR_NDIM(arr), AARR_DIMS(arr));
6285 :
6286 79054 : if (VARATT_IS_EXPANDED_HEADER(arr))
6287 : {
6288 : /* we can just grab the type data from expanded array */
6289 0 : fctx->elmlen = arr->xpn.typlen;
6290 0 : fctx->elmbyval = arr->xpn.typbyval;
6291 0 : fctx->elmalign = arr->xpn.typalign;
6292 : }
6293 : else
6294 79054 : get_typlenbyvalalign(AARR_ELEMTYPE(arr),
6295 : &fctx->elmlen,
6296 : &fctx->elmbyval,
6297 : &fctx->elmalign);
6298 :
6299 79054 : funcctx->user_fctx = fctx;
6300 79054 : MemoryContextSwitchTo(oldcontext);
6301 : }
6302 :
6303 : /* stuff done on every call of the function */
6304 491262 : funcctx = SRF_PERCALL_SETUP();
6305 491262 : fctx = funcctx->user_fctx;
6306 :
6307 491262 : if (fctx->nextelem < fctx->numelems)
6308 : {
6309 412208 : int offset = fctx->nextelem++;
6310 : Datum elem;
6311 :
6312 412208 : elem = array_iter_next(&fctx->iter, &fcinfo->isnull, offset,
6313 412208 : fctx->elmlen, fctx->elmbyval, fctx->elmalign);
6314 :
6315 412208 : SRF_RETURN_NEXT(funcctx, elem);
6316 : }
6317 : else
6318 : {
6319 : /* do when there is no more left */
6320 79054 : SRF_RETURN_DONE(funcctx);
6321 : }
6322 : }
6323 :
6324 : /*
6325 : * Planner support function for array_unnest(anyarray)
6326 : *
6327 : * Note: this is now also used for information_schema._pg_expandarray(),
6328 : * which is simply a wrapper around array_unnest().
6329 : */
6330 : Datum
6331 14370 : array_unnest_support(PG_FUNCTION_ARGS)
6332 : {
6333 14370 : Node *rawreq = (Node *) PG_GETARG_POINTER(0);
6334 14370 : Node *ret = NULL;
6335 :
6336 14370 : if (IsA(rawreq, SupportRequestRows))
6337 : {
6338 : /* Try to estimate the number of rows returned */
6339 4754 : SupportRequestRows *req = (SupportRequestRows *) rawreq;
6340 :
6341 4754 : if (is_funcclause(req->node)) /* be paranoid */
6342 : {
6343 4748 : List *args = ((FuncExpr *) req->node)->args;
6344 : Node *arg1;
6345 :
6346 : /* We can use estimated argument values here */
6347 4748 : arg1 = estimate_expression_value(req->root, linitial(args));
6348 :
6349 4748 : req->rows = estimate_array_length(req->root, arg1);
6350 4748 : ret = (Node *) req;
6351 : }
6352 : }
6353 :
6354 14370 : PG_RETURN_POINTER(ret);
6355 : }
6356 :
6357 :
6358 : /*
6359 : * array_replace/array_remove support
6360 : *
6361 : * Find all array entries matching (not distinct from) search/search_isnull,
6362 : * and delete them if remove is true, else replace them with
6363 : * replace/replace_isnull. Comparisons are done using the specified
6364 : * collation. fcinfo is passed only for caching purposes.
6365 : */
6366 : static ArrayType *
6367 3018 : array_replace_internal(ArrayType *array,
6368 : Datum search, bool search_isnull,
6369 : Datum replace, bool replace_isnull,
6370 : bool remove, Oid collation,
6371 : FunctionCallInfo fcinfo)
6372 : {
6373 3018 : LOCAL_FCINFO(locfcinfo, 2);
6374 : ArrayType *result;
6375 : Oid element_type;
6376 : Datum *values;
6377 : bool *nulls;
6378 : int *dim;
6379 : int ndim;
6380 : int nitems,
6381 : nresult;
6382 : int i;
6383 3018 : int32 nbytes = 0;
6384 : int32 dataoffset;
6385 : bool hasnulls;
6386 : int typlen;
6387 : bool typbyval;
6388 : char typalign;
6389 : char *arraydataptr;
6390 : bits8 *bitmap;
6391 : int bitmask;
6392 3018 : bool changed = false;
6393 : TypeCacheEntry *typentry;
6394 :
6395 3018 : element_type = ARR_ELEMTYPE(array);
6396 3018 : ndim = ARR_NDIM(array);
6397 3018 : dim = ARR_DIMS(array);
6398 3018 : nitems = ArrayGetNItems(ndim, dim);
6399 :
6400 : /* Return input array unmodified if it is empty */
6401 3018 : if (nitems <= 0)
6402 0 : return array;
6403 :
6404 : /*
6405 : * We can't remove elements from multi-dimensional arrays, since the
6406 : * result might not be rectangular.
6407 : */
6408 3018 : if (remove && ndim > 1)
6409 6 : ereport(ERROR,
6410 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6411 : errmsg("removing elements from multidimensional arrays is not supported")));
6412 :
6413 : /*
6414 : * We arrange to look up the equality function only once per series of
6415 : * calls, assuming the element type doesn't change underneath us.
6416 : */
6417 3012 : typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
6418 3012 : if (typentry == NULL ||
6419 2320 : typentry->type_id != element_type)
6420 : {
6421 692 : typentry = lookup_type_cache(element_type,
6422 : TYPECACHE_EQ_OPR_FINFO);
6423 692 : if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
6424 0 : ereport(ERROR,
6425 : (errcode(ERRCODE_UNDEFINED_FUNCTION),
6426 : errmsg("could not identify an equality operator for type %s",
6427 : format_type_be(element_type))));
6428 692 : fcinfo->flinfo->fn_extra = (void *) typentry;
6429 : }
6430 3012 : typlen = typentry->typlen;
6431 3012 : typbyval = typentry->typbyval;
6432 3012 : typalign = typentry->typalign;
6433 :
6434 : /*
6435 : * Detoast values if they are toasted. The replacement value must be
6436 : * detoasted for insertion into the result array, while detoasting the
6437 : * search value only once saves cycles.
6438 : */
6439 3012 : if (typlen == -1)
6440 : {
6441 2970 : if (!search_isnull)
6442 2964 : search = PointerGetDatum(PG_DETOAST_DATUM(search));
6443 2970 : if (!replace_isnull)
6444 12 : replace = PointerGetDatum(PG_DETOAST_DATUM(replace));
6445 : }
6446 :
6447 : /* Prepare to apply the comparison operator */
6448 3012 : InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
6449 : collation, NULL, NULL);
6450 :
6451 : /* Allocate temporary arrays for new values */
6452 3012 : values = (Datum *) palloc(nitems * sizeof(Datum));
6453 3012 : nulls = (bool *) palloc(nitems * sizeof(bool));
6454 :
6455 : /* Loop over source data */
6456 3012 : arraydataptr = ARR_DATA_PTR(array);
6457 3012 : bitmap = ARR_NULLBITMAP(array);
6458 3012 : bitmask = 1;
6459 3012 : hasnulls = false;
6460 3012 : nresult = 0;
6461 :
6462 6632 : for (i = 0; i < nitems; i++)
6463 : {
6464 : Datum elt;
6465 : bool isNull;
6466 : bool oprresult;
6467 3620 : bool skip = false;
6468 :
6469 : /* Get source element, checking for NULL */
6470 3620 : if (bitmap && (*bitmap & bitmask) == 0)
6471 : {
6472 36 : isNull = true;
6473 : /* If searching for NULL, we have a match */
6474 36 : if (search_isnull)
6475 : {
6476 36 : if (remove)
6477 : {
6478 12 : skip = true;
6479 12 : changed = true;
6480 : }
6481 24 : else if (!replace_isnull)
6482 : {
6483 18 : values[nresult] = replace;
6484 18 : isNull = false;
6485 18 : changed = true;
6486 : }
6487 : }
6488 : }
6489 : else
6490 : {
6491 3584 : isNull = false;
6492 3584 : elt = fetch_att(arraydataptr, typbyval, typlen);
6493 3584 : arraydataptr = att_addlength_datum(arraydataptr, typlen, elt);
6494 3584 : arraydataptr = (char *) att_align_nominal(arraydataptr, typalign);
6495 :
6496 3584 : if (search_isnull)
6497 : {
6498 : /* no match possible, keep element */
6499 54 : values[nresult] = elt;
6500 : }
6501 : else
6502 : {
6503 : /*
6504 : * Apply the operator to the element pair; treat NULL as false
6505 : */
6506 3530 : locfcinfo->args[0].value = elt;
6507 3530 : locfcinfo->args[0].isnull = false;
6508 3530 : locfcinfo->args[1].value = search;
6509 3530 : locfcinfo->args[1].isnull = false;
6510 3530 : locfcinfo->isnull = false;
6511 3530 : oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
6512 3530 : if (locfcinfo->isnull || !oprresult)
6513 : {
6514 : /* no match, keep element */
6515 3376 : values[nresult] = elt;
6516 : }
6517 : else
6518 : {
6519 : /* match, so replace or delete */
6520 154 : changed = true;
6521 154 : if (remove)
6522 130 : skip = true;
6523 : else
6524 : {
6525 24 : values[nresult] = replace;
6526 24 : isNull = replace_isnull;
6527 : }
6528 : }
6529 : }
6530 : }
6531 :
6532 3620 : if (!skip)
6533 : {
6534 3478 : nulls[nresult] = isNull;
6535 3478 : if (isNull)
6536 12 : hasnulls = true;
6537 : else
6538 : {
6539 : /* Update total result size */
6540 3466 : nbytes = att_addlength_datum(nbytes, typlen, values[nresult]);
6541 3466 : nbytes = att_align_nominal(nbytes, typalign);
6542 : /* check for overflow of total request */
6543 3466 : if (!AllocSizeIsValid(nbytes))
6544 0 : ereport(ERROR,
6545 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
6546 : errmsg("array size exceeds the maximum allowed (%d)",
6547 : (int) MaxAllocSize)));
6548 : }
6549 3478 : nresult++;
6550 : }
6551 :
6552 : /* advance bitmap pointer if any */
6553 3620 : if (bitmap)
6554 : {
6555 90 : bitmask <<= 1;
6556 90 : if (bitmask == 0x100)
6557 : {
6558 0 : bitmap++;
6559 0 : bitmask = 1;
6560 : }
6561 : }
6562 : }
6563 :
6564 : /*
6565 : * If not changed just return the original array
6566 : */
6567 3012 : if (!changed)
6568 : {
6569 2864 : pfree(values);
6570 2864 : pfree(nulls);
6571 2864 : return array;
6572 : }
6573 :
6574 : /* If all elements were removed return an empty array */
6575 148 : if (nresult == 0)
6576 : {
6577 6 : pfree(values);
6578 6 : pfree(nulls);
6579 6 : return construct_empty_array(element_type);
6580 : }
6581 :
6582 : /* Allocate and initialize the result array */
6583 142 : if (hasnulls)
6584 : {
6585 6 : dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nresult);
6586 6 : nbytes += dataoffset;
6587 : }
6588 : else
6589 : {
6590 136 : dataoffset = 0; /* marker for no null bitmap */
6591 136 : nbytes += ARR_OVERHEAD_NONULLS(ndim);
6592 : }
6593 142 : result = (ArrayType *) palloc0(nbytes);
6594 142 : SET_VARSIZE(result, nbytes);
6595 142 : result->ndim = ndim;
6596 142 : result->dataoffset = dataoffset;
6597 142 : result->elemtype = element_type;
6598 142 : memcpy(ARR_DIMS(result), ARR_DIMS(array), ndim * sizeof(int));
6599 142 : memcpy(ARR_LBOUND(result), ARR_LBOUND(array), ndim * sizeof(int));
6600 :
6601 142 : if (remove)
6602 : {
6603 : /* Adjust the result length */
6604 112 : ARR_DIMS(result)[0] = nresult;
6605 : }
6606 :
6607 : /* Insert data into result array */
6608 142 : CopyArrayEls(result,
6609 : values, nulls, nresult,
6610 : typlen, typbyval, typalign,
6611 : false);
6612 :
6613 142 : pfree(values);
6614 142 : pfree(nulls);
6615 :
6616 142 : return result;
6617 : }
6618 :
6619 : /*
6620 : * Remove any occurrences of an element from an array
6621 : *
6622 : * If used on a multi-dimensional array this will raise an error.
6623 : */
6624 : Datum
6625 161922 : array_remove(PG_FUNCTION_ARGS)
6626 : {
6627 : ArrayType *array;
6628 161922 : Datum search = PG_GETARG_DATUM(1);
6629 161922 : bool search_isnull = PG_ARGISNULL(1);
6630 :
6631 161922 : if (PG_ARGISNULL(0))
6632 158940 : PG_RETURN_NULL();
6633 2982 : array = PG_GETARG_ARRAYTYPE_P(0);
6634 :
6635 2982 : array = array_replace_internal(array,
6636 : search, search_isnull,
6637 : (Datum) 0, true,
6638 : true, PG_GET_COLLATION(),
6639 : fcinfo);
6640 2976 : PG_RETURN_ARRAYTYPE_P(array);
6641 : }
6642 :
6643 : /*
6644 : * Replace any occurrences of an element in an array
6645 : */
6646 : Datum
6647 36 : array_replace(PG_FUNCTION_ARGS)
6648 : {
6649 : ArrayType *array;
6650 36 : Datum search = PG_GETARG_DATUM(1);
6651 36 : bool search_isnull = PG_ARGISNULL(1);
6652 36 : Datum replace = PG_GETARG_DATUM(2);
6653 36 : bool replace_isnull = PG_ARGISNULL(2);
6654 :
6655 36 : if (PG_ARGISNULL(0))
6656 0 : PG_RETURN_NULL();
6657 36 : array = PG_GETARG_ARRAYTYPE_P(0);
6658 :
6659 36 : array = array_replace_internal(array,
6660 : search, search_isnull,
6661 : replace, replace_isnull,
6662 : false, PG_GET_COLLATION(),
6663 : fcinfo);
6664 36 : PG_RETURN_ARRAYTYPE_P(array);
6665 : }
6666 :
6667 : /*
6668 : * Implements width_bucket(anyelement, anyarray).
6669 : *
6670 : * 'thresholds' is an array containing lower bound values for each bucket;
6671 : * these must be sorted from smallest to largest, or bogus results will be
6672 : * produced. If N thresholds are supplied, the output is from 0 to N:
6673 : * 0 is for inputs < first threshold, N is for inputs >= last threshold.
6674 : */
6675 : Datum
6676 810 : width_bucket_array(PG_FUNCTION_ARGS)
6677 : {
6678 810 : Datum operand = PG_GETARG_DATUM(0);
6679 810 : ArrayType *thresholds = PG_GETARG_ARRAYTYPE_P(1);
6680 810 : Oid collation = PG_GET_COLLATION();
6681 810 : Oid element_type = ARR_ELEMTYPE(thresholds);
6682 : int result;
6683 :
6684 : /* Check input */
6685 810 : if (ARR_NDIM(thresholds) > 1)
6686 6 : ereport(ERROR,
6687 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
6688 : errmsg("thresholds must be one-dimensional array")));
6689 :
6690 804 : if (array_contains_nulls(thresholds))
6691 6 : ereport(ERROR,
6692 : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
6693 : errmsg("thresholds array must not contain NULLs")));
6694 :
6695 : /* We have a dedicated implementation for float8 data */
6696 798 : if (element_type == FLOAT8OID)
6697 366 : result = width_bucket_array_float8(operand, thresholds);
6698 : else
6699 : {
6700 : TypeCacheEntry *typentry;
6701 :
6702 : /* Cache information about the input type */
6703 432 : typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
6704 432 : if (typentry == NULL ||
6705 390 : typentry->type_id != element_type)
6706 : {
6707 42 : typentry = lookup_type_cache(element_type,
6708 : TYPECACHE_CMP_PROC_FINFO);
6709 42 : if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
6710 0 : ereport(ERROR,
6711 : (errcode(ERRCODE_UNDEFINED_FUNCTION),
6712 : errmsg("could not identify a comparison function for type %s",
6713 : format_type_be(element_type))));
6714 42 : fcinfo->flinfo->fn_extra = (void *) typentry;
6715 : }
6716 :
6717 : /*
6718 : * We have separate implementation paths for fixed- and variable-width
6719 : * types, since indexing the array is a lot cheaper in the first case.
6720 : */
6721 432 : if (typentry->typlen > 0)
6722 90 : result = width_bucket_array_fixed(operand, thresholds,
6723 : collation, typentry);
6724 : else
6725 342 : result = width_bucket_array_variable(operand, thresholds,
6726 : collation, typentry);
6727 : }
6728 :
6729 : /* Avoid leaking memory when handed toasted input. */
6730 798 : PG_FREE_IF_COPY(thresholds, 1);
6731 :
6732 798 : PG_RETURN_INT32(result);
6733 : }
6734 :
6735 : /*
6736 : * width_bucket_array for float8 data.
6737 : */
6738 : static int
6739 366 : width_bucket_array_float8(Datum operand, ArrayType *thresholds)
6740 : {
6741 366 : float8 op = DatumGetFloat8(operand);
6742 : float8 *thresholds_data;
6743 : int left;
6744 : int right;
6745 :
6746 : /*
6747 : * Since we know the array contains no NULLs, we can just index it
6748 : * directly.
6749 : */
6750 366 : thresholds_data = (float8 *) ARR_DATA_PTR(thresholds);
6751 :
6752 366 : left = 0;
6753 366 : right = ArrayGetNItems(ARR_NDIM(thresholds), ARR_DIMS(thresholds));
6754 :
6755 : /*
6756 : * If the probe value is a NaN, it's greater than or equal to all possible
6757 : * threshold values (including other NaNs), so we need not search. Note
6758 : * that this would give the same result as searching even if the array
6759 : * contains multiple NaNs (as long as they're correctly sorted), since the
6760 : * loop logic will find the rightmost of multiple equal threshold values.
6761 : */
6762 366 : if (isnan(op))
6763 6 : return right;
6764 :
6765 : /* Find the bucket */
6766 1134 : while (left < right)
6767 : {
6768 774 : int mid = (left + right) / 2;
6769 :
6770 774 : if (isnan(thresholds_data[mid]) || op < thresholds_data[mid])
6771 336 : right = mid;
6772 : else
6773 438 : left = mid + 1;
6774 : }
6775 :
6776 360 : return left;
6777 : }
6778 :
6779 : /*
6780 : * width_bucket_array for generic fixed-width data types.
6781 : */
6782 : static int
6783 90 : width_bucket_array_fixed(Datum operand,
6784 : ArrayType *thresholds,
6785 : Oid collation,
6786 : TypeCacheEntry *typentry)
6787 : {
6788 90 : LOCAL_FCINFO(locfcinfo, 2);
6789 : char *thresholds_data;
6790 90 : int typlen = typentry->typlen;
6791 90 : bool typbyval = typentry->typbyval;
6792 : int left;
6793 : int right;
6794 :
6795 : /*
6796 : * Since we know the array contains no NULLs, we can just index it
6797 : * directly.
6798 : */
6799 90 : thresholds_data = (char *) ARR_DATA_PTR(thresholds);
6800 :
6801 90 : InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
6802 : collation, NULL, NULL);
6803 :
6804 : /* Find the bucket */
6805 90 : left = 0;
6806 90 : right = ArrayGetNItems(ARR_NDIM(thresholds), ARR_DIMS(thresholds));
6807 270 : while (left < right)
6808 : {
6809 180 : int mid = (left + right) / 2;
6810 : char *ptr;
6811 : int32 cmpresult;
6812 :
6813 180 : ptr = thresholds_data + mid * typlen;
6814 :
6815 180 : locfcinfo->args[0].value = operand;
6816 180 : locfcinfo->args[0].isnull = false;
6817 180 : locfcinfo->args[1].value = fetch_att(ptr, typbyval, typlen);
6818 180 : locfcinfo->args[1].isnull = false;
6819 :
6820 180 : cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
6821 :
6822 : /* We don't expect comparison support functions to return null */
6823 : Assert(!locfcinfo->isnull);
6824 :
6825 180 : if (cmpresult < 0)
6826 90 : right = mid;
6827 : else
6828 90 : left = mid + 1;
6829 : }
6830 :
6831 90 : return left;
6832 : }
6833 :
6834 : /*
6835 : * width_bucket_array for generic variable-width data types.
6836 : */
6837 : static int
6838 342 : width_bucket_array_variable(Datum operand,
6839 : ArrayType *thresholds,
6840 : Oid collation,
6841 : TypeCacheEntry *typentry)
6842 : {
6843 342 : LOCAL_FCINFO(locfcinfo, 2);
6844 : char *thresholds_data;
6845 342 : int typlen = typentry->typlen;
6846 342 : bool typbyval = typentry->typbyval;
6847 342 : char typalign = typentry->typalign;
6848 : int left;
6849 : int right;
6850 :
6851 342 : thresholds_data = (char *) ARR_DATA_PTR(thresholds);
6852 :
6853 342 : InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
6854 : collation, NULL, NULL);
6855 :
6856 : /* Find the bucket */
6857 342 : left = 0;
6858 342 : right = ArrayGetNItems(ARR_NDIM(thresholds), ARR_DIMS(thresholds));
6859 1068 : while (left < right)
6860 : {
6861 726 : int mid = (left + right) / 2;
6862 : char *ptr;
6863 : int i;
6864 : int32 cmpresult;
6865 :
6866 : /* Locate mid'th array element by advancing from left element */
6867 726 : ptr = thresholds_data;
6868 1242 : for (i = left; i < mid; i++)
6869 : {
6870 516 : ptr = att_addlength_pointer(ptr, typlen, ptr);
6871 516 : ptr = (char *) att_align_nominal(ptr, typalign);
6872 : }
6873 :
6874 726 : locfcinfo->args[0].value = operand;
6875 726 : locfcinfo->args[0].isnull = false;
6876 726 : locfcinfo->args[1].value = fetch_att(ptr, typbyval, typlen);
6877 726 : locfcinfo->args[1].isnull = false;
6878 :
6879 726 : cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
6880 :
6881 : /* We don't expect comparison support functions to return null */
6882 : Assert(!locfcinfo->isnull);
6883 :
6884 726 : if (cmpresult < 0)
6885 300 : right = mid;
6886 : else
6887 : {
6888 426 : left = mid + 1;
6889 :
6890 : /*
6891 : * Move the thresholds pointer to match new "left" index, so we
6892 : * don't have to seek over those elements again. This trick
6893 : * ensures we do only O(N) array indexing work, not O(N^2).
6894 : */
6895 426 : ptr = att_addlength_pointer(ptr, typlen, ptr);
6896 426 : thresholds_data = (char *) att_align_nominal(ptr, typalign);
6897 : }
6898 : }
6899 :
6900 342 : return left;
6901 : }
6902 :
6903 : /*
6904 : * Trim the last N elements from an array by building an appropriate slice.
6905 : * Only the first dimension is trimmed.
6906 : */
6907 : Datum
6908 48 : trim_array(PG_FUNCTION_ARGS)
6909 : {
6910 48 : ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
6911 48 : int n = PG_GETARG_INT32(1);
6912 48 : int array_length = (ARR_NDIM(v) > 0) ? ARR_DIMS(v)[0] : 0;
6913 : int16 elmlen;
6914 : bool elmbyval;
6915 : char elmalign;
6916 : int lower[MAXDIM];
6917 : int upper[MAXDIM];
6918 : bool lowerProvided[MAXDIM];
6919 : bool upperProvided[MAXDIM];
6920 : Datum result;
6921 :
6922 : /* Per spec, throw an error if out of bounds */
6923 48 : if (n < 0 || n > array_length)
6924 18 : ereport(ERROR,
6925 : (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
6926 : errmsg("number of elements to trim must be between 0 and %d",
6927 : array_length)));
6928 :
6929 : /* Set all the bounds as unprovided except the first upper bound */
6930 30 : memset(lowerProvided, false, sizeof(lowerProvided));
6931 30 : memset(upperProvided, false, sizeof(upperProvided));
6932 30 : if (ARR_NDIM(v) > 0)
6933 : {
6934 30 : upper[0] = ARR_LBOUND(v)[0] + array_length - n - 1;
6935 30 : upperProvided[0] = true;
6936 : }
6937 :
6938 : /* Fetch the needed information about the element type */
6939 30 : get_typlenbyvalalign(ARR_ELEMTYPE(v), &elmlen, &elmbyval, &elmalign);
6940 :
6941 : /* Get the slice */
6942 30 : result = array_get_slice(PointerGetDatum(v), 1,
6943 : upper, lower, upperProvided, lowerProvided,
6944 : -1, elmlen, elmbyval, elmalign);
6945 :
6946 30 : PG_RETURN_DATUM(result);
6947 : }
|