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