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