PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
arrayfuncs.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * arrayfuncs.c
4 * Support functions for arrays.
5 *
6 * Portions Copyright (c) 1996-2025, 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 */
43bool 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 */
57typedef enum
58{
66
67/* Working state for array_iterate() */
68typedef 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 */
90
91static bool ReadArrayDimensions(char **srcptr, int *ndim_p,
92 int *dim, int *lBound,
93 const char *origStr, Node *escontext);
94static bool ReadDimensionInt(char **srcptr, int *result,
95 const char *origStr, Node *escontext);
96static 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);
104static ArrayToken ReadArrayToken(char **srcptr, StringInfo elembuf, char typdelim,
105 const char *origStr, Node *escontext);
106static 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);
111static 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);
116static 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);
121static bool array_get_isnull(const bits8 *nullbitmap, int offset);
122static void array_set_isnull(bits8 *nullbitmap, int offset, bool isNull);
123static Datum ArrayCast(char *value, bool byval, int len);
124static int ArrayCastAndSet(Datum src,
125 int typlen, bool typbyval, char typalign,
126 char *dest);
127static char *array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
128 int typlen, bool typbyval, char typalign);
129static int array_nelems_size(char *ptr, int offset, bits8 *nullbitmap,
130 int nitems, int typlen, bool typbyval, char typalign);
131static int array_copy(char *destptr, int nitems,
132 char *srcptr, int offset, bits8 *nullbitmap,
133 int typlen, bool typbyval, char typalign);
134static 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);
138static 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);
143static 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);
148static int array_cmp(FunctionCallInfo fcinfo);
149static ArrayType *create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
150 Oid elmtype, int dataoffset);
152 Datum value, bool isnull, Oid elmtype,
153 FunctionCallInfo fcinfo);
155 Datum search, bool search_isnull,
156 Datum replace, bool replace_isnull,
157 bool remove, Oid collation,
158 FunctionCallInfo fcinfo);
159static int width_bucket_array_float8(Datum operand, ArrayType *thresholds);
160static int width_bucket_array_fixed(Datum operand,
161 ArrayType *thresholds,
162 Oid collation,
163 TypeCacheEntry *typentry);
164static 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 */
178Datum
180{
181 char *string = PG_GETARG_CSTRING(0); /* external form */
182 Oid element_type = PG_GETARG_OID(1); /* type of an array
183 * element */
184 int32 typmod = PG_GETARG_INT32(2); /* typmod for array elements */
185 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 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
210 if (my_extra == NULL)
211 {
212 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
213 sizeof(ArrayMetaState));
214 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
215 my_extra->element_type = ~element_type;
216 }
217
218 if (my_extra->element_type != element_type)
219 {
220 /*
221 * Get info about element type, including its input conversion proc
222 */
223 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 fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
228 fcinfo->flinfo->fn_mcxt);
229 my_extra->element_type = element_type;
230 }
231 typlen = my_extra->typlen;
232 typbyval = my_extra->typbyval;
233 typalign = my_extra->typalign;
234 typdelim = my_extra->typdelim;
235 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 for (int i = 0; i < MAXDIM; i++)
243 {
244 dim[i] = -1; /* indicates "not yet known" */
245 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 p = string;
255 if (!ReadArrayDimensions(&p, &ndim, dim, lBound, string, escontext))
256 return (Datum) 0;
257
258 if (ndim == 0)
259 {
260 /* No array dimensions, so next character should be a left brace */
261 if (*p != '{')
262 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 if (strncmp(p, ASSGN, strlen(ASSGN)) != 0)
271 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 p += strlen(ASSGN);
277 /* Allow whitespace after it */
278 while (scanner_isspace(*p))
279 p++;
280
281 if (*p != '{')
282 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 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 return (Datum) 0;
300
301 /* only whitespace is allowed after the closing brace */
302 while (*p)
303 {
304 if (!scanner_isspace(*p++))
305 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 if (nitems == 0)
314
315 /*
316 * Check for nulls, compute total data space needed
317 */
318 hasnulls = false;
319 nbytes = 0;
320 for (int i = 0; i < nitems; i++)
321 {
322 if (nulls[i])
323 hasnulls = true;
324 else
325 {
326 /* let's just make sure data is not toasted */
327 if (typlen == -1)
329 nbytes = att_addlength_datum(nbytes, typlen, values[i]);
330 nbytes = att_align_nominal(nbytes, typalign);
331 /* check for overflow of total request */
332 if (!AllocSizeIsValid(nbytes))
333 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 if (hasnulls)
340 {
341 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
342 nbytes += dataoffset;
343 }
344 else
345 {
346 dataoffset = 0; /* marker for no null bitmap */
347 nbytes += ARR_OVERHEAD_NONULLS(ndim);
348 }
349
350 /*
351 * Construct the final array datum
352 */
353 retval = (ArrayType *) palloc0(nbytes);
354 SET_VARSIZE(retval, nbytes);
355 retval->ndim = ndim;
356 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 retval->elemtype = element_type;
364 memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
365 memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
366
367 CopyArrayEls(retval,
368 values, nulls, nitems,
369 typlen, typbyval, typalign,
370 true);
371
372 pfree(values);
373 pfree(nulls);
374
375 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 */
401static bool
402ReadArrayDimensions(char **srcptr, int *ndim_p, int *dim, int *lBound,
403 const char *origStr, Node *escontext)
404{
405 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 ndim = 0;
413 for (;;)
414 {
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 while (scanner_isspace(*p))
424 p++;
425 if (*p != '[')
426 break; /* no more dimension items */
427 p++;
428 if (ndim >= MAXDIM)
429 ereturn(escontext, false,
430 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
431 errmsg("number of array dimensions exceeds the maximum allowed (%d)",
432 MAXDIM)));
433
434 q = p;
435 if (!ReadDimensionInt(&p, &i, origStr, escontext))
436 return false;
437 if (p == q) /* no digits? */
438 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 if (*p == ':')
444 {
445 /* [m:n] format */
446 lBound[ndim] = i;
447 p++;
448 q = p;
449 if (!ReadDimensionInt(&p, &ub, origStr, escontext))
450 return false;
451 if (p == q) /* no digits? */
452 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 lBound[ndim] = 1;
461 ub = i;
462 }
463 if (*p != ']')
464 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 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 if (ub < lBound[ndim])
479 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 if (ub == INT_MAX)
485 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 if (pg_sub_s32_overflow(ub, lBound[ndim], &ub) ||
491 pg_add_s32_overflow(ub, 1, &ub))
492 ereturn(escontext, false,
493 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
494 errmsg("array size exceeds the maximum allowed (%d)",
495 (int) MaxArraySize)));
496
497 dim[ndim] = ub;
498 ndim++;
499 }
500
501 *srcptr = p;
502 *ndim_p = ndim;
503 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 */
518static bool
519ReadDimensionInt(char **srcptr, int *result,
520 const char *origStr, Node *escontext)
521{
522 char *p = *srcptr;
523 long l;
524
525 /* don't accept leading whitespace */
526 if (!isdigit((unsigned char) *p) && *p != '-' && *p != '+')
527 {
528 *result = 0;
529 return true;
530 }
531
532 errno = 0;
533 l = strtol(p, srcptr, 10);
534
535 if (errno == ERANGE || l > PG_INT32_MAX || l < PG_INT32_MIN)
536 ereturn(escontext, false,
537 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
538 errmsg("array bound is out of integer range")));
539
540 *result = (int) l;
541 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 */
578static bool
579ReadArrayStr(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 int ndim = *ndim_p;
596 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 maxitems = 16;
609 values = palloc_array(Datum, maxitems);
610 nulls = palloc_array(bool, maxitems);
611
612 /* Allocate workspace to hold (string representation of) one element */
613 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 nest_level = 0;
620 nitems = 0;
621 ndim_frozen = dimensions_specified;
622 expect_delim = false;
623 do
624 {
625 ArrayToken tok;
626
627 tok = ReadArrayToken(srcptr, &elembuf, typdelim, origStr, escontext);
628
629 switch (tok)
630 {
631 case ATOK_LEVEL_START:
632 /* Can't write left brace where delim is expected */
633 if (expect_delim)
634 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 if (nest_level >= MAXDIM)
641 ereturn(escontext, false,
642 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
643 errmsg("number of array dimensions exceeds the maximum allowed (%d)",
644 MAXDIM)));
645
646 nelems[nest_level] = 0;
647 nest_level++;
648 if (nest_level > ndim)
649 {
650 /* Can't increase ndim once it's frozen */
651 if (ndim_frozen)
652 goto dimension_error;
653 ndim = nest_level;
654 }
655 break;
656
657 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 if (nelems[nest_level - 1] > 0 && !expect_delim)
666 ereturn(escontext, false,
667 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
668 errmsg("malformed array literal: \"%s\"", origStr),
669 errdetail("Unexpected \"%c\" character.",
670 '}')));
671 nest_level--;
672 /* Nested sub-arrays count as elements of outer level */
673 if (nest_level > 0)
674 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 if (dim[nest_level] < 0)
682 {
683 /* Save length of first sub-array of this level */
684 dim[nest_level] = nelems[nest_level];
685 }
686 else if (nelems[nest_level] != dim[nest_level])
687 {
688 /* Subsequent sub-arrays must have same length */
689 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 expect_delim = true;
697 break;
698
699 case ATOK_DELIM:
700 if (!expect_delim)
701 ereturn(escontext, false,
702 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
703 errmsg("malformed array literal: \"%s\"", origStr),
704 errdetail("Unexpected \"%c\" character.",
705 typdelim)));
706 expect_delim = false;
707 break;
708
709 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 if (expect_delim)
716 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 if (nitems >= maxitems)
723 {
724 if (maxitems >= MaxArraySize)
725 ereturn(escontext, false,
726 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
727 errmsg("array size exceeds the maximum allowed (%d)",
728 (int) MaxArraySize)));
729 maxitems = Min(maxitems * 2, MaxArraySize);
730 values = repalloc_array(values, Datum, maxitems);
731 nulls = repalloc_array(nulls, bool, maxitems);
732 }
733
734 /* Read the element's value, or check that NULL is allowed */
735 if (!InputFunctionCallSafe(inputproc,
736 (tok == ATOK_ELEM_NULL) ? NULL : elembuf.data,
737 typioparam, typmod,
738 escontext,
739 &values[nitems]))
740 return false;
741 nulls[nitems] = (tok == ATOK_ELEM_NULL);
742 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 ndim_frozen = true;
750 if (nest_level != ndim)
751 goto dimension_error;
752 /* Count the new element */
753 nelems[nest_level - 1]++;
754
755 /* Must have a delim or a right brace following */
756 expect_delim = true;
757 break;
758
759 case ATOK_ERROR:
760 return false;
761 }
762 } while (nest_level > 0);
763
764 /* Clean up and return results */
765 pfree(elembuf.data);
766
767 *ndim_p = ndim;
768 *nitems_p = nitems;
769 *values_p = values;
770 *nulls_p = nulls;
771 return true;
772
773dimension_error:
774 if (dimensions_specified)
775 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 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 */
795static ArrayToken
796ReadArrayToken(char **srcptr, StringInfo elembuf, char typdelim,
797 const char *origStr, Node *escontext)
798{
799 char *p = *srcptr;
800 int dstlen;
801 bool has_escapes;
802
803 resetStringInfo(elembuf);
804
805 /* Identify token type. Loop advances over leading whitespace. */
806 for (;;)
807 {
808 switch (*p)
809 {
810 case '\0':
811 goto ending_error;
812 case '{':
813 *srcptr = p + 1;
814 return ATOK_LEVEL_START;
815 case '}':
816 *srcptr = p + 1;
817 return ATOK_LEVEL_END;
818 case '"':
819 p++;
820 goto quoted_element;
821 default:
822 if (*p == typdelim)
823 {
824 *srcptr = p + 1;
825 return ATOK_DELIM;
826 }
827 if (scanner_isspace(*p))
828 {
829 p++;
830 continue;
831 }
832 goto unquoted_element;
833 }
834 }
835
836quoted_element:
837 for (;;)
838 {
839 switch (*p)
840 {
841 case '\0':
842 goto ending_error;
843 case '\\':
844 /* Skip backslash, copy next character as-is. */
845 p++;
846 if (*p == '\0')
847 goto ending_error;
848 appendStringInfoChar(elembuf, *p++);
849 break;
850 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 while (*(++p) != '\0')
861 {
862 if (*p == typdelim || *p == '}' || *p == '{')
863 {
864 *srcptr = p;
865 return ATOK_ELEM;
866 }
867 if (!scanner_isspace(*p))
868 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 goto ending_error;
874 default:
875 appendStringInfoChar(elembuf, *p++);
876 break;
877 }
878 }
879
880unquoted_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 dstlen = 0;
887 has_escapes = false;
888 for (;;)
889 {
890 switch (*p)
891 {
892 case '\0':
893 goto ending_error;
894 case '{':
895 ereturn(escontext, ATOK_ERROR,
896 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
897 errmsg("malformed array literal: \"%s\"", origStr),
898 errdetail("Unexpected \"%c\" character.",
899 '{')));
900 case '"':
901 /* Must double-quote all or none of an element. */
902 ereturn(escontext, ATOK_ERROR,
903 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
904 errmsg("malformed array literal: \"%s\"", origStr),
905 errdetail("Incorrectly quoted array element.")));
906 case '\\':
907 /* Skip backslash, copy next character as-is. */
908 p++;
909 if (*p == '\0')
910 goto ending_error;
911 appendStringInfoChar(elembuf, *p++);
912 dstlen = elembuf->len; /* treat it as non-whitespace */
913 has_escapes = true;
914 break;
915 default:
916 /* End of elem? */
917 if (*p == typdelim || *p == '}')
918 {
919 /* hack: truncate the output string to dstlen */
920 elembuf->data[dstlen] = '\0';
921 elembuf->len = dstlen;
922 *srcptr = p;
923 /* Check if it's unquoted "NULL" */
924 if (Array_nulls && !has_escapes &&
925 pg_strcasecmp(elembuf->data, "NULL") == 0)
926 return ATOK_ELEM_NULL;
927 else
928 return ATOK_ELEM;
929 }
930 appendStringInfoChar(elembuf, *p);
931 if (!scanner_isspace(*p))
932 dstlen = elembuf->len;
933 p++;
934 break;
935 }
936 }
937
938ending_error:
939 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 */
960void
962 Datum *values,
963 bool *nulls,
964 int nitems,
965 int typlen,
966 bool typbyval,
967 char typalign,
968 bool freedata)
969{
970 char *p = ARR_DATA_PTR(array);
971 bits8 *bitmap = ARR_NULLBITMAP(array);
972 int bitval = 0;
973 int bitmask = 1;
974 int i;
975
976 if (typbyval)
977 freedata = false;
978
979 for (i = 0; i < nitems; i++)
980 {
981 if (nulls && nulls[i])
982 {
983 if (!bitmap) /* shouldn't happen */
984 elog(ERROR, "null array element where not supported");
985 /* bitmap bit stays 0 */
986 }
987 else
988 {
989 bitval |= bitmask;
990 p += ArrayCastAndSet(values[i], typlen, typbyval, typalign, p);
991 if (freedata)
993 }
994 if (bitmap)
995 {
996 bitmask <<= 1;
997 if (bitmask == 0x100)
998 {
999 *bitmap++ = bitval;
1000 bitval = 0;
1001 bitmask = 1;
1002 }
1003 }
1004 }
1005
1006 if (bitmap && bitmask != 1)
1007 *bitmap = bitval;
1008}
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 */
1015Datum
1017{
1019 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 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 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1055 if (my_extra == NULL)
1056 {
1057 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1058 sizeof(ArrayMetaState));
1059 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1060 my_extra->element_type = ~element_type;
1061 }
1062
1063 if (my_extra->element_type != element_type)
1064 {
1065 /*
1066 * Get info about element type, including its output conversion proc
1067 */
1068 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 fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1073 fcinfo->flinfo->fn_mcxt);
1074 my_extra->element_type = element_type;
1075 }
1076 typlen = my_extra->typlen;
1077 typbyval = my_extra->typbyval;
1078 typalign = my_extra->typalign;
1079 typdelim = my_extra->typdelim;
1080
1081 ndim = AARR_NDIM(v);
1082 dims = AARR_DIMS(v);
1083 lb = AARR_LBOUND(v);
1084 nitems = ArrayGetNItems(ndim, dims);
1085
1086 if (nitems == 0)
1087 {
1088 retval = pstrdup("{}");
1089 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 for (i = 0; i < ndim; i++)
1097 {
1098 if (lb[i] != 1)
1099 {
1100 needdims = true;
1101 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 values = (char **) palloc(nitems * sizeof(char *));
1111 needquotes = (bool *) palloc(nitems * sizeof(bool));
1112 overall_length = 0;
1113
1114 array_iter_setup(&iter, v);
1115
1116 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 itemvalue = array_iter_next(&iter, &isnull, i,
1124 typlen, typbyval, typalign);
1125
1126 if (isnull)
1127 {
1128 values[i] = pstrdup("NULL");
1129 overall_length += 4;
1130 needquote = false;
1131 }
1132 else
1133 {
1134 values[i] = OutputFunctionCall(&my_extra->proc, itemvalue);
1135
1136 /* count data plus backslashes; detect chars needing quotes */
1137 if (values[i][0] == '\0')
1138 needquote = true; /* force quotes for empty string */
1139 else if (pg_strcasecmp(values[i], "NULL") == 0)
1140 needquote = true; /* force quotes for literal NULL */
1141 else
1142 needquote = false;
1143
1144 for (tmp = values[i]; *tmp != '\0'; tmp++)
1145 {
1146 char ch = *tmp;
1147
1148 overall_length += 1;
1149 if (ch == '"' || ch == '\\')
1150 {
1151 needquote = true;
1152 overall_length += 1;
1153 }
1154 else if (ch == '{' || ch == '}' || ch == typdelim ||
1155 scanner_isspace(ch))
1156 needquote = true;
1157 }
1158 }
1159
1160 needquotes[i] = needquote;
1161
1162 /* Count the pair of double quotes, if needed */
1163 if (needquote)
1164 overall_length += 2;
1165 /* and the comma (or other typdelim delimiter) */
1166 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 for (i = j = 0, k = 1; i < ndim; i++)
1176 {
1177 j += k, k *= dims[i];
1178 }
1179 overall_length += 2 * j;
1180
1181 /* Format explicit dimensions if required */
1182 dims_str[0] = '\0';
1183 if (needdims)
1184 {
1185 char *ptr = dims_str;
1186
1187 for (i = 0; i < ndim; i++)
1188 {
1189 sprintf(ptr, "[%d:%d]", lb[i], lb[i] + dims[i] - 1);
1190 ptr += strlen(ptr);
1191 }
1192 *ptr++ = *ASSGN;
1193 *ptr = '\0';
1194 overall_length += ptr - dims_str;
1195 }
1196
1197 /* Now construct the output string */
1198 retval = (char *) palloc(overall_length);
1199 p = retval;
1200
1201#define APPENDSTR(str) (strcpy(p, (str)), p += strlen(p))
1202#define APPENDCHAR(ch) (*p++ = (ch), *p = '\0')
1203
1204 if (needdims)
1205 APPENDSTR(dims_str);
1206 APPENDCHAR('{');
1207 for (i = 0; i < ndim; i++)
1208 indx[i] = 0;
1209 j = 0;
1210 k = 0;
1211 do
1212 {
1213 for (i = j; i < ndim - 1; i++)
1214 APPENDCHAR('{');
1215
1216 if (needquotes[k])
1217 {
1218 APPENDCHAR('"');
1219 for (tmp = values[k]; *tmp; tmp++)
1220 {
1221 char ch = *tmp;
1222
1223 if (ch == '"' || ch == '\\')
1224 *p++ = '\\';
1225 *p++ = ch;
1226 }
1227 *p = '\0';
1228 APPENDCHAR('"');
1229 }
1230 else
1231 APPENDSTR(values[k]);
1232 pfree(values[k++]);
1233
1234 for (i = ndim - 1; i >= 0; i--)
1235 {
1236 if (++(indx[i]) < dims[i])
1237 {
1238 APPENDCHAR(typdelim);
1239 break;
1240 }
1241 else
1242 {
1243 indx[i] = 0;
1244 APPENDCHAR('}');
1245 }
1246 }
1247 j = i;
1248 } 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 pfree(values);
1257 pfree(needquotes);
1258
1259 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 */
1270Datum
1272{
1274 Oid spec_element_type = PG_GETARG_OID(1); /* type of an array
1275 * element */
1276 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 ndim = pq_getmsgint(buf, 4);
1298 if (ndim < 0) /* we do allow zero-dimension arrays */
1299 ereport(ERROR,
1300 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1301 errmsg("invalid number of dimensions: %d", ndim)));
1302 if (ndim > MAXDIM)
1303 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 flags = pq_getmsgint(buf, 4);
1309 if (flags != 0 && flags != 1)
1310 ereport(ERROR,
1311 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1312 errmsg("invalid array flags")));
1313
1314 /* Check element type recorded in the data */
1315 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 if (element_type != spec_element_type)
1329 {
1330 if (element_type < FirstGenbkiObjectId &&
1331 spec_element_type < FirstGenbkiObjectId)
1332 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,
1338 spec_element_type,
1339 format_type_extended(spec_element_type, -1,
1341 element_type = spec_element_type;
1342 }
1343
1344 for (i = 0; i < ndim; i++)
1345 {
1346 dim[i] = pq_getmsgint(buf, 4);
1347 lBound[i] = pq_getmsgint(buf, 4);
1348 }
1349
1350 /* This checks for overflow of array dimensions */
1351 nitems = ArrayGetNItems(ndim, dim);
1352 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 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1360 if (my_extra == NULL)
1361 {
1362 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1363 sizeof(ArrayMetaState));
1364 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1365 my_extra->element_type = ~element_type;
1366 }
1367
1368 if (my_extra->element_type != element_type)
1369 {
1370 /* Get info about element type, including its receive proc */
1371 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 if (!OidIsValid(my_extra->typiofunc))
1376 ereport(ERROR,
1377 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1378 errmsg("no binary input function available for type %s",
1379 format_type_be(element_type))));
1380 fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1381 fcinfo->flinfo->fn_mcxt);
1382 my_extra->element_type = element_type;
1383 }
1384
1385 if (nitems == 0)
1386 {
1387 /* Return empty array ... but not till we've validated element_type */
1389 }
1390
1391 typlen = my_extra->typlen;
1392 typbyval = my_extra->typbyval;
1393 typalign = my_extra->typalign;
1394 typioparam = my_extra->typioparam;
1395
1396 dataPtr = (Datum *) palloc(nitems * sizeof(Datum));
1397 nullsPtr = (bool *) palloc(nitems * sizeof(bool));
1399 &my_extra->proc, typioparam, typmod,
1400 typlen, typbyval, typalign,
1401 dataPtr, nullsPtr,
1402 &hasnulls, &nbytes);
1403 if (hasnulls)
1404 {
1405 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
1406 nbytes += dataoffset;
1407 }
1408 else
1409 {
1410 dataoffset = 0; /* marker for no null bitmap */
1411 nbytes += ARR_OVERHEAD_NONULLS(ndim);
1412 }
1413 retval = (ArrayType *) palloc0(nbytes);
1414 SET_VARSIZE(retval, nbytes);
1415 retval->ndim = ndim;
1416 retval->dataoffset = dataoffset;
1417 retval->elemtype = element_type;
1418 memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
1419 memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
1420
1421 CopyArrayEls(retval,
1422 dataPtr, nullsPtr, nitems,
1423 typlen, typbyval, typalign,
1424 true);
1425
1426 pfree(dataPtr);
1427 pfree(nullsPtr);
1428
1429 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 */
1453static void
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 for (i = 0; i < nitems; i++)
1472 {
1473 int itemlen;
1474 StringInfoData elem_buf;
1475
1476 /* Get and check the item length */
1477 itemlen = pq_getmsgint(buf, 4);
1478 if (itemlen < -1 || itemlen > (buf->len - buf->cursor))
1479 ereport(ERROR,
1480 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1481 errmsg("insufficient data left in message")));
1482
1483 if (itemlen == -1)
1484 {
1485 /* -1 length means NULL */
1486 values[i] = ReceiveFunctionCall(receiveproc, NULL,
1487 typioparam, typmod);
1488 nulls[i] = true;
1489 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 initReadOnlyStringInfo(&elem_buf, &buf->data[buf->cursor], itemlen);
1497
1498 buf->cursor += itemlen;
1499
1500 /* Now call the element's receiveproc */
1501 values[i] = ReceiveFunctionCall(receiveproc, &elem_buf,
1502 typioparam, typmod);
1503 nulls[i] = false;
1504
1505 /* Trouble if it didn't eat the whole buffer */
1506 if (elem_buf.cursor != itemlen)
1507 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 hasnull = false;
1517 totbytes = 0;
1518 for (i = 0; i < nitems; i++)
1519 {
1520 if (nulls[i])
1521 hasnull = true;
1522 else
1523 {
1524 /* let's just make sure data is not toasted */
1525 if (typlen == -1)
1527 totbytes = att_addlength_datum(totbytes, typlen, values[i]);
1528 totbytes = att_align_nominal(totbytes, typalign);
1529 /* check for overflow of total request */
1530 if (!AllocSizeIsValid(totbytes))
1531 ereport(ERROR,
1532 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1533 errmsg("array size exceeds the maximum allowed (%d)",
1534 (int) MaxAllocSize)));
1535 }
1536 }
1537 *hasnulls = hasnull;
1538 *nbytes = totbytes;
1539}
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 */
1547Datum
1549{
1551 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;
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 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1570 if (my_extra == NULL)
1571 {
1572 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1573 sizeof(ArrayMetaState));
1574 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1575 my_extra->element_type = ~element_type;
1576 }
1577
1578 if (my_extra->element_type != element_type)
1579 {
1580 /* Get info about element type, including its send proc */
1581 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 if (!OidIsValid(my_extra->typiofunc))
1586 ereport(ERROR,
1587 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1588 errmsg("no binary output function available for type %s",
1589 format_type_be(element_type))));
1590 fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1591 fcinfo->flinfo->fn_mcxt);
1592 my_extra->element_type = element_type;
1593 }
1594 typlen = my_extra->typlen;
1595 typbyval = my_extra->typbyval;
1596 typalign = my_extra->typalign;
1597
1598 ndim = AARR_NDIM(v);
1599 dim = AARR_DIMS(v);
1600 lb = AARR_LBOUND(v);
1601 nitems = ArrayGetNItems(ndim, dim);
1602
1604
1605 /* Send the array header information */
1606 pq_sendint32(&buf, ndim);
1607 pq_sendint32(&buf, AARR_HASNULL(v) ? 1 : 0);
1608 pq_sendint32(&buf, element_type);
1609 for (i = 0; i < ndim; i++)
1610 {
1611 pq_sendint32(&buf, dim[i]);
1612 pq_sendint32(&buf, lb[i]);
1613 }
1614
1615 /* Send the array elements using the element's own sendproc */
1616 array_iter_setup(&iter, v);
1617
1618 for (i = 0; i < nitems; i++)
1619 {
1620 Datum itemvalue;
1621 bool isnull;
1622
1623 /* Get source element, checking for NULL */
1624 itemvalue = array_iter_next(&iter, &isnull, i,
1625 typlen, typbyval, typalign);
1626
1627 if (isnull)
1628 {
1629 /* -1 length means a NULL */
1630 pq_sendint32(&buf, -1);
1631 }
1632 else
1633 {
1634 bytea *outputbytes;
1635
1636 outputbytes = SendFunctionCall(&my_extra->proc, itemvalue);
1637 pq_sendint32(&buf, VARSIZE(outputbytes) - VARHDRSZ);
1638 pq_sendbytes(&buf, VARDATA(outputbytes),
1639 VARSIZE(outputbytes) - VARHDRSZ);
1640 pfree(outputbytes);
1641 }
1642 }
1643
1645}
1646
1647/*
1648 * array_ndims :
1649 * returns the number of dimensions of the array pointed to by "v"
1650 */
1651Datum
1653{
1655
1656 /* Sanity check: does it look like an array at all? */
1657 if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
1659
1661}
1662
1663/*
1664 * array_dims :
1665 * returns the dimensions of the array pointed to by "v", as a "text"
1666 */
1667Datum
1669{
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 if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
1686
1687 dimv = AARR_DIMS(v);
1688 lb = AARR_LBOUND(v);
1689
1690 p = buf;
1691 for (i = 0; i < AARR_NDIM(v); i++)
1692 {
1693 sprintf(p, "[%d:%d]", lb[i], dimv[i] + lb[i] - 1);
1694 p += strlen(p);
1695 }
1696
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 */
1705Datum
1707{
1709 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 if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
1716
1717 /* Sanity check: was the requested dim valid */
1718 if (reqdim <= 0 || reqdim > AARR_NDIM(v))
1720
1721 lb = AARR_LBOUND(v);
1722 result = lb[reqdim - 1];
1723
1724 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 */
1732Datum
1734{
1736 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 if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
1744
1745 /* Sanity check: was the requested dim valid */
1746 if (reqdim <= 0 || reqdim > AARR_NDIM(v))
1748
1749 lb = AARR_LBOUND(v);
1750 dimv = AARR_DIMS(v);
1751
1752 result = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
1753
1754 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 */
1762Datum
1764{
1766 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 if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
1773
1774 /* Sanity check: was the requested dim valid */
1775 if (reqdim <= 0 || reqdim > AARR_NDIM(v))
1777
1778 dimv = AARR_DIMS(v);
1779
1780 result = dimv[reqdim - 1];
1781
1782 PG_RETURN_INT32(result);
1783}
1784
1785/*
1786 * array_cardinality:
1787 * returns the total number of elements in an array
1788 */
1789Datum
1791{
1793
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 */
1819Datum
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 if (arraytyplen > 0)
1841 {
1842 /*
1843 * fixed-length arrays -- these are assumed to be 1-d, 0-based
1844 */
1845 ndim = 1;
1846 fixedDim[0] = arraytyplen / elmlen;
1847 fixedLb[0] = 0;
1848 dim = fixedDim;
1849 lb = fixedLb;
1850 arraydataptr = (char *) DatumGetPointer(arraydatum);
1851 arraynullsptr = NULL;
1852 }
1853 else if (VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(arraydatum)))
1854 {
1855 /* expanded array: let's do this in a separate function */
1856 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 ArrayType *array = DatumGetArrayTypeP(arraydatum);
1869
1870 ndim = ARR_NDIM(array);
1871 dim = ARR_DIMS(array);
1872 lb = ARR_LBOUND(array);
1873 arraydataptr = ARR_DATA_PTR(array);
1874 arraynullsptr = ARR_NULLBITMAP(array);
1875 }
1876
1877 /*
1878 * Return NULL for invalid subscript
1879 */
1880 if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
1881 {
1882 *isNull = true;
1883 return (Datum) 0;
1884 }
1885 for (i = 0; i < ndim; i++)
1886 {
1887 if (indx[i] < lb[i] || indx[i] >= (dim[i] + lb[i]))
1888 {
1889 *isNull = true;
1890 return (Datum) 0;
1891 }
1892 }
1893
1894 /*
1895 * Calculate the element number
1896 */
1897 offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
1898
1899 /*
1900 * Check for NULL array element
1901 */
1902 if (array_get_isnull(arraynullsptr, offset))
1903 {
1904 *isNull = true;
1905 return (Datum) 0;
1906 }
1907
1908 /*
1909 * OK, get the element
1910 */
1911 *isNull = false;
1912 retptr = array_seek(arraydataptr, 0, arraynullsptr, offset,
1913 elmlen, elmbyval, elmalign);
1914 return ArrayCast(retptr, elmbyval, elmlen);
1915}
1916
1917/*
1918 * Implementation of array_get_element() for an expanded array
1919 */
1920static Datum
1922 int nSubscripts, int *indx,
1923 int arraytyplen,
1924 int elmlen, bool elmbyval, char elmalign,
1925 bool *isNull)
1926{
1928 int i,
1929 ndim,
1930 *dim,
1931 *lb,
1932 offset;
1933 Datum *dvalues;
1934 bool *dnulls;
1935
1936 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 ndim = eah->ndims;
1946 dim = eah->dims;
1947 lb = eah->lbound;
1948
1949 /*
1950 * Return NULL for invalid subscript
1951 */
1952 if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
1953 {
1954 *isNull = true;
1955 return (Datum) 0;
1956 }
1957 for (i = 0; i < ndim; i++)
1958 {
1959 if (indx[i] < lb[i] || indx[i] >= (dim[i] + lb[i]))
1960 {
1961 *isNull = true;
1962 return (Datum) 0;
1963 }
1964 }
1965
1966 /*
1967 * Calculate the element number
1968 */
1969 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 */
1976
1977 dvalues = eah->dvalues;
1978 dnulls = eah->dnulls;
1979
1980 /*
1981 * Check for NULL array element
1982 */
1983 if (dnulls && dnulls[offset])
1984 {
1985 *isNull = true;
1986 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 *isNull = false;
1996 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 */
2029Datum
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 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 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 array = DatumGetArrayTypeP(arraydatum);
2087
2088 ndim = ARR_NDIM(array);
2089 dim = ARR_DIMS(array);
2090 lb = ARR_LBOUND(array);
2091 elemtype = ARR_ELEMTYPE(array);
2092 arraydataptr = ARR_DATA_PTR(array);
2093 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 if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
2102 return PointerGetDatum(construct_empty_array(elemtype));
2103
2104 for (i = 0; i < nSubscripts; i++)
2105 {
2106 if (!lowerProvided[i] || lowerIndx[i] < lb[i])
2107 lowerIndx[i] = lb[i];
2108 if (!upperProvided[i] || upperIndx[i] >= (dim[i] + lb[i]))
2109 upperIndx[i] = dim[i] + lb[i] - 1;
2110 if (lowerIndx[i] > upperIndx[i])
2111 return PointerGetDatum(construct_empty_array(elemtype));
2112 }
2113 /* fill any missing subscript positions with full array range */
2114 for (; i < ndim; i++)
2115 {
2116 lowerIndx[i] = lb[i];
2117 upperIndx[i] = dim[i] + lb[i] - 1;
2118 if (lowerIndx[i] > upperIndx[i])
2119 return PointerGetDatum(construct_empty_array(elemtype));
2120 }
2121
2122 mda_get_range(ndim, span, lowerIndx, upperIndx);
2123
2124 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 if (arraynullsptr)
2134 {
2135 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, ArrayGetNItems(ndim, span));
2136 bytes += dataoffset;
2137 }
2138 else
2139 {
2140 dataoffset = 0; /* marker for no null bitmap */
2141 bytes += ARR_OVERHEAD_NONULLS(ndim);
2142 }
2143
2144 newarray = (ArrayType *) palloc0(bytes);
2145 SET_VARSIZE(newarray, bytes);
2146 newarray->ndim = ndim;
2147 newarray->dataoffset = dataoffset;
2148 newarray->elemtype = elemtype;
2149 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 newlb = ARR_LBOUND(newarray);
2156 for (i = 0; i < ndim; i++)
2157 newlb[i] = 1;
2158
2159 array_extract_slice(newarray,
2160 ndim, dim, lb,
2161 arraydataptr, arraynullsptr,
2162 lowerIndx, upperIndx,
2163 elmlen, elmbyval, elmalign);
2164
2165 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 */
2200Datum
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 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 if (nSubscripts != 1)
2243 ereport(ERROR,
2244 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2245 errmsg("wrong number of array subscripts")));
2246
2247 if (indx[0] < 0 || indx[0] >= arraytyplen / elmlen)
2248 ereport(ERROR,
2249 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2250 errmsg("array subscript out of range")));
2251
2252 if (isNull)
2253 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 resultarray = (char *) palloc(arraytyplen);
2258 memcpy(resultarray, DatumGetPointer(arraydatum), arraytyplen);
2259 elt_ptr = (char *) resultarray + indx[0] * elmlen;
2260 ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign, elt_ptr);
2261 return PointerGetDatum(resultarray);
2262 }
2263
2264 if (nSubscripts <= 0 || nSubscripts > MAXDIM)
2265 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 if (elmlen == -1 && !isNull)
2271 dataValue = PointerGetDatum(PG_DETOAST_DATUM(dataValue));
2272
2274 {
2275 /* expanded array: let's do this in a separate function */
2276 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 array = DatumGetArrayTypeP(arraydatum);
2289
2290 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 if (ndim == 0)
2298 {
2299 Oid elmtype = ARR_ELEMTYPE(array);
2300
2301 for (i = 0; i < nSubscripts; i++)
2302 {
2303 dim[i] = 1;
2304 lb[i] = indx[i];
2305 }
2306
2307 return PointerGetDatum(construct_md_array(&dataValue, &isNull,
2308 nSubscripts, dim, lb,
2309 elmtype,
2310 elmlen, elmbyval, elmalign));
2311 }
2312
2313 if (ndim != nSubscripts)
2314 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 memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
2320 memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
2321
2322 newhasnulls = (ARR_HASNULL(array) || isNull);
2323 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 if (ndim == 1)
2332 {
2333 if (indx[0] < lb[0])
2334 {
2335 /* addedbefore = lb[0] - indx[0]; */
2336 /* dim[0] += addedbefore; */
2337 if (pg_sub_s32_overflow(lb[0], indx[0], &addedbefore) ||
2338 pg_add_s32_overflow(dim[0], addedbefore, &dim[0]))
2339 ereport(ERROR,
2340 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2341 errmsg("array size exceeds the maximum allowed (%d)",
2342 (int) MaxArraySize)));
2343 lb[0] = indx[0];
2344 if (addedbefore > 1)
2345 newhasnulls = true; /* will insert nulls */
2346 }
2347 if (indx[0] >= (dim[0] + lb[0]))
2348 {
2349 /* addedafter = indx[0] - (dim[0] + lb[0]) + 1; */
2350 /* dim[0] += addedafter; */
2351 if (pg_sub_s32_overflow(indx[0], dim[0] + lb[0], &addedafter) ||
2352 pg_add_s32_overflow(addedafter, 1, &addedafter) ||
2353 pg_add_s32_overflow(dim[0], addedafter, &dim[0]))
2354 ereport(ERROR,
2355 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2356 errmsg("array size exceeds the maximum allowed (%d)",
2357 (int) MaxArraySize)));
2358 if (addedafter > 1)
2359 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 for (i = 0; i < ndim; i++)
2369 {
2370 if (indx[i] < lb[i] ||
2371 indx[i] >= (dim[i] + lb[i]))
2372 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 newnitems = ArrayGetNItems(ndim, dim);
2380 ArrayCheckBounds(ndim, dim, lb);
2381
2382 /*
2383 * Compute sizes of items and areas to copy
2384 */
2385 if (newhasnulls)
2386 overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, newnitems);
2387 else
2388 overheadlen = ARR_OVERHEAD_NONULLS(ndim);
2389 oldnitems = ArrayGetNItems(ndim, ARR_DIMS(array));
2390 oldnullbitmap = ARR_NULLBITMAP(array);
2391 oldoverheadlen = ARR_DATA_OFFSET(array);
2392 olddatasize = ARR_SIZE(array) - oldoverheadlen;
2393 if (addedbefore)
2394 {
2395 offset = 0;
2396 lenbefore = 0;
2397 olditemlen = 0;
2398 lenafter = olddatasize;
2399 }
2400 else if (addedafter)
2401 {
2402 offset = oldnitems;
2403 lenbefore = olddatasize;
2404 olditemlen = 0;
2405 lenafter = 0;
2406 }
2407 else
2408 {
2409 offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
2410 elt_ptr = array_seek(ARR_DATA_PTR(array), 0, oldnullbitmap, offset,
2411 elmlen, elmbyval, elmalign);
2412 lenbefore = (int) (elt_ptr - ARR_DATA_PTR(array));
2413 if (array_get_isnull(oldnullbitmap, offset))
2414 olditemlen = 0;
2415 else
2416 {
2417 olditemlen = att_addlength_pointer(0, elmlen, elt_ptr);
2418 olditemlen = att_align_nominal(olditemlen, elmalign);
2419 }
2420 lenafter = (int) (olddatasize - lenbefore - olditemlen);
2421 }
2422
2423 if (isNull)
2424 newitemlen = 0;
2425 else
2426 {
2427 newitemlen = att_addlength_datum(0, elmlen, dataValue);
2428 newitemlen = att_align_nominal(newitemlen, elmalign);
2429 }
2430
2431 newsize = overheadlen + lenbefore + newitemlen + lenafter;
2432
2433 /*
2434 * OK, create the new array and fill in header/dimensions
2435 */
2436 newarray = (ArrayType *) palloc0(newsize);
2437 SET_VARSIZE(newarray, newsize);
2438 newarray->ndim = ndim;
2439 newarray->dataoffset = newhasnulls ? overheadlen : 0;
2440 newarray->elemtype = ARR_ELEMTYPE(array);
2441 memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
2442 memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
2443
2444 /*
2445 * Fill in data
2446 */
2447 memcpy((char *) newarray + overheadlen,
2448 (char *) array + oldoverheadlen,
2449 lenbefore);
2450 if (!isNull)
2451 ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign,
2452 (char *) newarray + overheadlen + lenbefore);
2453 memcpy((char *) newarray + overheadlen + lenbefore + newitemlen,
2454 (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 if (newhasnulls)
2464 {
2465 bits8 *newnullbitmap = ARR_NULLBITMAP(newarray);
2466
2467 /* palloc0 above already marked any inserted positions as nulls */
2468 /* Fix the inserted value */
2469 if (addedafter)
2470 array_set_isnull(newnullbitmap, newnitems - 1, isNull);
2471 else
2472 array_set_isnull(newnullbitmap, offset, isNull);
2473 /* Fix the copied range(s) */
2474 if (addedbefore)
2475 array_bitmap_copy(newnullbitmap, addedbefore,
2476 oldnullbitmap, 0,
2477 oldnitems);
2478 else
2479 {
2480 array_bitmap_copy(newnullbitmap, 0,
2481 oldnullbitmap, 0,
2482 offset);
2483 if (addedafter == 0)
2484 array_bitmap_copy(newnullbitmap, offset + 1,
2485 oldnullbitmap, offset + 1,
2486 oldnitems - offset - 1);
2487 }
2488 }
2489
2490 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 */
2500static Datum
2502 int nSubscripts, int *indx,
2503 Datum dataValue, bool isNull,
2504 int arraytyplen,
2505 int elmlen, bool elmbyval, char elmalign)
2506{
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 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 ndim = eah->ndims;
2536 Assert(ndim >= 0 && ndim <= MAXDIM);
2537 memcpy(dim, eah->dims, ndim * sizeof(int));
2538 memcpy(lb, eah->lbound, ndim * sizeof(int));
2539 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 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 eah->dims = (int *) MemoryContextAllocZero(eah->hdr.eoh_context,
2554 nSubscripts * sizeof(int));
2555 eah->lbound = (int *) MemoryContextAllocZero(eah->hdr.eoh_context,
2556 nSubscripts * sizeof(int));
2557
2558 /* Update local copies of dimension info */
2559 ndim = nSubscripts;
2560 for (i = 0; i < nSubscripts; i++)
2561 {
2562 dim[i] = 0;
2563 lb[i] = indx[i];
2564 }
2565 dimschanged = true;
2566 }
2567 else if (ndim != nSubscripts)
2568 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 */
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 if (!eah->typbyval && !isNull)
2589 {
2591
2592 dataValue = datumCopy(dataValue, false, eah->typlen);
2593 MemoryContextSwitchTo(oldcxt);
2594 }
2595
2596 dvalues = eah->dvalues;
2597 dnulls = eah->dnulls;
2598
2599 newhasnulls = ((dnulls != NULL) || isNull);
2600 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 if (ndim == 1)
2609 {
2610 if (indx[0] < lb[0])
2611 {
2612 /* addedbefore = lb[0] - indx[0]; */
2613 /* dim[0] += addedbefore; */
2614 if (pg_sub_s32_overflow(lb[0], indx[0], &addedbefore) ||
2615 pg_add_s32_overflow(dim[0], addedbefore, &dim[0]))
2616 ereport(ERROR,
2617 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2618 errmsg("array size exceeds the maximum allowed (%d)",
2619 (int) MaxArraySize)));
2620 lb[0] = indx[0];
2621 dimschanged = true;
2622 if (addedbefore > 1)
2623 newhasnulls = true; /* will insert nulls */
2624 }
2625 if (indx[0] >= (dim[0] + lb[0]))
2626 {
2627 /* addedafter = indx[0] - (dim[0] + lb[0]) + 1; */
2628 /* dim[0] += addedafter; */
2629 if (pg_sub_s32_overflow(indx[0], dim[0] + lb[0], &addedafter) ||
2630 pg_add_s32_overflow(addedafter, 1, &addedafter) ||
2631 pg_add_s32_overflow(dim[0], addedafter, &dim[0]))
2632 ereport(ERROR,
2633 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2634 errmsg("array size exceeds the maximum allowed (%d)",
2635 (int) MaxArraySize)));
2636 dimschanged = true;
2637 if (addedafter > 1)
2638 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 for (i = 0; i < ndim; i++)
2648 {
2649 if (indx[i] < lb[i] ||
2650 indx[i] >= (dim[i] + lb[i]))
2651 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 if (dimschanged)
2659 {
2660 (void) ArrayGetNItems(ndim, dim);
2661 ArrayCheckBounds(ndim, dim, lb);
2662 }
2663
2664 /* Now we can calculate linear offset of target item in array */
2665 offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
2666
2667 /* Physically enlarge existing dvalues/dnulls arrays if needed */
2668 if (dim[0] > eah->dvalueslen)
2669 {
2670 /* We want some extra space if we're enlarging */
2671 int newlen = dim[0] + dim[0] / 8;
2672
2673 newlen = Max(newlen, dim[0]); /* integer overflow guard */
2674 eah->dvalues = dvalues = (Datum *)
2675 repalloc(dvalues, newlen * sizeof(Datum));
2676 if (dnulls)
2677 eah->dnulls = dnulls = (bool *)
2678 repalloc(dnulls, newlen * sizeof(bool));
2679 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 if (newhasnulls && dnulls == NULL)
2687 eah->dnulls = dnulls = (bool *)
2689 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 eah->fvalue = NULL;
2698 /* And we don't know the flattened size either */
2699 eah->flat_size = 0;
2700
2701 /* Update dimensionality info if needed */
2702 if (dimschanged)
2703 {
2704 eah->ndims = ndim;
2705 memcpy(eah->dims, dim, ndim * sizeof(int));
2706 memcpy(eah->lbound, lb, ndim * sizeof(int));
2707 }
2708
2709 /* Reposition items if needed, and fill addedbefore items with nulls */
2710 if (addedbefore > 0)
2711 {
2712 memmove(dvalues + addedbefore, dvalues, eah->nelems * sizeof(Datum));
2713 for (i = 0; i < addedbefore; i++)
2714 dvalues[i] = (Datum) 0;
2715 if (dnulls)
2716 {
2717 memmove(dnulls + addedbefore, dnulls, eah->nelems * sizeof(bool));
2718 for (i = 0; i < addedbefore; i++)
2719 dnulls[i] = true;
2720 }
2721 eah->nelems += addedbefore;
2722 }
2723
2724 /* fill addedafter items with nulls */
2725 if (addedafter > 0)
2726 {
2727 for (i = 0; i < addedafter; i++)
2728 dvalues[eah->nelems + i] = (Datum) 0;
2729 if (dnulls)
2730 {
2731 for (i = 0; i < addedafter; i++)
2732 dnulls[eah->nelems + i] = true;
2733 }
2734 eah->nelems += addedafter;
2735 }
2736
2737 /* Grab old element value for pfree'ing, if needed. */
2738 if (!eah->typbyval && (dnulls == NULL || !dnulls[offset]))
2739 oldValue = (char *) DatumGetPointer(dvalues[offset]);
2740 else
2741 oldValue = NULL;
2742
2743 /* And finally we can insert the new element. */
2744 dvalues[offset] = dataValue;
2745 if (dnulls)
2746 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 if (oldValue)
2754 {
2755 /* Don't try to pfree a part of the original flat array */
2756 if (oldValue < eah->fstartptr || oldValue >= eah->fendptr)
2757 pfree(oldValue);
2758 }
2759
2760 /* Done, return standard TOAST pointer for object */
2761 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 */
2805Datum
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 if (isNull)
2846 return arraydatum;
2847
2848 if (arraytyplen > 0)
2849 {
2850 /*
2851 * fixed-length arrays -- not got round to doing this...
2852 */
2853 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 array = DatumGetArrayTypeP(arraydatum);
2860 srcArray = DatumGetArrayTypeP(srcArrayDatum);
2861
2862 /* note: we assume srcArray contains no toasted elements */
2863
2864 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 if (ndim == 0)
2872 {
2873 Datum *dvalues;
2874 bool *dnulls;
2875 int nelems;
2876 Oid elmtype = ARR_ELEMTYPE(array);
2877
2878 deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
2879 &dvalues, &dnulls, &nelems);
2880
2881 for (i = 0; i < nSubscripts; i++)
2882 {
2883 if (!upperProvided[i] || !lowerProvided[i])
2884 ereport(ERROR,
2885 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2886 errmsg("array slice subscript must provide both boundaries"),
2887 errdetail("When assigning to a slice of an empty array value,"
2888 " slice boundaries must be fully specified.")));
2889
2890 /* compute "upperIndx[i] - lowerIndx[i] + 1", detecting overflow */
2891 if (pg_sub_s32_overflow(upperIndx[i], lowerIndx[i], &dim[i]) ||
2892 pg_add_s32_overflow(dim[i], 1, &dim[i]))
2893 ereport(ERROR,
2894 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2895 errmsg("array size exceeds the maximum allowed (%d)",
2896 (int) MaxArraySize)));
2897
2898 lb[i] = lowerIndx[i];
2899 }
2900
2901 /* complain if too few source items; we ignore extras, however */
2902 if (nelems < ArrayGetNItems(nSubscripts, dim))
2903 ereport(ERROR,
2904 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2905 errmsg("source array too small")));
2906
2907 return PointerGetDatum(construct_md_array(dvalues, dnulls, nSubscripts,
2908 dim, lb, elmtype,
2909 elmlen, elmbyval, elmalign));
2910 }
2911
2912 if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
2913 ereport(ERROR,
2914 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2915 errmsg("wrong number of array subscripts")));
2916
2917 /* copy dim/lb since we may modify them */
2918 memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
2919 memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
2920
2921 newhasnulls = (ARR_HASNULL(array) || ARR_HASNULL(srcArray));
2922 addedbefore = addedafter = 0;
2923
2924 /*
2925 * Check subscripts. We assume the existing subscripts passed
2926 * ArrayCheckBounds, so that dim[i] + lb[i] can be computed without
2927 * overflow. But we must beware of other overflows in our calculations of
2928 * new dim[] values.
2929 */
2930 if (ndim == 1)
2931 {
2932 Assert(nSubscripts == 1);
2933 if (!lowerProvided[0])
2934 lowerIndx[0] = lb[0];
2935 if (!upperProvided[0])
2936 upperIndx[0] = dim[0] + lb[0] - 1;
2937 if (lowerIndx[0] > upperIndx[0])
2938 ereport(ERROR,
2939 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2940 errmsg("upper bound cannot be less than lower bound")));
2941 if (lowerIndx[0] < lb[0])
2942 {
2943 /* addedbefore = lb[0] - lowerIndx[0]; */
2944 /* dim[0] += addedbefore; */
2945 if (pg_sub_s32_overflow(lb[0], lowerIndx[0], &addedbefore) ||
2946 pg_add_s32_overflow(dim[0], addedbefore, &dim[0]))
2947 ereport(ERROR,
2948 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2949 errmsg("array size exceeds the maximum allowed (%d)",
2950 (int) MaxArraySize)));
2951 lb[0] = lowerIndx[0];
2952 if (addedbefore > 1)
2953 newhasnulls = true; /* will insert nulls */
2954 }
2955 if (upperIndx[0] >= (dim[0] + lb[0]))
2956 {
2957 /* addedafter = upperIndx[0] - (dim[0] + lb[0]) + 1; */
2958 /* dim[0] += addedafter; */
2959 if (pg_sub_s32_overflow(upperIndx[0], dim[0] + lb[0], &addedafter) ||
2960 pg_add_s32_overflow(addedafter, 1, &addedafter) ||
2961 pg_add_s32_overflow(dim[0], addedafter, &dim[0]))
2962 ereport(ERROR,
2963 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2964 errmsg("array size exceeds the maximum allowed (%d)",
2965 (int) MaxArraySize)));
2966 if (addedafter > 1)
2967 newhasnulls = true; /* will insert nulls */
2968 }
2969 }
2970 else
2971 {
2972 /*
2973 * XXX currently we do not support extending multi-dimensional arrays
2974 * during assignment
2975 */
2976 for (i = 0; i < nSubscripts; i++)
2977 {
2978 if (!lowerProvided[i])
2979 lowerIndx[i] = lb[i];
2980 if (!upperProvided[i])
2981 upperIndx[i] = dim[i] + lb[i] - 1;
2982 if (lowerIndx[i] > upperIndx[i])
2983 ereport(ERROR,
2984 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2985 errmsg("upper bound cannot be less than lower bound")));
2986 if (lowerIndx[i] < lb[i] ||
2987 upperIndx[i] >= (dim[i] + lb[i]))
2988 ereport(ERROR,
2989 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2990 errmsg("array subscript out of range")));
2991 }
2992 /* fill any missing subscript positions with full array range */
2993 for (; i < ndim; i++)
2994 {
2995 lowerIndx[i] = lb[i];
2996 upperIndx[i] = dim[i] + lb[i] - 1;
2997 if (lowerIndx[i] > upperIndx[i])
2998 ereport(ERROR,
2999 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
3000 errmsg("upper bound cannot be less than lower bound")));
3001 }
3002 }
3003
3004 /* Do this mainly to check for overflow */
3005 nitems = ArrayGetNItems(ndim, dim);
3006 ArrayCheckBounds(ndim, dim, lb);
3007
3008 /*
3009 * Make sure source array has enough entries. Note we ignore the shape of
3010 * the source array and just read entries serially.
3011 */
3012 mda_get_range(ndim, span, lowerIndx, upperIndx);
3013 nsrcitems = ArrayGetNItems(ndim, span);
3014 if (nsrcitems > ArrayGetNItems(ARR_NDIM(srcArray), ARR_DIMS(srcArray)))
3015 ereport(ERROR,
3016 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
3017 errmsg("source array too small")));
3018
3019 /*
3020 * Compute space occupied by new entries, space occupied by replaced
3021 * entries, and required space for new array.
3022 */
3023 if (newhasnulls)
3024 overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
3025 else
3026 overheadlen = ARR_OVERHEAD_NONULLS(ndim);
3027 newitemsize = array_nelems_size(ARR_DATA_PTR(srcArray), 0,
3028 ARR_NULLBITMAP(srcArray), nsrcitems,
3029 elmlen, elmbyval, elmalign);
3030 oldoverheadlen = ARR_DATA_OFFSET(array);
3031 olddatasize = ARR_SIZE(array) - oldoverheadlen;
3032 if (ndim > 1)
3033 {
3034 /*
3035 * here we do not need to cope with extension of the array; it would
3036 * be a lot more complicated if we had to do so...
3037 */
3038 olditemsize = array_slice_size(ARR_DATA_PTR(array),
3039 ARR_NULLBITMAP(array),
3040 ndim, dim, lb,
3041 lowerIndx, upperIndx,
3042 elmlen, elmbyval, elmalign);
3043 lenbefore = lenafter = 0; /* keep compiler quiet */
3044 itemsbefore = itemsafter = nolditems = 0;
3045 }
3046 else
3047 {
3048 /*
3049 * here we must allow for possibility of slice larger than orig array
3050 * and/or not adjacent to orig array subscripts
3051 */
3052 int oldlb = ARR_LBOUND(array)[0];
3053 int oldub = oldlb + ARR_DIMS(array)[0] - 1;
3054 int slicelb = Max(oldlb, lowerIndx[0]);
3055 int sliceub = Min(oldub, upperIndx[0]);
3056 char *oldarraydata = ARR_DATA_PTR(array);
3057 bits8 *oldarraybitmap = ARR_NULLBITMAP(array);
3058
3059 /* count/size of old array entries that will go before the slice */
3060 itemsbefore = Min(slicelb, oldub + 1) - oldlb;
3061 lenbefore = array_nelems_size(oldarraydata, 0, oldarraybitmap,
3062 itemsbefore,
3063 elmlen, elmbyval, elmalign);
3064 /* count/size of old array entries that will be replaced by slice */
3065 if (slicelb > sliceub)
3066 {
3067 nolditems = 0;
3068 olditemsize = 0;
3069 }
3070 else
3071 {
3072 nolditems = sliceub - slicelb + 1;
3073 olditemsize = array_nelems_size(oldarraydata + lenbefore,
3074 itemsbefore, oldarraybitmap,
3075 nolditems,
3076 elmlen, elmbyval, elmalign);
3077 }
3078 /* count/size of old array entries that will go after the slice */
3079 itemsafter = oldub + 1 - Max(sliceub + 1, oldlb);
3080 lenafter = olddatasize - lenbefore - olditemsize;
3081 }
3082
3083 newsize = overheadlen + olddatasize - olditemsize + newitemsize;
3084
3085 newarray = (ArrayType *) palloc0(newsize);
3086 SET_VARSIZE(newarray, newsize);
3087 newarray->ndim = ndim;
3088 newarray->dataoffset = newhasnulls ? overheadlen : 0;
3089 newarray->elemtype = ARR_ELEMTYPE(array);
3090 memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
3091 memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
3092
3093 if (ndim > 1)
3094 {
3095 /*
3096 * here we do not need to cope with extension of the array; it would
3097 * be a lot more complicated if we had to do so...
3098 */
3099 array_insert_slice(newarray, array, srcArray,
3100 ndim, dim, lb,
3101 lowerIndx, upperIndx,
3102 elmlen, elmbyval, elmalign);
3103 }
3104 else
3105 {
3106 /* fill in data */
3107 memcpy((char *) newarray + overheadlen,
3108 (char *) array + oldoverheadlen,
3109 lenbefore);
3110 memcpy((char *) newarray + overheadlen + lenbefore,
3111 ARR_DATA_PTR(srcArray),
3112 newitemsize);
3113 memcpy((char *) newarray + overheadlen + lenbefore + newitemsize,
3114 (char *) array + oldoverheadlen + lenbefore + olditemsize,
3115 lenafter);
3116 /* fill in nulls bitmap if needed */
3117 if (newhasnulls)
3118 {
3119 bits8 *newnullbitmap = ARR_NULLBITMAP(newarray);
3120 bits8 *oldnullbitmap = ARR_NULLBITMAP(array);
3121
3122 /* palloc0 above already marked any inserted positions as nulls */
3123 array_bitmap_copy(newnullbitmap, addedbefore,
3124 oldnullbitmap, 0,
3125 itemsbefore);
3126 array_bitmap_copy(newnullbitmap, lowerIndx[0] - lb[0],
3127 ARR_NULLBITMAP(srcArray), 0,
3128 nsrcitems);
3129 array_bitmap_copy(newnullbitmap, addedbefore + itemsbefore + nolditems,
3130 oldnullbitmap, itemsbefore + nolditems,
3131 itemsafter);
3132 }
3133 }
3134
3135 return PointerGetDatum(newarray);
3136}
3137
3138/*
3139 * array_ref : backwards compatibility wrapper for array_get_element
3140 *
3141 * This only works for detoasted/flattened varlena arrays, since the array
3142 * argument is declared as "ArrayType *". However there's enough code like
3143 * that to justify preserving this API.
3144 */
3145Datum
3146array_ref(ArrayType *array, int nSubscripts, int *indx,
3147 int arraytyplen, int elmlen, bool elmbyval, char elmalign,
3148 bool *isNull)
3149{
3150 return array_get_element(PointerGetDatum(array), nSubscripts, indx,
3151 arraytyplen, elmlen, elmbyval, elmalign,
3152 isNull);
3153}
3154
3155/*
3156 * array_set : backwards compatibility wrapper for array_set_element
3157 *
3158 * This only works for detoasted/flattened varlena arrays, since the array
3159 * argument and result are declared as "ArrayType *". However there's enough
3160 * code like that to justify preserving this API.
3161 */
3162ArrayType *
3163array_set(ArrayType *array, int nSubscripts, int *indx,
3164 Datum dataValue, bool isNull,
3165 int arraytyplen, int elmlen, bool elmbyval, char elmalign)
3166{
3168 nSubscripts, indx,
3169 dataValue, isNull,
3170 arraytyplen,
3171 elmlen, elmbyval, elmalign));
3172}
3173
3174/*
3175 * array_map()
3176 *
3177 * Map an array through an arbitrary expression. Return a new array with
3178 * the same dimensions and each source element transformed by the given,
3179 * already-compiled expression. Each source element is placed in the
3180 * innermost_caseval/innermost_casenull fields of the ExprState.
3181 *
3182 * Parameters are:
3183 * * arrayd: Datum representing array argument.
3184 * * exprstate: ExprState representing the per-element transformation.
3185 * * econtext: context for expression evaluation.
3186 * * retType: OID of element type of output array. This must be the same as,
3187 * or binary-compatible with, the result type of the expression. It might
3188 * be different from the input array's element type.
3189 * * amstate: workspace for array_map. Must be zeroed by caller before
3190 * first call, and not touched after that.
3191 *
3192 * It is legitimate to pass a freshly-zeroed ArrayMapState on each call,
3193 * but better performance can be had if the state can be preserved across
3194 * a series of calls.
3195 *
3196 * NB: caller must assure that input array is not NULL. NULL elements in
3197 * the array are OK however.
3198 * NB: caller should be running in econtext's per-tuple memory context.
3199 */
3200Datum
3202 ExprState *exprstate, ExprContext *econtext,
3203 Oid retType, ArrayMapState *amstate)
3204{
3205 AnyArrayType *v = DatumGetAnyArrayP(arrayd);
3206 ArrayType *result;
3207 Datum *values;
3208 bool *nulls;
3209 int *dim;
3210 int ndim;
3211 int nitems;
3212 int i;
3213 int32 nbytes = 0;
3214 int32 dataoffset;
3215 bool hasnulls;
3216 Oid inpType;
3217 int inp_typlen;
3218 bool inp_typbyval;
3219 char inp_typalign;
3220 int typlen;
3221 bool typbyval;
3222 char typalign;
3223 array_iter iter;
3224 ArrayMetaState *inp_extra;
3225 ArrayMetaState *ret_extra;
3226 Datum *transform_source = exprstate->innermost_caseval;
3227 bool *transform_source_isnull = exprstate->innermost_casenull;
3228
3229 inpType = AARR_ELEMTYPE(v);
3230 ndim = AARR_NDIM(v);
3231 dim = AARR_DIMS(v);
3232 nitems = ArrayGetNItems(ndim, dim);
3233
3234 /* Check for empty array */
3235 if (nitems <= 0)
3236 {
3237 /* Return empty array */
3238 return PointerGetDatum(construct_empty_array(retType));
3239 }
3240
3241 /*
3242 * We arrange to look up info about input and return element types only
3243 * once per series of calls, assuming the element type doesn't change
3244 * underneath us.
3245 */
3246 inp_extra = &amstate->inp_extra;
3247 ret_extra = &amstate->ret_extra;
3248
3249 if (inp_extra->element_type != inpType)
3250 {
3251 get_typlenbyvalalign(inpType,
3252 &inp_extra->typlen,
3253 &inp_extra->typbyval,
3254 &inp_extra->typalign);
3255 inp_extra->element_type = inpType;
3256 }
3257 inp_typlen = inp_extra->typlen;
3258 inp_typbyval = inp_extra->typbyval;
3259 inp_typalign = inp_extra->typalign;
3260
3261 if (ret_extra->element_type != retType)
3262 {
3263 get_typlenbyvalalign(retType,
3264 &ret_extra->typlen,
3265 &ret_extra->typbyval,
3266 &ret_extra->typalign);
3267 ret_extra->element_type = retType;
3268 }
3269 typlen = ret_extra->typlen;
3270 typbyval = ret_extra->typbyval;
3271 typalign = ret_extra->typalign;
3272
3273 /* Allocate temporary arrays for new values */
3274 values = (Datum *) palloc(nitems * sizeof(Datum));
3275 nulls = (bool *) palloc(nitems * sizeof(bool));
3276
3277 /* Loop over source data */
3278 array_iter_setup(&iter, v);
3279 hasnulls = false;
3280
3281 for (i = 0; i < nitems; i++)
3282 {
3283 /* Get source element, checking for NULL */
3284 *transform_source =
3285 array_iter_next(&iter, transform_source_isnull, i,
3286 inp_typlen, inp_typbyval, inp_typalign);
3287
3288 /* Apply the given expression to source element */
3289 values[i] = ExecEvalExpr(exprstate, econtext, &nulls[i]);
3290
3291 if (nulls[i])
3292 hasnulls = true;
3293 else
3294 {
3295 /* Ensure data is not toasted */
3296 if (typlen == -1)
3298 /* Update total result size */
3299 nbytes = att_addlength_datum(nbytes, typlen, values[i]);
3300 nbytes = att_align_nominal(nbytes, typalign);
3301 /* check for overflow of total request */
3302 if (!AllocSizeIsValid(nbytes))
3303 ereport(ERROR,
3304 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3305 errmsg("array size exceeds the maximum allowed (%d)",
3306 (int) MaxAllocSize)));
3307 }
3308 }
3309
3310 /* Allocate and fill the result array */
3311 if (hasnulls)
3312 {
3313 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
3314 nbytes += dataoffset;
3315 }
3316 else
3317 {
3318 dataoffset = 0; /* marker for no null bitmap */
3319 nbytes += ARR_OVERHEAD_NONULLS(ndim);
3320 }
3321 result = (ArrayType *) palloc0(nbytes);
3322 SET_VARSIZE(result, nbytes);
3323 result->ndim = ndim;
3324 result->dataoffset = dataoffset;
3325 result->elemtype = retType;
3326 memcpy(ARR_DIMS(result), AARR_DIMS(v), ndim * sizeof(int));
3327 memcpy(ARR_LBOUND(result), AARR_LBOUND(v), ndim * sizeof(int));
3328
3329 CopyArrayEls(result,
3330 values, nulls, nitems,
3331 typlen, typbyval, typalign,
3332 false);
3333
3334 /*
3335 * Note: do not risk trying to pfree the results of the called expression
3336 */
3337 pfree(values);
3338 pfree(nulls);
3339
3340 return PointerGetDatum(result);
3341}
3342
3343/*
3344 * construct_array --- simple method for constructing an array object
3345 *
3346 * elems: array of Datum items to become the array contents
3347 * (NULL element values are not supported).
3348 * nelems: number of items
3349 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
3350 *
3351 * A palloc'd 1-D array object is constructed and returned. Note that
3352 * elem values will be copied into the object even if pass-by-ref type.
3353 * Also note the result will be 0-D not 1-D if nelems = 0.
3354 *
3355 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
3356 * from the system catalogs, given the elmtype. However, the caller is
3357 * in a better position to cache this info across multiple uses, or even
3358 * to hard-wire values if the element type is hard-wired.
3359 */
3360ArrayType *
3361construct_array(Datum *elems, int nelems,
3362 Oid elmtype,
3363 int elmlen, bool elmbyval, char elmalign)
3364{
3365 int dims[1];
3366 int lbs[1];
3367
3368 dims[0] = nelems;
3369 lbs[0] = 1;
3370
3371 return construct_md_array(elems, NULL, 1, dims, lbs,
3372 elmtype, elmlen, elmbyval, elmalign);
3373}
3374
3375/*
3376 * Like construct_array(), where elmtype must be a built-in type, and
3377 * elmlen/elmbyval/elmalign is looked up from hardcoded data. This is often
3378 * useful when manipulating arrays from/for system catalogs.
3379 */
3380ArrayType *
3381construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
3382{
3383 int elmlen;
3384 bool elmbyval;
3385 char elmalign;
3386
3387 switch (elmtype)
3388 {
3389 case CHAROID:
3390 elmlen = 1;
3391 elmbyval = true;
3392 elmalign = TYPALIGN_CHAR;
3393 break;
3394
3395 case CSTRINGOID:
3396 elmlen = -2;
3397 elmbyval = false;
3398 elmalign = TYPALIGN_CHAR;
3399 break;
3400
3401 case FLOAT4OID:
3402 elmlen = sizeof(float4);
3403 elmbyval = true;
3404 elmalign = TYPALIGN_INT;
3405 break;
3406
3407 case FLOAT8OID:
3408 elmlen = sizeof(float8);
3409 elmbyval = FLOAT8PASSBYVAL;
3410 elmalign = TYPALIGN_DOUBLE;
3411 break;
3412
3413 case INT2OID:
3414 elmlen = sizeof(int16);
3415 elmbyval = true;
3416 elmalign = TYPALIGN_SHORT;
3417 break;
3418
3419 case INT4OID:
3420 elmlen = sizeof(int32);
3421 elmbyval = true;
3422 elmalign = TYPALIGN_INT;
3423 break;
3424
3425 case INT8OID:
3426 elmlen = sizeof(int64);
3427 elmbyval = FLOAT8PASSBYVAL;
3428 elmalign = TYPALIGN_DOUBLE;
3429 break;
3430
3431 case NAMEOID:
3432 elmlen = NAMEDATALEN;
3433 elmbyval = false;
3434 elmalign = TYPALIGN_CHAR;
3435 break;
3436
3437 case OIDOID:
3438 case REGTYPEOID:
3439 elmlen = sizeof(Oid);
3440 elmbyval = true;
3441 elmalign = TYPALIGN_INT;
3442 break;
3443
3444 case TEXTOID:
3445 elmlen = -1;
3446 elmbyval = false;
3447 elmalign = TYPALIGN_INT;
3448 break;
3449
3450 case TIDOID:
3451 elmlen = sizeof(ItemPointerData);
3452 elmbyval = false;
3453 elmalign = TYPALIGN_SHORT;
3454 break;
3455
3456 case XIDOID:
3457 elmlen = sizeof(TransactionId);
3458 elmbyval = true;
3459 elmalign = TYPALIGN_INT;
3460 break;
3461
3462 default:
3463 elog(ERROR, "type %u not supported by construct_array_builtin()", elmtype);
3464 /* keep compiler quiet */
3465 elmlen = 0;
3466 elmbyval = false;
3467 elmalign = 0;
3468 }
3469
3470 return construct_array(elems, nelems, elmtype, elmlen, elmbyval, elmalign);
3471}
3472
3473/*
3474 * construct_md_array --- simple method for constructing an array object
3475 * with arbitrary dimensions and possible NULLs
3476 *
3477 * elems: array of Datum items to become the array contents
3478 * nulls: array of is-null flags (can be NULL if no nulls)
3479 * ndims: number of dimensions
3480 * dims: integer array with size of each dimension
3481 * lbs: integer array with lower bound of each dimension
3482 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
3483 *
3484 * A palloc'd ndims-D array object is constructed and returned. Note that
3485 * elem values will be copied into the object even if pass-by-ref type.
3486 * Also note the result will be 0-D not ndims-D if any dims[i] = 0.
3487 *
3488 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
3489 * from the system catalogs, given the elmtype. However, the caller is
3490 * in a better position to cache this info across multiple uses, or even
3491 * to hard-wire values if the element type is hard-wired.
3492 */
3493ArrayType *
3495 bool *nulls,
3496 int ndims,
3497 int *dims,
3498 int *lbs,
3499 Oid elmtype, int elmlen, bool elmbyval, char elmalign)
3500{
3501 ArrayType *result;
3502 bool hasnulls;
3503 int32 nbytes;
3504 int32 dataoffset;
3505 int i;
3506 int nelems;
3507
3508 if (ndims < 0) /* we do allow zero-dimension arrays */
3509 ereport(ERROR,
3510 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3511 errmsg("invalid number of dimensions: %d", ndims)));
3512 if (ndims > MAXDIM)
3513 ereport(ERROR,
3514 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3515 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
3516 ndims, MAXDIM)));
3517
3518 /* This checks for overflow of the array dimensions */
3519 nelems = ArrayGetNItems(ndims, dims);
3520 ArrayCheckBounds(ndims, dims, lbs);
3521
3522 /* if ndims <= 0 or any dims[i] == 0, return empty array */
3523 if (nelems <= 0)
3524 return construct_empty_array(elmtype);
3525
3526 /* compute required space */
3527 nbytes = 0;
3528 hasnulls = false;
3529 for (i = 0; i < nelems; i++)
3530 {
3531 if (nulls && nulls[i])
3532 {
3533 hasnulls = true;
3534 continue;
3535 }
3536 /* make sure data is not toasted */
3537 if (elmlen == -1)
3538 elems[i] = PointerGetDatum(PG_DETOAST_DATUM(elems[i]));
3539 nbytes = att_addlength_datum(nbytes, elmlen, elems[i]);
3540 nbytes = att_align_nominal(nbytes, elmalign);
3541 /* check for overflow of total request */
3542 if (!AllocSizeIsValid(nbytes))
3543 ereport(ERROR,
3544 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3545 errmsg("array size exceeds the maximum allowed (%d)",
3546 (int) MaxAllocSize)));
3547 }
3548
3549 /* Allocate and initialize result array */
3550 if (hasnulls)
3551 {
3552 dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nelems);
3553 nbytes += dataoffset;
3554 }
3555 else
3556 {
3557 dataoffset = 0; /* marker for no null bitmap */
3558 nbytes += ARR_OVERHEAD_NONULLS(ndims);
3559 }
3560 result = (ArrayType *) palloc0(nbytes);
3561 SET_VARSIZE(result, nbytes);
3562 result->ndim = ndims;
3563 result->dataoffset = dataoffset;
3564 result->elemtype = elmtype;
3565 memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
3566 memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
3567
3568 CopyArrayEls(result,
3569 elems, nulls, nelems,
3570 elmlen, elmbyval, elmalign,
3571 false);
3572
3573 return result;
3574}
3575
3576/*
3577 * construct_empty_array --- make a zero-dimensional array of given type
3578 */
3579ArrayType *
3581{
3582 ArrayType *result;
3583
3584 result = (ArrayType *) palloc0(sizeof(ArrayType));
3585 SET_VARSIZE(result, sizeof(ArrayType));
3586 result->ndim = 0;
3587 result->dataoffset = 0;
3588 result->elemtype = elmtype;
3589 return result;
3590}
3591
3592/*
3593 * construct_empty_expanded_array: make an empty expanded array
3594 * given only type information. (metacache can be NULL if not needed.)
3595 */
3598 MemoryContext parentcontext,
3599 ArrayMetaState *metacache)
3600{
3601 ArrayType *array = construct_empty_array(element_type);
3602 Datum d;
3603
3604 d = expand_array(PointerGetDatum(array), parentcontext, metacache);
3605 pfree(array);
3606 return (ExpandedArrayHeader *) DatumGetEOHP(d);
3607}
3608
3609/*
3610 * deconstruct_array --- simple method for extracting data from an array
3611 *
3612 * array: array object to examine (must not be NULL)
3613 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
3614 * elemsp: return value, set to point to palloc'd array of Datum values
3615 * nullsp: return value, set to point to palloc'd array of isnull markers
3616 * nelemsp: return value, set to number of extracted values
3617 *
3618 * The caller may pass nullsp == NULL if it does not support NULLs in the
3619 * array. Note that this produces a very uninformative error message,
3620 * so do it only in cases where a NULL is really not expected.
3621 *
3622 * If array elements are pass-by-ref data type, the returned Datums will
3623 * be pointers into the array object.
3624 *
3625 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
3626 * from the system catalogs, given the elmtype. However, the caller is
3627 * in a better position to cache this info across multiple uses, or even
3628 * to hard-wire values if the element type is hard-wired.
3629 */
3630void
3632 Oid elmtype,
3633 int elmlen, bool elmbyval, char elmalign,
3634 Datum **elemsp, bool **nullsp, int *nelemsp)
3635{
3636 Datum *elems;
3637 bool *nulls;
3638 int nelems;
3639 char *p;
3640 bits8 *bitmap;
3641 int bitmask;
3642 int i;
3643
3644 Assert(ARR_ELEMTYPE(array) == elmtype);
3645
3646 nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
3647 *elemsp = elems = (Datum *) palloc(nelems * sizeof(Datum));
3648 if (nullsp)
3649 *nullsp = nulls = (bool *) palloc0(nelems * sizeof(bool));
3650 else
3651 nulls = NULL;
3652 *nelemsp = nelems;
3653
3654 p = ARR_DATA_PTR(array);
3655 bitmap = ARR_NULLBITMAP(array);
3656 bitmask = 1;
3657
3658 for (i = 0; i < nelems; i++)
3659 {
3660 /* Get source element, checking for NULL */
3661 if (bitmap && (*bitmap & bitmask) == 0)
3662 {
3663 elems[i] = (Datum) 0;
3664 if (nulls)
3665 nulls[i] = true;
3666 else
3667 ereport(ERROR,
3668 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
3669 errmsg("null array element not allowed in this context")));
3670 }
3671 else
3672 {
3673 elems[i] = fetch_att(p, elmbyval, elmlen);
3674 p = att_addlength_pointer(p, elmlen, p);
3675 p = (char *) att_align_nominal(p, elmalign);
3676 }
3677
3678 /* advance bitmap pointer if any */
3679 if (bitmap)
3680 {
3681 bitmask <<= 1;
3682 if (bitmask == 0x100)
3683 {
3684 bitmap++;
3685 bitmask = 1;
3686 }
3687 }
3688 }
3689}
3690
3691/*
3692 * Like deconstruct_array(), where elmtype must be a built-in type, and
3693 * elmlen/elmbyval/elmalign is looked up from hardcoded data. This is often
3694 * useful when manipulating arrays from/for system catalogs.
3695 */
3696void
3698 Oid elmtype,
3699 Datum **elemsp, bool **nullsp, int *nelemsp)
3700{
3701 int elmlen;
3702 bool elmbyval;
3703 char elmalign;
3704
3705 switch (elmtype)
3706 {
3707 case CHAROID:
3708 elmlen = 1;
3709 elmbyval = true;
3710 elmalign = TYPALIGN_CHAR;
3711 break;
3712
3713 case CSTRINGOID:
3714 elmlen = -2;
3715 elmbyval = false;
3716 elmalign = TYPALIGN_CHAR;
3717 break;
3718
3719 case FLOAT8OID:
3720 elmlen = sizeof(float8);
3721 elmbyval = FLOAT8PASSBYVAL;
3722 elmalign = TYPALIGN_DOUBLE;
3723 break;
3724
3725 case INT2OID:
3726 elmlen = sizeof(int16);
3727 elmbyval = true;
3728 elmalign = TYPALIGN_SHORT;
3729 break;
3730
3731 case OIDOID:
3732 elmlen = sizeof(Oid);
3733 elmbyval = true;
3734 elmalign = TYPALIGN_INT;
3735 break;
3736
3737 case TEXTOID:
3738 elmlen = -1;
3739 elmbyval = false;
3740 elmalign = TYPALIGN_INT;
3741 break;
3742
3743 case TIDOID:
3744 elmlen = sizeof(ItemPointerData);
3745 elmbyval = false;
3746 elmalign = TYPALIGN_SHORT;
3747 break;
3748
3749 default:
3750 elog(ERROR, "type %u not supported by deconstruct_array_builtin()", elmtype);
3751 /* keep compiler quiet */
3752 elmlen = 0;
3753 elmbyval = false;
3754 elmalign = 0;
3755 }
3756
3757 deconstruct_array(array, elmtype, elmlen, elmbyval, elmalign, elemsp, nullsp, nelemsp);
3758}
3759
3760/*
3761 * array_contains_nulls --- detect whether an array has any null elements
3762 *
3763 * This gives an accurate answer, whereas testing ARR_HASNULL only tells
3764 * if the array *might* contain a null.
3765 */
3766bool
3768{
3769 int nelems;
3770 bits8 *bitmap;
3771 int bitmask;
3772
3773 /* Easy answer if there's no null bitmap */
3774 if (!ARR_HASNULL(array))
3775 return false;
3776
3777 nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
3778
3779 bitmap = ARR_NULLBITMAP(array);
3780
3781 /* check whole bytes of the bitmap byte-at-a-time */
3782 while (nelems >= 8)
3783 {
3784 if (*bitmap != 0xFF)
3785 return true;
3786 bitmap++;
3787 nelems -= 8;
3788 }
3789
3790 /* check last partial byte */
3791 bitmask = 1;
3792 while (nelems > 0)
3793 {
3794 if ((*bitmap & bitmask) == 0)
3795 return true;
3796 bitmask <<= 1;
3797 nelems--;
3798 }
3799
3800 return false;
3801}
3802
3803
3804/*
3805 * array_eq :
3806 * compares two arrays for equality
3807 * result :
3808 * returns true if the arrays are equal, false otherwise.
3809 *
3810 * Note: we do not use array_cmp here, since equality may be meaningful in
3811 * datatypes that don't have a total ordering (and hence no btree support).
3812 */
3813Datum
3815{
3816 LOCAL_FCINFO(locfcinfo, 2);
3819 Oid collation = PG_GET_COLLATION();
3820 int ndims1 = AARR_NDIM(array1);
3821 int ndims2 = AARR_NDIM(array2);
3822 int *dims1 = AARR_DIMS(array1);
3823 int *dims2 = AARR_DIMS(array2);
3824 int *lbs1 = AARR_LBOUND(array1);
3825 int *lbs2 = AARR_LBOUND(array2);
3826 Oid element_type = AARR_ELEMTYPE(array1);
3827 bool result = true;
3828 int nitems;
3829 TypeCacheEntry *typentry;
3830 int typlen;
3831 bool typbyval;
3832 char typalign;
3833 array_iter it1;
3834 array_iter it2;
3835 int i;
3836
3837 if (element_type != AARR_ELEMTYPE(array2))
3838 ereport(ERROR,
3839 (errcode(ERRCODE_DATATYPE_MISMATCH),
3840 errmsg("cannot compare arrays of different element types")));
3841
3842 /* fast path if the arrays do not have the same dimensionality */
3843 if (ndims1 != ndims2 ||
3844 memcmp(dims1, dims2, ndims1 * sizeof(int)) != 0 ||
3845 memcmp(lbs1, lbs2, ndims1 * sizeof(int)) != 0)
3846 result = false;
3847 else
3848 {
3849 /*
3850 * We arrange to look up the equality function only once per series of
3851 * calls, assuming the element type doesn't change underneath us. The
3852 * typcache is used so that we have no memory leakage when being used
3853 * as an index support function.
3854 */
3855 typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3856 if (typentry == NULL ||
3857 typentry->type_id != element_type)
3858 {
3859 typentry = lookup_type_cache(element_type,
3861 if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
3862 ereport(ERROR,
3863 (errcode(ERRCODE_UNDEFINED_FUNCTION),
3864 errmsg("could not identify an equality operator for type %s",
3865 format_type_be(element_type))));
3866 fcinfo->flinfo->fn_extra = typentry;
3867 }
3868 typlen = typentry->typlen;
3869 typbyval = typentry->typbyval;
3870 typalign = typentry->typalign;
3871
3872 /*
3873 * apply the operator to each pair of array elements.
3874 */
3875 InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
3876 collation, NULL, NULL);
3877
3878 /* Loop over source data */
3879 nitems = ArrayGetNItems(ndims1, dims1);
3880 array_iter_setup(&it1, array1);
3881 array_iter_setup(&it2, array2);
3882
3883 for (i = 0; i < nitems; i++)
3884 {
3885 Datum elt1;
3886 Datum elt2;
3887 bool isnull1;
3888 bool isnull2;
3889 bool oprresult;
3890
3891 /* Get elements, checking for NULL */
3892 elt1 = array_iter_next(&it1, &isnull1, i,
3893 typlen, typbyval, typalign);
3894 elt2 = array_iter_next(&it2, &isnull2, i,
3895 typlen, typbyval, typalign);
3896
3897 /*
3898 * We consider two NULLs equal; NULL and not-NULL are unequal.
3899 */
3900 if (isnull1 && isnull2)
3901 continue;
3902 if (isnull1 || isnull2)
3903 {
3904 result = false;
3905 break;
3906 }
3907
3908 /*
3909 * Apply the operator to the element pair; treat NULL as false
3910 */
3911 locfcinfo->args[0].value = elt1;
3912 locfcinfo->args[0].isnull = false;
3913 locfcinfo->args[1].value = elt2;
3914 locfcinfo->args[1].isnull = false;
3915 locfcinfo->isnull = false;
3916 oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
3917 if (locfcinfo->isnull || !oprresult)
3918 {
3919 result = false;
3920 break;
3921 }
3922 }
3923 }
3924
3925 /* Avoid leaking memory when handed toasted input. */
3926 AARR_FREE_IF_COPY(array1, 0);
3927 AARR_FREE_IF_COPY(array2, 1);
3928
3929 PG_RETURN_BOOL(result);
3930}
3931
3932
3933/*-----------------------------------------------------------------------------
3934 * array-array bool operators:
3935 * Given two arrays, iterate comparison operators
3936 * over the array. Uses logic similar to text comparison
3937 * functions, except element-by-element instead of
3938 * character-by-character.
3939 *----------------------------------------------------------------------------
3940 */
3941
3942Datum
3944{
3946}
3947
3948Datum
3950{
3951 PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
3952}
3953
3954Datum
3956{
3957 PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
3958}
3959
3960Datum
3962{
3963 PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
3964}
3965
3966Datum
3968{
3969 PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
3970}
3971
3972Datum
3974{
3975 PG_RETURN_INT32(array_cmp(fcinfo));
3976}
3977
3978/*
3979 * array_cmp()
3980 * Internal comparison function for arrays.
3981 *
3982 * Returns -1, 0 or 1
3983 */
3984static int
3986{
3987 LOCAL_FCINFO(locfcinfo, 2);
3990 Oid collation = PG_GET_COLLATION();
3991 int ndims1 = AARR_NDIM(array1);
3992 int ndims2 = AARR_NDIM(array2);
3993 int *dims1 = AARR_DIMS(array1);
3994 int *dims2 = AARR_DIMS(array2);
3995 int nitems1 = ArrayGetNItems(ndims1, dims1);
3996 int nitems2 = ArrayGetNItems(ndims2, dims2);
3997 Oid element_type = AARR_ELEMTYPE(array1);
3998 int result = 0;
3999 TypeCacheEntry *typentry;
4000 int typlen;
4001 bool typbyval;
4002 char typalign;
4003 int min_nitems;
4004 array_iter it1;
4005 array_iter it2;
4006 int i;
4007
4008 if (element_type != AARR_ELEMTYPE(array2))
4009 ereport(ERROR,
4010 (errcode(ERRCODE_DATATYPE_MISMATCH),
4011 errmsg("cannot compare arrays of different element types")));
4012
4013 /*
4014 * We arrange to look up the comparison function only once per series of
4015 * calls, assuming the element type doesn't change underneath us. The
4016 * typcache is used so that we have no memory leakage when being used as
4017 * an index support function.
4018 */
4019 typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
4020 if (typentry == NULL ||
4021 typentry->type_id != element_type)
4022 {
4023 typentry = lookup_type_cache(element_type,
4025 if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
4026 ereport(ERROR,
4027 (errcode(ERRCODE_UNDEFINED_FUNCTION),
4028 errmsg("could not identify a comparison function for type %s",
4029 format_type_be(element_type))));
4030 fcinfo->flinfo->fn_extra = typentry;
4031 }
4032 typlen = typentry->typlen;
4033 typbyval = typentry->typbyval;
4034 typalign = typentry->typalign;
4035
4036 /*
4037 * apply the operator to each pair of array elements.
4038 */
4039 InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
4040 collation, NULL, NULL);
4041
4042 /* Loop over source data */
4043 min_nitems = Min(nitems1, nitems2);
4044 array_iter_setup(&it1, array1);
4045 array_iter_setup(&it2, array2);
4046
4047 for (i = 0; i < min_nitems; i++)
4048 {
4049 Datum elt1;
4050 Datum elt2;
4051 bool isnull1;
4052 bool isnull2;
4053 int32 cmpresult;
4054
4055 /* Get elements, checking for NULL */
4056 elt1 = array_iter_next(&it1, &isnull1, i, typlen, typbyval, typalign);
4057 elt2 = array_iter_next(&it2, &isnull2, i, typlen, typbyval, typalign);
4058
4059 /*
4060 * We consider two NULLs equal; NULL > not-NULL.
4061 */
4062 if (isnull1 && isnull2)
4063 continue;
4064 if (isnull1)
4065 {
4066 /* arg1 is greater than arg2 */
4067 result = 1;
4068 break;
4069 }
4070 if (isnull2)
4071 {
4072 /* arg1 is less than arg2 */
4073 result = -1;
4074 break;
4075 }
4076
4077 /* Compare the pair of elements */
4078 locfcinfo->args[0].value = elt1;
4079 locfcinfo->args[0].isnull = false;
4080 locfcinfo->args[1].value = elt2;
4081 locfcinfo->args[1].isnull = false;
4082 cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
4083
4084 /* We don't expect comparison support functions to return null */
4085 Assert(!locfcinfo->isnull);
4086
4087 if (cmpresult == 0)
4088 continue; /* equal */
4089
4090 if (cmpresult < 0)
4091 {
4092 /* arg1 is less than arg2 */
4093 result = -1;
4094 break;
4095 }
4096 else
4097 {
4098 /* arg1 is greater than arg2 */
4099 result = 1;
4100 break;
4101 }
4102 }
4103
4104 /*
4105 * If arrays contain same data (up to end of shorter one), apply
4106 * additional rules to sort by dimensionality. The relative significance
4107 * of the different bits of information is historical; mainly we just care
4108 * that we don't say "equal" for arrays of different dimensionality.
4109 */
4110 if (result == 0)
4111 {
4112 if (nitems1 != nitems2)
4113 result = (nitems1 < nitems2) ? -1 : 1;
4114 else if (ndims1 != ndims2)
4115 result = (ndims1 < ndims2) ? -1 : 1;
4116 else
4117 {
4118 for (i = 0; i < ndims1; i++)
4119 {
4120 if (dims1[i] != dims2[i])
4121 {
4122 result = (dims1[i] < dims2[i]) ? -1 : 1;
4123 break;
4124 }
4125 }
4126 if (result == 0)
4127 {
4128 int *lbound1 = AARR_LBOUND(array1);
4129 int *lbound2 = AARR_LBOUND(array2);
4130
4131 for (i = 0; i < ndims1; i++)
4132 {
4133 if (lbound1[i] != lbound2[i])
4134 {
4135 result = (lbound1[i] < lbound2[i]) ? -1 : 1;
4136 break;
4137 }
4138 }
4139 }
4140 }
4141 }
4142
4143 /* Avoid leaking memory when handed toasted input. */
4144 AARR_FREE_IF_COPY(array1, 0);
4145 AARR_FREE_IF_COPY(array2, 1);
4146
4147 return result;
4148}
4149
4150
4151/*-----------------------------------------------------------------------------
4152 * array hashing
4153 * Hash the elements and combine the results.
4154 *----------------------------------------------------------------------------
4155 */
4156
4157Datum
4159{
4160 LOCAL_FCINFO(locfcinfo, 1);
4162 int ndims = AARR_NDIM(array);
4163 int *dims = AARR_DIMS(array);
4164 Oid element_type = AARR_ELEMTYPE(array);
4165 uint32 result = 1;
4166 int nitems;
4167 TypeCacheEntry *typentry;
4168 int typlen;
4169 bool typbyval;
4170 char typalign;
4171 int i;
4172 array_iter iter;
4173
4174 /*
4175 * We arrange to look up the hash function only once per series of calls,
4176 * assuming the element type doesn't change underneath us. The typcache
4177 * is used so that we have no memory leakage when being used as an index
4178 * support function.
4179 */
4180 typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
4181 if (typentry == NULL ||
4182 typentry->type_id != element_type)
4183 {
4184 typentry = lookup_type_cache(element_type,
4186 if (!OidIsValid(typentry->hash_proc_finfo.fn_oid) && element_type != RECORDOID)
4187 ereport(ERROR,
4188 (errcode(ERRCODE_UNDEFINED_FUNCTION),
4189 errmsg("could not identify a hash function for type %s",
4190 format_type_be(element_type))));
4191
4192 /*
4193 * The type cache doesn't believe that record is hashable (see
4194 * cache_record_field_properties()), but since we're here, we're
4195 * committed to hashing, so we can assume it does. Worst case, if any
4196 * components of the record don't support hashing, we will fail at
4197 * execution.
4198 */
4199 if (element_type == RECORDOID)
4200 {
4201 MemoryContext oldcontext;
4202 TypeCacheEntry *record_typentry;
4203
4204 oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
4205
4206 /*
4207 * Make fake type cache entry structure. Note that we can't just
4208 * modify typentry, since that points directly into the type
4209 * cache.
4210 */
4211 record_typentry = palloc0(sizeof(*record_typentry));
4212 record_typentry->type_id = element_type;
4213
4214 /* fill in what we need below */
4215 record_typentry->typlen = typentry->typlen;
4216 record_typentry->typbyval = typentry->typbyval;
4217 record_typentry->typalign = typentry->typalign;
4218 fmgr_info(F_HASH_RECORD, &record_typentry->hash_proc_finfo);
4219
4220 MemoryContextSwitchTo(oldcontext);
4221
4222 typentry = record_typentry;
4223 }
4224
4225 fcinfo->flinfo->fn_extra = typentry;
4226 }
4227
4228 typlen = typentry->typlen;
4229 typbyval = typentry->typbyval;
4230 typalign = typentry->typalign;
4231
4232 /*
4233 * apply the hash function to each array element.
4234 */
4235 InitFunctionCallInfoData(*locfcinfo, &typentry->hash_proc_finfo, 1,
4236 PG_GET_COLLATION(), NULL, NULL);
4237
4238 /* Loop over source data */
4239 nitems = ArrayGetNItems(ndims, dims);
4240 array_iter_setup(&iter, array);
4241
4242 for (i = 0; i < nitems; i++)
4243 {
4244 Datum elt;
4245 bool isnull;
4246 uint32 elthash;
4247
4248 /* Get element, checking for NULL */
4249 elt = array_iter_next(&iter, &isnull, i, typlen, typbyval, typalign);
4250
4251 if (isnull)
4252 {
4253 /* Treat nulls as having hashvalue 0 */
4254 elthash = 0;
4255 }
4256 else
4257 {
4258 /* Apply the hash function */
4259 locfcinfo->args[0].value = elt;
4260 locfcinfo->args[0].isnull = false;
4261 elthash = DatumGetUInt32(FunctionCallInvoke(locfcinfo));
4262 /* We don't expect hash functions to return null */
4263 Assert(!locfcinfo->isnull);
4264 }
4265
4266 /*
4267 * Combine hash values of successive elements by multiplying the
4268 * current value by 31 and adding on the new element's hash value.
4269 *
4270 * The result is a sum in which each element's hash value is
4271 * multiplied by a different power of 31. This is modulo 2^32
4272 * arithmetic, and the powers of 31 modulo 2^32 form a cyclic group of
4273 * order 2^27. So for arrays of up to 2^27 elements, each element's
4274 * hash value is multiplied by a different (odd) number, resulting in
4275 * a good mixing of all the elements' hash values.
4276 */
4277 result = (result << 5) - result + elthash;
4278 }
4279
4280 /* Avoid leaking memory when handed toasted input. */
4281 AARR_FREE_IF_COPY(array, 0);
4282
4283 PG_RETURN_UINT32(result);
4284}
4285
4286/*
4287 * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
4288 * Otherwise, similar to hash_array.
4289 */
4290Datum
4292{
4293 LOCAL_FCINFO(locfcinfo, 2);
4295 uint64 seed = PG_GETARG_INT64(1);
4296 int ndims = AARR_NDIM(array);
4297 int *dims = AARR_DIMS(array);
4298 Oid element_type = AARR_ELEMTYPE(array);
4299 uint64 result = 1;
4300 int nitems;
4301 TypeCacheEntry *typentry;
4302 int typlen;
4303 bool typbyval;
4304 char typalign;
4305 int i;
4306 array_iter iter;
4307
4308 typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
4309 if (typentry == NULL ||
4310 typentry->type_id != element_type)
4311 {
4312 typentry = lookup_type_cache(element_type,
4315 ereport(ERROR,
4316 (errcode(ERRCODE_UNDEFINED_FUNCTION),
4317 errmsg("could not identify an extended hash function for type %s",
4318 format_type_be(element_type))));
4319 fcinfo->flinfo->fn_extra = typentry;
4320 }
4321 typlen = typentry->typlen;
4322 typbyval = typentry->typbyval;
4323 typalign = typentry->typalign;
4324
4325 InitFunctionCallInfoData(*locfcinfo, &typentry->hash_extended_proc_finfo, 2,
4326 PG_GET_COLLATION(), NULL, NULL);
4327
4328 /* Loop over source data */
4329 nitems = ArrayGetNItems(ndims, dims);
4330 array_iter_setup(&iter, array);
4331
4332 for (i = 0; i < nitems; i++)
4333 {
4334 Datum elt;
4335 bool isnull;
4336 uint64 elthash;
4337
4338 /* Get element, checking for NULL */
4339 elt = array_iter_next(&iter, &isnull, i, typlen, typbyval, typalign);
4340
4341 if (isnull)
4342 {
4343 elthash = 0;
4344 }
4345 else
4346 {
4347 /* Apply the hash function */
4348 locfcinfo->args[0].value = elt;
4349 locfcinfo->args[0].isnull = false;
4350 locfcinfo->args[1].value = Int64GetDatum(seed);
4351 locfcinfo->args[1].isnull = false;
4352 elthash = DatumGetUInt64(FunctionCallInvoke(locfcinfo));
4353 /* We don't expect hash functions to return null */
4354 Assert(!locfcinfo->isnull);
4355 }
4356
4357 result = (result << 5) - result + elthash;
4358 }
4359
4360 AARR_FREE_IF_COPY(array, 0);
4361
4362 PG_RETURN_UINT64(result);
4363}
4364
4365
4366/*-----------------------------------------------------------------------------
4367 * array overlap/containment comparisons
4368 * These use the same methods of comparing array elements as array_eq.
4369 * We consider only the elements of the arrays, ignoring dimensionality.
4370 *----------------------------------------------------------------------------
4371 */
4372
4373/*
4374 * array_contain_compare :
4375 * compares two arrays for overlap/containment
4376 *
4377 * When matchall is true, return true if all members of array1 are in array2.
4378 * When matchall is false, return true if any members of array1 are in array2.
4379 */
4380static bool
4382 bool matchall, void **fn_extra)
4383{
4384 LOCAL_FCINFO(locfcinfo, 2);
4385 bool result = matchall;
4386 Oid element_type = AARR_ELEMTYPE(array1);
4387 TypeCacheEntry *typentry;
4388 int nelems1;
4389 Datum *values2;
4390 bool *nulls2;
4391 int nelems2;
4392 int typlen;
4393 bool typbyval;
4394 char typalign;
4395 int i;
4396 int j;
4397 array_iter it1;
4398
4399 if (element_type != AARR_ELEMTYPE(array2))
4400 ereport(ERROR,
4401 (errcode(ERRCODE_DATATYPE_MISMATCH),
4402 errmsg("cannot compare arrays of different element types")));
4403
4404 /*
4405 * We arrange to look up the equality function only once per series of
4406 * calls, assuming the element type doesn't change underneath us. The
4407 * typcache is used so that we have no memory leakage when being used as
4408 * an index support function.
4409 */
4410 typentry = (TypeCacheEntry *) *fn_extra;
4411 if (typentry == NULL ||
4412 typentry->type_id != element_type)
4413 {
4414 typentry = lookup_type_cache(element_type,
4416 if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
4417 ereport(ERROR,
4418 (errcode(ERRCODE_UNDEFINED_FUNCTION),
4419 errmsg("could not identify an equality operator for type %s",
4420 format_type_be(element_type))));
4421 *fn_extra = typentry;
4422 }
4423 typlen = typentry->typlen;
4424 typbyval = typentry->typbyval;
4425 typalign = typentry->typalign;
4426
4427 /*
4428 * Since we probably will need to scan array2 multiple times, it's
4429 * worthwhile to use deconstruct_array on it. We scan array1 the hard way
4430 * however, since we very likely won't need to look at all of it.
4431 */
4432 if (VARATT_IS_EXPANDED_HEADER(array2))
4433 {
4434 /* This should be safe even if input is read-only */
4435 deconstruct_expanded_array(&(array2->xpn));
4436 values2 = array2->xpn.dvalues;
4437 nulls2 = array2->xpn.dnulls;
4438 nelems2 = array2->xpn.nelems;
4439 }
4440 else
4441 deconstruct_array((ArrayType *) array2,
4442 element_type, typlen, typbyval, typalign,
4443 &values2, &nulls2, &nelems2);
4444
4445 /*
4446 * Apply the comparison operator to each pair of array elements.
4447 */
4448 InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
4449 collation, NULL, NULL);
4450
4451 /* Loop over source data */
4452 nelems1 = ArrayGetNItems(AARR_NDIM(array1), AARR_DIMS(array1));
4453 array_iter_setup(&it1, array1);
4454
4455 for (i = 0; i < nelems1; i++)
4456 {
4457 Datum elt1;
4458 bool isnull1;
4459
4460 /* Get element, checking for NULL */
4461 elt1 = array_iter_next(&it1, &isnull1, i, typlen, typbyval, typalign);
4462
4463 /*
4464 * We assume that the comparison operator is strict, so a NULL can't
4465 * match anything. XXX this diverges from the "NULL=NULL" behavior of
4466 * array_eq, should we act like that?
4467 */
4468 if (isnull1)
4469 {
4470 if (matchall)
4471 {
4472 result = false;
4473 break;
4474 }
4475 continue;
4476 }
4477
4478 for (j = 0; j < nelems2; j++)
4479 {
4480 Datum elt2 = values2[j];
4481 bool isnull2 = nulls2 ? nulls2[j] : false;
4482 bool oprresult;
4483
4484 if (isnull2)
4485 continue; /* can't match */
4486
4487 /*
4488 * Apply the operator to the element pair; treat NULL as false
4489 */
4490 locfcinfo->args[0].value = elt1;
4491 locfcinfo->args[0].isnull = false;
4492 locfcinfo->args[1].value = elt2;
4493 locfcinfo->args[1].isnull = false;
4494 locfcinfo->isnull = false;
4495 oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
4496 if (!locfcinfo->isnull && oprresult)
4497 break;
4498 }
4499
4500 if (j < nelems2)
4501 {
4502 /* found a match for elt1 */
4503 if (!matchall)
4504 {
4505 result = true;
4506 break;
4507 }
4508 }
4509 else
4510 {
4511 /* no match for elt1 */
4512 if (matchall)
4513 {
4514 result = false;
4515 break;
4516 }
4517 }
4518 }
4519
4520 return result;
4521}
4522
4523Datum
4525{
4528 Oid collation = PG_GET_COLLATION();
4529 bool result;
4530
4531 result = array_contain_compare(array1, array2, collation, false,
4532 &fcinfo->flinfo->fn_extra);
4533
4534 /* Avoid leaking memory when handed toasted input. */
4535 AARR_FREE_IF_COPY(array1, 0);
4536 AARR_FREE_IF_COPY(array2, 1);
4537
4538 PG_RETURN_BOOL(result);
4539}
4540
4541Datum
4543{
4546 Oid collation = PG_GET_COLLATION();
4547 bool result;
4548
4549 result = array_contain_compare(array2, array1, collation, true,
4550 &fcinfo->flinfo->fn_extra);
4551
4552 /* Avoid leaking memory when handed toasted input. */
4553 AARR_FREE_IF_COPY(array1, 0);
4554 AARR_FREE_IF_COPY(array2, 1);
4555
4556 PG_RETURN_BOOL(result);
4557}
4558
4559Datum
4561{
4564 Oid collation = PG_GET_COLLATION();
4565 bool result;
4566
4567 result = array_contain_compare(array1, array2, collation, true,
4568 &fcinfo->flinfo->fn_extra);
4569
4570 /* Avoid leaking memory when handed toasted input. */
4571 AARR_FREE_IF_COPY(array1, 0);
4572 AARR_FREE_IF_COPY(array2, 1);
4573
4574 PG_RETURN_BOOL(result);
4575}
4576
4577
4578/*-----------------------------------------------------------------------------
4579 * Array iteration functions
4580 * These functions are used to iterate efficiently through arrays
4581 *-----------------------------------------------------------------------------
4582 */
4583
4584/*
4585 * array_create_iterator --- set up to iterate through an array
4586 *
4587 * If slice_ndim is zero, we will iterate element-by-element; the returned
4588 * datums are of the array's element type.
4589 *
4590 * If slice_ndim is 1..ARR_NDIM(arr), we will iterate by slices: the
4591 * returned datums are of the same array type as 'arr', but of size
4592 * equal to the rightmost N dimensions of 'arr'.
4593 *
4594 * The passed-in array must remain valid for the lifetime of the iterator.
4595 */
4597array_create_iterator(ArrayType *arr, int slice_ndim, ArrayMetaState *mstate)
4598{
4599 ArrayIterator iterator = palloc0(sizeof(ArrayIteratorData));
4600
4601 /*
4602 * Sanity-check inputs --- caller should have got this right already
4603 */
4604 Assert(PointerIsValid(arr));
4605 if (slice_ndim < 0 || slice_ndim > ARR_NDIM(arr))
4606 elog(ERROR, "invalid arguments to array_create_iterator");
4607
4608 /*
4609 * Remember basic info about the array and its element type
4610 */
4611 iterator->arr = arr;
4612 iterator->nullbitmap = ARR_NULLBITMAP(arr);
4613 iterator->nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
4614
4615 if (mstate != NULL)
4616 {
4617 Assert(mstate->element_type == ARR_ELEMTYPE(arr));
4618
4619 iterator->typlen = mstate->typlen;
4620 iterator->typbyval = mstate->typbyval;
4621 iterator->typalign = mstate->typalign;
4622 }
4623 else
4625 &iterator->typlen,
4626 &iterator->typbyval,
4627 &iterator->typalign);
4628
4629 /*
4630 * Remember the slicing parameters.
4631 */
4632 iterator->slice_ndim = slice_ndim;
4633
4634 if (slice_ndim > 0)
4635 {
4636 /*
4637 * Get pointers into the array's dims and lbound arrays to represent
4638 * the dims/lbound arrays of a slice. These are the same as the
4639 * rightmost N dimensions of the array.
4640 */
4641 iterator->slice_dims = ARR_DIMS(arr) + ARR_NDIM(arr) - slice_ndim;
4642 iterator->slice_lbound = ARR_LBOUND(arr) + ARR_NDIM(arr) - slice_ndim;
4643
4644 /*
4645 * Compute number of elements in a slice.
4646 */
4647 iterator->slice_len = ArrayGetNItems(slice_ndim,
4648 iterator->slice_dims);
4649
4650 /*
4651 * Create workspace for building sub-arrays.
4652 */
4653 iterator->slice_values = (Datum *)
4654 palloc(iterator->slice_len * sizeof(Datum));
4655 iterator->slice_nulls = (bool *)
4656 palloc(iterator->slice_len * sizeof(bool));
4657 }
4658
4659 /*
4660 * Initialize our data pointer and linear element number. These will
4661 * advance through the array during array_iterate().
4662 */
4663 iterator->data_ptr = ARR_DATA_PTR(arr);
4664 iterator->current_item = 0;
4665
4666 return iterator;
4667}
4668
4669/*
4670 * Iterate through the array referenced by 'iterator'.
4671 *
4672 * As long as there is another element (or slice), return it into
4673 * *value / *isnull, and return true. Return false when no more data.
4674 */
4675bool
4676array_iterate(ArrayIterator iterator, Datum *value, bool *isnull)
4677{
4678 /* Done if we have reached the end of the array */
4679 if (iterator->current_item >= iterator->nitems)
4680 return false;
4681
4682 if (iterator->slice_ndim == 0)
4683 {
4684 /*
4685 * Scalar case: return one element.
4686 */
4687 if (array_get_isnull(iterator->nullbitmap, iterator->current_item++))
4688 {
4689 *isnull = true;
4690 *value = (Datum) 0;
4691 }
4692 else
4693 {
4694 /* non-NULL, so fetch the individual Datum to return */
4695 char *p = iterator->data_ptr;
4696
4697 *isnull = false;
4698 *value = fetch_att(p, iterator->typbyval, iterator->typlen);
4699
4700 /* Move our data pointer forward to the next element */
4701 p = att_addlength_pointer(p, iterator->typlen, p);
4702 p = (char *) att_align_nominal(p, iterator->typalign);
4703 iterator->data_ptr = p;
4704 }
4705 }
4706 else
4707 {
4708 /*
4709 * Slice case: build and return an array of the requested size.
4710 */
4711 ArrayType *result;
4712 Datum *values = iterator->slice_values;
4713 bool *nulls = iterator->slice_nulls;
4714 char *p = iterator->data_ptr;
4715 int i;
4716
4717 for (i = 0; i < iterator->slice_len; i++)
4718 {
4719 if (array_get_isnull(iterator->nullbitmap,
4720 iterator->current_item++))
4721 {
4722 nulls[i] = true;
4723 values[i] = (Datum) 0;
4724 }
4725 else
4726 {
4727 nulls[i] = false;
4728 values[i] = fetch_att(p, iterator->typbyval, iterator->typlen);
4729
4730 /* Move our data pointer forward to the next element */
4731 p = att_addlength_pointer(p, iterator->typlen, p);
4732 p = (char *) att_align_nominal(p, iterator->typalign);
4733 }
4734 }
4735
4736 iterator->data_ptr = p;
4737
4738 result = construct_md_array(values,
4739 nulls,
4740 iterator->slice_ndim,
4741 iterator->slice_dims,
4742 iterator->slice_lbound,
4743 ARR_ELEMTYPE(iterator->arr),
4744 iterator->typlen,
4745 iterator->typbyval,
4746 iterator->typalign);
4747
4748 *isnull = false;
4749 *value = PointerGetDatum(result);
4750 }
4751
4752 return true;
4753}
4754
4755/*
4756 * Release an ArrayIterator data structure
4757 */
4758void
4760{
4761 if (iterator->slice_ndim > 0)
4762 {
4763 pfree(iterator->slice_values);
4764 pfree(iterator->slice_nulls);
4765 }
4766 pfree(iterator);
4767}
4768
4769
4770/***************************************************************************/
4771/******************| Support Routines |*****************/
4772/***************************************************************************/
4773
4774/*
4775 * Check whether a specific array element is NULL
4776 *
4777 * nullbitmap: pointer to array's null bitmap (NULL if none)
4778 * offset: 0-based linear element number of array element
4779 */
4780static bool
4781array_get_isnull(const bits8 *nullbitmap, int offset)
4782{
4783 if (nullbitmap == NULL)
4784 return false; /* assume not null */
4785 if (nullbitmap[offset / 8] & (1 << (offset % 8)))
4786 return false; /* not null */
4787 return true;
4788}
4789
4790/*
4791 * Set a specific array element's null-bitmap entry
4792 *
4793 * nullbitmap: pointer to array's null bitmap (mustn't be NULL)
4794 * offset: 0-based linear element number of array element
4795 * isNull: null status to set
4796 */
4797static void
4798array_set_isnull(bits8 *nullbitmap, int offset, bool isNull)
4799{
4800 int bitmask;
4801
4802 nullbitmap += offset / 8;
4803 bitmask = 1 << (offset % 8);
4804 if (isNull)
4805 *nullbitmap &= ~bitmask;
4806 else
4807 *nullbitmap |= bitmask;
4808}
4809
4810/*
4811 * Fetch array element at pointer, converted correctly to a Datum
4812 *
4813 * Caller must have handled case of NULL element
4814 */
4815static Datum
4816ArrayCast(char *value, bool byval, int len)
4817{
4818 return fetch_att(value, byval, len);
4819}
4820
4821/*
4822 * Copy datum to *dest and return total space used (including align padding)
4823 *
4824 * Caller must have handled case of NULL element
4825 */
4826static int
4828 int typlen,
4829 bool typbyval,
4830 char typalign,
4831 char *dest)
4832{
4833 int inc;
4834
4835 if (typlen > 0)
4836 {
4837 if (typbyval)
4838 store_att_byval(dest, src, typlen);
4839 else
4840 memmove(dest, DatumGetPointer(src), typlen);
4841 inc = att_align_nominal(typlen, typalign);
4842 }
4843 else
4844 {
4845 Assert(!typbyval);
4846 inc = att_addlength_datum(0, typlen, src);
4847 memmove(dest, DatumGetPointer(src), inc);
4848 inc = att_align_nominal(inc, typalign);
4849 }
4850
4851 return inc;
4852}
4853
4854/*
4855 * Advance ptr over nitems array elements
4856 *
4857 * ptr: starting location in array
4858 * offset: 0-based linear element number of first element (the one at *ptr)
4859 * nullbitmap: start of array's null bitmap, or NULL if none
4860 * nitems: number of array elements to advance over (>= 0)
4861 * typlen, typbyval, typalign: storage parameters of array element datatype
4862 *
4863 * It is caller's responsibility to ensure that nitems is within range
4864 */
4865static char *
4866array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
4867 int typlen, bool typbyval, char typalign)
4868{
4869 int bitmask;
4870 int i;
4871
4872 /* easy if fixed-size elements and no NULLs */
4873 if (typlen > 0 && !nullbitmap)
4874 return ptr + nitems * ((Size) att_align_nominal(typlen, typalign));
4875
4876 /* seems worth having separate loops for NULL and no-NULLs cases */
4877 if (nullbitmap)
4878 {
4879 nullbitmap += offset / 8;
4880 bitmask = 1 << (offset % 8);
4881
4882 for (i = 0; i < nitems; i++)
4883 {
4884 if (*nullbitmap & bitmask)
4885 {
4886 ptr = att_addlength_pointer(ptr, typlen, ptr);
4887 ptr = (char *) att_align_nominal(ptr, typalign);
4888 }
4889 bitmask <<= 1;
4890 if (bitmask == 0x100)
4891 {
4892 nullbitmap++;
4893 bitmask = 1;
4894 }
4895 }
4896 }
4897 else
4898 {
4899 for (i = 0; i < nitems; i++)
4900 {
4901 ptr = att_addlength_pointer(ptr, typlen, ptr);
4902 ptr = (char *) att_align_nominal(ptr, typalign);
4903 }
4904 }
4905 return ptr;
4906}
4907
4908/*
4909 * Compute total size of the nitems array elements starting at *ptr
4910 *
4911 * Parameters same as for array_seek
4912 */
4913static int
4914array_nelems_size(char *ptr, int offset, bits8 *nullbitmap, int nitems,
4915 int typlen, bool typbyval, char typalign)
4916{
4917 return array_seek(ptr, offset, nullbitmap, nitems,
4918 typlen, typbyval, typalign) - ptr;
4919}
4920
4921/*
4922 * Copy nitems array elements from srcptr to destptr
4923 *
4924 * destptr: starting destination location (must be enough room!)
4925 * nitems: number of array elements to copy (>= 0)
4926 * srcptr: starting location in source array
4927 * offset: 0-based linear element number of first element (the one at *srcptr)
4928 * nullbitmap: start of source array's null bitmap, or NULL if none
4929 * typlen, typbyval, typalign: storage parameters of array element datatype
4930 *
4931 * Returns number of bytes copied
4932 *
4933 * NB: this does not take care of setting up the destination's null bitmap!
4934 */
4935static int
4936array_copy(char *destptr, int nitems,
4937 char *srcptr, int offset, bits8 *nullbitmap,
4938 int typlen, bool typbyval, char typalign)
4939{
4940 int numbytes;
4941
4942 numbytes = array_nelems_size(srcptr, offset, nullbitmap, nitems,
4943 typlen, typbyval, typalign);
4944 memcpy(destptr, srcptr, numbytes);
4945 return numbytes;
4946}
4947
4948/*
4949 * Copy nitems null-bitmap bits from source to destination
4950 *
4951 * destbitmap: start of destination array's null bitmap (mustn't be NULL)
4952 * destoffset: 0-based linear element number of first dest element
4953 * srcbitmap: start of source array's null bitmap, or NULL if none
4954 * srcoffset: 0-based linear element number of first source element
4955 * nitems: number of bits to copy (>= 0)
4956 *
4957 * If srcbitmap is NULL then we assume the source is all-non-NULL and
4958 * fill 1's into the destination bitmap. Note that only the specified
4959 * bits in the destination map are changed, not any before or after.
4960 *
4961 * Note: this could certainly be optimized using standard bitblt methods.
4962 * However, it's not clear that the typical Postgres array has enough elements
4963 * to make it worth worrying too much. For the moment, KISS.
4964 */
4965void
4966array_bitmap_copy(bits8 *destbitmap, int destoffset,
4967 const bits8 *srcbitmap, int srcoffset,
4968 int nitems)
4969{
4970 int destbitmask,
4971 destbitval,
4972 srcbitmask,
4973 srcbitval;
4974
4975 Assert(destbitmap);
4976 if (nitems <= 0)
4977 return; /* don't risk fetch off end of memory */
4978 destbitmap += destoffset / 8;
4979 destbitmask = 1 << (destoffset % 8);
4980 destbitval = *destbitmap;
4981 if (srcbitmap)
4982 {
4983 srcbitmap += srcoffset / 8;
4984 srcbitmask = 1 << (srcoffset % 8);
4985 srcbitval = *srcbitmap;
4986 while (nitems-- > 0)
4987 {
4988 if (srcbitval & srcbitmask)
4989 destbitval |= destbitmask;
4990 else
4991 destbitval &= ~destbitmask;
4992 destbitmask <<= 1;
4993 if (destbitmask == 0x100)
4994 {
4995 *destbitmap++ = destbitval;
4996 destbitmask = 1;
4997 if (nitems > 0)
4998 destbitval = *destbitmap;
4999 }
5000 srcbitmask <<= 1;
5001 if (srcbitmask == 0x100)
5002 {
5003 srcbitmap++;
5004 srcbitmask = 1;
5005 if (nitems > 0)
5006 srcbitval = *srcbitmap;
5007 }
5008 }
5009 if (destbitmask != 1)
5010 *destbitmap = destbitval;
5011 }
5012 else
5013 {
5014 while (nitems-- > 0)
5015 {
5016 destbitval |= destbitmask;
5017 destbitmask <<= 1;
5018 if (destbitmask == 0x100)
5019 {
5020 *destbitmap++ = destbitval;
5021 destbitmask = 1;
5022 if (nitems > 0)
5023 destbitval = *destbitmap;
5024 }
5025 }
5026 if (destbitmask != 1)
5027 *destbitmap = destbitval;
5028 }
5029}
5030
5031/*
5032 * Compute space needed for a slice of an array
5033 *
5034 * We assume the caller has verified that the slice coordinates are valid.
5035 */
5036static int
5037array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
5038 int ndim, int *dim, int *lb,
5039 int *st, int *endp,
5040 int typlen, bool typbyval, char typalign)
5041{
5042 int src_offset,
5043 span[MAXDIM],
5044 prod[MAXDIM],
5045 dist[MAXDIM],
5046 indx[MAXDIM];
5047 char *ptr;
5048 int i,
5049 j,
5050 inc;
5051 int count = 0;
5052
5053 mda_get_range(ndim, span, st, endp);
5054
5055 /* Pretty easy for fixed element length without nulls ... */
5056 if (typlen > 0 && !arraynullsptr)
5057 return ArrayGetNItems(ndim, span) * att_align_nominal(typlen, typalign);
5058
5059 /* Else gotta do it the hard way */
5060 src_offset = ArrayGetOffset(ndim, dim, lb, st);
5061 ptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
5062 typlen, typbyval, typalign);
5063 mda_get_prod(ndim, dim, prod);
5064 mda_get_offset_values(ndim, dist, prod, span);
5065 for (i = 0; i < ndim; i++)
5066 indx[i] = 0;
5067 j = ndim - 1;
5068 do
5069 {
5070 if (dist[j])
5071 {
5072 ptr = array_seek(ptr, src_offset, arraynullsptr, dist[j],
5073 typlen, typbyval, typalign);
5074 src_offset += dist[j];
5075 }
5076 if (!array_get_isnull(arraynullsptr, src_offset))
5077 {
5078 inc = att_addlength_pointer(0, typlen, ptr);
5079 inc = att_align_nominal(inc, typalign);
5080 ptr += inc;
5081 count += inc;
5082 }
5083 src_offset++;
5084 } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
5085 return count;
5086}
5087
5088/*
5089 * Extract a slice of an array into consecutive elements in the destination
5090 * array.
5091 *
5092 * We assume the caller has verified that the slice coordinates are valid,
5093 * allocated enough storage for the result, and initialized the header
5094 * of the new array.
5095 */
5096static void
5098 int ndim,
5099 int *dim,
5100 int *lb,
5101 char *arraydataptr,
5102 bits8 *arraynullsptr,
5103 int *st,
5104 int *endp,
5105 int typlen,
5106 bool typbyval,
5107 char typalign)
5108{
5109 char *destdataptr = ARR_DATA_PTR(newarray);
5110 bits8 *destnullsptr = ARR_NULLBITMAP(newarray);
5111 char *srcdataptr;
5112 int src_offset,
5113 dest_offset,
5114 prod[MAXDIM],
5115 span[MAXDIM],
5116 dist[MAXDIM],
5117 indx[MAXDIM];
5118 int i,
5119 j,
5120 inc;
5121
5122 src_offset = ArrayGetOffset(ndim, dim, lb, st);
5123 srcdataptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
5124 typlen, typbyval, typalign);
5125 mda_get_prod(ndim, dim, prod);
5126 mda_get_range(ndim, span, st, endp);
5127 mda_get_offset_values(ndim, dist, prod, span);
5128 for (i = 0; i < ndim; i++)
5129 indx[i] = 0;
5130 dest_offset = 0;
5131 j = ndim - 1;
5132 do
5133 {
5134 if (dist[j])
5135 {
5136 /* skip unwanted elements */
5137 srcdataptr = array_seek(srcdataptr, src_offset, arraynullsptr,
5138 dist[j],
5139 typlen, typbyval, typalign);
5140 src_offset += dist[j];
5141 }
5142 inc = array_copy(destdataptr, 1,
5143 srcdataptr, src_offset, arraynullsptr,
5144 typlen, typbyval, typalign);
5145 if (destnullsptr)
5146 array_bitmap_copy(destnullsptr, dest_offset,
5147 arraynullsptr, src_offset,
5148 1);
5149 destdataptr += inc;
5150 srcdataptr += inc;
5151 src_offset++;
5152 dest_offset++;
5153 } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
5154}
5155
5156/*
5157 * Insert a slice into an array.
5158 *
5159 * ndim/dim[]/lb[] are dimensions of the original array. A new array with
5160 * those same dimensions is to be constructed. destArray must already
5161 * have been allocated and its header initialized.
5162 *
5163 * st[]/endp[] identify the slice to be replaced. Elements within the slice
5164 * volume are taken from consecutive elements of the srcArray; elements
5165 * outside it are copied from origArray.
5166 *
5167 * We assume the caller has verified that the slice coordinates are valid.
5168 */
5169static void
5171 ArrayType *origArray,
5172 ArrayType *srcArray,
5173 int ndim,
5174 int *dim,
5175 int *lb,
5176 int *st,
5177 int *endp,
5178 int typlen,
5179 bool typbyval,
5180 char typalign)
5181{
5182 char *destPtr = ARR_DATA_PTR(destArray);
5183 char *origPtr = ARR_DATA_PTR(origArray);
5184 char *srcPtr = ARR_DATA_PTR(srcArray);
5185 bits8 *destBitmap = ARR_NULLBITMAP(destArray);
5186 bits8 *origBitmap = ARR_NULLBITMAP(origArray);
5187 bits8 *srcBitmap = ARR_NULLBITMAP(srcArray);
5188 int orignitems = ArrayGetNItems(ARR_NDIM(origArray),
5189 ARR_DIMS(origArray));
5190 int dest_offset,
5191 orig_offset,
5192 src_offset,
5193 prod[MAXDIM],
5194 span[MAXDIM],
5195 dist[MAXDIM],
5196 indx[MAXDIM];
5197 int i,
5198 j,
5199 inc;
5200
5201 dest_offset = ArrayGetOffset(ndim, dim, lb, st);
5202 /* copy items before the slice start */
5203 inc = array_copy(destPtr, dest_offset,
5204 origPtr, 0, origBitmap,
5205 typlen, typbyval, typalign);
5206 destPtr += inc;
5207 origPtr += inc;
5208 if (destBitmap)
5209 array_bitmap_copy(destBitmap, 0, origBitmap, 0, dest_offset);
5210 orig_offset = dest_offset;
5211 mda_get_prod(ndim, dim, prod);
5212 mda_get_range(ndim, span, st, endp);
5213 mda_get_offset_values(ndim, dist, prod, span);
5214 for (i = 0; i < ndim; i++)
5215 indx[i] = 0;
5216 src_offset = 0;
5217 j = ndim - 1;
5218 do
5219 {
5220 /* Copy/advance over elements between here and next part of slice */
5221 if (dist[j])
5222 {
5223 inc = array_copy(destPtr, dist[j],
5224 origPtr, orig_offset, origBitmap,
5225 typlen, typbyval, typalign);
5226 destPtr += inc;
5227 origPtr += inc;
5228 if (destBitmap)
5229 array_bitmap_copy(destBitmap, dest_offset,
5230 origBitmap, orig_offset,
5231 dist[j]);
5232 dest_offset += dist[j];
5233 orig_offset += dist[j];
5234 }
5235 /* Copy new element at this slice position */
5236 inc = array_copy(destPtr, 1,
5237 srcPtr, src_offset, srcBitmap,
5238 typlen, typbyval, typalign);
5239 if (destBitmap)
5240 array_bitmap_copy(destBitmap, dest_offset,
5241 srcBitmap, src_offset,
5242 1);
5243 destPtr += inc;
5244 srcPtr += inc;
5245 dest_offset++;
5246 src_offset++;
5247 /* Advance over old element at this slice position */
5248 origPtr = array_seek(origPtr, orig_offset, origBitmap, 1,
5249 typlen, typbyval, typalign);
5250 orig_offset++;
5251 } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
5252
5253 /* don't miss any data at the end */
5254 array_copy(destPtr, orignitems - orig_offset,
5255 origPtr, orig_offset, origBitmap,
5256 typlen, typbyval, typalign);
5257 if (destBitmap)
5258 array_bitmap_copy(destBitmap, dest_offset,
5259 origBitmap, orig_offset,
5260 orignitems - orig_offset);
5261}
5262
5263/*
5264 * initArrayResult - initialize an empty ArrayBuildState
5265 *
5266 * element_type is the array element type (must be a valid array element type)
5267 * rcontext is where to keep working state
5268 * subcontext is a flag determining whether to use a separate memory context
5269 *
5270 * Note: there are two common schemes for using accumArrayResult().
5271 * In the older scheme, you start with a NULL ArrayBuildState pointer, and
5272 * call accumArrayResult once per element. In this scheme you end up with
5273 * a NULL pointer if there were no elements, which you need to special-case.
5274 * In the newer scheme, call initArrayResult and then call accumArrayResult
5275 * once per element. In this scheme you always end with a non-NULL pointer
5276 * that you can pass to makeArrayResult; you get an empty array if there
5277 * were no elements. This is preferred if an empty array is what you want.
5278 *
5279 * It's possible to choose whether to create a separate memory context for the
5280 * array build state, or whether to allocate it directly within rcontext.
5281 *
5282 * When there are many concurrent small states (e.g. array_agg() using hash
5283 * aggregation of many small groups), using a separate memory context for each
5284 * one may result in severe memory bloat. In such cases, use the same memory
5285 * context to initialize all such array build states, and pass
5286 * subcontext=false.
5287 *
5288 * In cases when the array build states have different lifetimes, using a
5289 * single memory context is impractical. Instead, pass subcontext=true so that
5290 * the array build states can be freed individually.
5291 */
5293initArrayResult(Oid element_type, MemoryContext rcontext, bool subcontext)
5294{
5295 /*
5296 * When using a subcontext, we can afford to start with a somewhat larger
5297 * initial array size. Without subcontexts, we'd better hope that most of
5298 * the states stay small ...
5299 */
5300 return initArrayResultWithSize(element_type, rcontext, subcontext,
5301 subcontext ? 64 : 8);
5302}
5303
5304/*
5305 * initArrayResultWithSize
5306 * As initArrayResult, but allow the initial size of the allocated arrays
5307 * to be specified.
5308 */
5311 bool subcontext, int initsize)
5312{
5313 ArrayBuildState *astate;
5314 MemoryContext arr_context = rcontext;
5315
5316 /* Make a temporary context to hold all the junk */
5317 if (subcontext)
5318 arr_context = AllocSetContextCreate(rcontext,
5319 "accumArrayResult",
5321
5322 astate = (ArrayBuildState *)
5323 MemoryContextAlloc(arr_context, sizeof(ArrayBuildState));
5324 astate->mcontext = arr_context;
5325 astate->private_cxt = subcontext;
5326 astate->alen = initsize;
5327 astate->dvalues = (Datum *)
5328 MemoryContextAlloc(arr_context, astate->alen * sizeof(Datum));
5329 astate->dnulls = (bool *)
5330 MemoryContextAlloc(arr_context, astate->alen * sizeof(bool));
5331 astate->nelems = 0;
5332 astate->element_type = element_type;
5333 get_typlenbyvalalign(element_type,
5334 &astate->typlen,
5335 &astate->typbyval,
5336 &astate->typalign);
5337
5338 return astate;
5339}
5340
5341/*
5342 * accumArrayResult - accumulate one (more) Datum for an array result
5343 *
5344 * astate is working state (can be NULL on first call)
5345 * dvalue/disnull represent the new Datum to append to the array
5346 * element_type is the Datum's type (must be a valid array element type)
5347 * rcontext is where to keep working state
5348 */
5351 Datum dvalue, bool disnull,
5352 Oid element_type,
5353 MemoryContext rcontext)
5354{
5355 MemoryContext oldcontext;
5356
5357 if (astate == NULL)
5358 {
5359 /* First time through --- initialize */
5360 astate = initArrayResult(element_type, rcontext, true);
5361 }
5362 else
5363 {
5364 Assert(astate->element_type == element_type);
5365 }
5366
5367 oldcontext = MemoryContextSwitchTo(astate->mcontext);
5368
5369 /* enlarge dvalues[]/dnulls[] if needed */
5370 if (astate->nelems >= astate->alen)
5371 {
5372 astate->alen *= 2;
5373 /* give an array-related error if we go past MaxAllocSize */
5374 if (!AllocSizeIsValid(astate->alen * sizeof(Datum)))
5375 ereport(ERROR,
5376 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
5377 errmsg("array size exceeds the maximum allowed (%d)",
5378 (int) MaxAllocSize)));
5379 astate->dvalues = (Datum *)
5380 repalloc(astate->dvalues, astate->alen * sizeof(Datum));
5381 astate->dnulls = (bool *)
5382 repalloc(astate->dnulls, astate->alen * sizeof(bool));
5383 }
5384
5385 /*
5386 * Ensure pass-by-ref stuff is copied into mcontext; and detoast it too if
5387 * it's varlena. (You might think that detoasting is not needed here
5388 * because construct_md_array can detoast the array elements later.
5389 * However, we must not let construct_md_array modify the ArrayBuildState
5390 * because that would mean array_agg_finalfn damages its input, which is
5391 * verboten. Also, this way frequently saves one copying step.)
5392 */
5393 if (!disnull && !astate->typbyval)
5394 {
5395 if (astate->typlen == -1)
5396 dvalue = PointerGetDatum(PG_DETOAST_DATUM_COPY(dvalue));
5397 else
5398 dvalue = datumCopy(dvalue, astate->typbyval, astate->typlen);
5399 }
5400
5401 astate->dvalues[astate->nelems] = dvalue;
5402 astate->dnulls[astate->nelems] = disnull;
5403 astate->nelems++;
5404
5405 MemoryContextSwitchTo(oldcontext);
5406
5407 return astate;
5408}
5409
5410/*
5411 * makeArrayResult - produce 1-D final result of accumArrayResult
5412 *
5413 * Note: only releases astate if it was initialized within a separate memory
5414 * context (i.e. using subcontext=true when calling initArrayResult).
5415 *
5416 * astate is working state (must not be NULL)
5417 * rcontext is where to construct result
5418 */
5419Datum
5421 MemoryContext rcontext)
5422{
5423 int ndims;
5424 int dims[1];
5425 int lbs[1];
5426
5427 /* If no elements were presented, we want to create an empty array */
5428 ndims = (astate->nelems > 0) ? 1 : 0;
5429 dims[0] = astate->nelems;
5430 lbs[0] = 1;
5431
5432 return makeMdArrayResult(astate, ndims, dims, lbs, rcontext,
5433 astate->private_cxt);
5434}
5435
5436/*
5437 * makeMdArrayResult - produce multi-D final result of accumArrayResult
5438 *
5439 * beware: no check that specified dimensions match the number of values
5440 * accumulated.
5441 *
5442 * Note: if the astate was not initialized within a separate memory context
5443 * (that is, initArrayResult was called with subcontext=false), then using
5444 * release=true is illegal. Instead, release astate along with the rest of its
5445 * context when appropriate.
5446 *
5447 * astate is working state (must not be NULL)
5448 * rcontext is where to construct result
5449 * release is true if okay to release working state
5450 */
5451Datum
5453 int ndims,
5454 int *dims,
5455 int *lbs,
5456 MemoryContext rcontext,
5457 bool release)
5458{
5459 ArrayType *result;
5460 MemoryContext oldcontext;
5461
5462 /* Build the final array result in rcontext */
5463 oldcontext = MemoryContextSwitchTo(rcontext);
5464
5465 result = construct_md_array(astate->dvalues,
5466 astate->dnulls,
5467 ndims,
5468 dims,
5469 lbs,
5470 astate->element_type,
5471 astate->typlen,
5472 astate->typbyval,
5473 astate->typalign);
5474
5475 MemoryContextSwitchTo(oldcontext);
5476
5477 /* Clean up all the junk */
5478 if (release)
5479 {
5480 Assert(astate->private_cxt);
5482 }
5483
5484 return PointerGetDatum(result);
5485}
5486
5487/*
5488 * The following three functions provide essentially the same API as
5489 * initArrayResult/accumArrayResult/makeArrayResult, but instead of accepting
5490 * inputs that are array elements, they accept inputs that are arrays and
5491 * produce an output array having N+1 dimensions. The inputs must all have
5492 * identical dimensionality as well as element type.
5493 */
5494
5495/*
5496 * initArrayResultArr - initialize an empty ArrayBuildStateArr
5497 *
5498 * array_type is the array type (must be a valid varlena array type)
5499 * element_type is the type of the array's elements (lookup if InvalidOid)
5500 * rcontext is where to keep working state
5501 * subcontext is a flag determining whether to use a separate memory context
5502 */
5504initArrayResultArr(Oid array_type, Oid element_type, MemoryContext rcontext,
5505 bool subcontext)
5506{
5507 ArrayBuildStateArr *astate;
5508 MemoryContext arr_context = rcontext; /* by default use the parent ctx */
5509
5510 /* Lookup element type, unless element_type already provided */
5511 if (!OidIsValid(element_type))
5512 {
5513 element_type = get_element_type(array_type);
5514
5515 if (!OidIsValid(element_type))
5516 ereport(ERROR,
5517 (errcode(ERRCODE_DATATYPE_MISMATCH),
5518 errmsg("data type %s is not an array type",
5519 format_type_be(array_type))));
5520 }
5521
5522 /* Make a temporary context to hold all the junk */
5523 if (subcontext)
5524 arr_context = AllocSetContextCreate(rcontext,
5525 "accumArrayResultArr",
5527
5528 /* Note we initialize all fields to zero */
5529 astate = (ArrayBuildStateArr *)
5530 MemoryContextAllocZero(arr_context, sizeof(ArrayBuildStateArr));
5531 astate->mcontext = arr_context;
5532 astate->private_cxt = subcontext;
5533
5534 /* Save relevant datatype information */
5535 astate->array_type = array_type;
5536 astate->element_type = element_type;
5537
5538 return astate;
5539}
5540
5541/*
5542 * accumArrayResultArr - accumulate one (more) sub-array for an array result
5543 *
5544 * astate is working state (can be NULL on first call)
5545 * dvalue/disnull represent the new sub-array to append to the array
5546 * array_type is the array type (must be a valid varlena array type)
5547 * rcontext is where to keep working state
5548 */
5551 Datum dvalue, bool disnull,
5552 Oid array_type,
5553 MemoryContext rcontext)
5554{
5555 ArrayType *arg;
5556 MemoryContext oldcontext;
5557 int *dims,
5558 *lbs,
5559 ndims,
5560 nitems,
5561 ndatabytes;
5562 char *data;
5563 int i;
5564
5565 /*
5566 * We disallow accumulating null subarrays. Another plausible definition
5567 * is to ignore them, but callers that want that can just skip calling
5568 * this function.
5569 */
5570 if (disnull)
5571 ereport(ERROR,
5572 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5573 errmsg("cannot accumulate null arrays")));
5574
5575 /* Detoast input array in caller's context */
5576 arg = DatumGetArrayTypeP(dvalue);
5577
5578 if (astate == NULL)
5579 astate = initArrayResultArr(array_type, InvalidOid, rcontext, true);
5580 else
5581 Assert(astate->array_type == array_type);
5582
5583 oldcontext = MemoryContextSwitchTo(astate->mcontext);
5584
5585 /* Collect this input's dimensions */
5586 ndims = ARR_NDIM(arg);
5587 dims = ARR_DIMS(arg);
5588 lbs = ARR_LBOUND(arg);
5590 nitems = ArrayGetNItems(ndims, dims);
5591 ndatabytes = ARR_SIZE(arg) - ARR_DATA_OFFSET(arg);
5592
5593 if (astate->ndims == 0)
5594 {
5595 /* First input; check/save the dimensionality info */
5596
5597 /* Should we allow empty inputs and just produce an empty output? */
5598 if (ndims == 0)
5599 ereport(ERROR,
5600 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5601 errmsg("cannot accumulate empty arrays")));
5602 if (ndims + 1 > MAXDIM)
5603 ereport(ERROR,
5604 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
5605 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
5606 ndims + 1, MAXDIM)));
5607
5608 /*
5609 * The output array will have n+1 dimensions, with the ones after the
5610 * first matching the input's dimensions.
5611 */
5612 astate->ndims = ndims + 1;
5613 astate->dims[0] = 0;
5614 memcpy(&astate->dims[1], dims, ndims * sizeof(int));
5615 astate->lbs[0] = 1;
5616 memcpy(&astate->lbs[1], lbs, ndims * sizeof(int));
5617
5618 /* Allocate at least enough data space for this item */
5619 astate->abytes = pg_nextpower2_32(Max(1024, ndatabytes + 1));
5620 astate->data = (char *) palloc(astate->abytes);
5621 }
5622 else
5623 {
5624 /* Second or later input: must match first input's dimensionality */
5625 if (astate->ndims != ndims + 1)
5626 ereport(ERROR,
5627 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5628 errmsg("cannot accumulate arrays of different dimensionality")));
5629 for (i = 0; i < ndims; i++)
5630 {
5631 if (astate->dims[i + 1] != dims[i] || astate->lbs[i + 1] != lbs[i])
5632 ereport(ERROR,
5633 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5634 errmsg("cannot accumulate arrays of different dimensionality")));
5635 }
5636
5637 /* Enlarge data space if needed */
5638 if (astate->nbytes + ndatabytes > astate->abytes)
5639 {
5640 astate->abytes = Max(astate->abytes * 2,
5641 astate->nbytes + ndatabytes);
5642 astate->data = (char *) repalloc(astate->data, astate->abytes);
5643 }
5644 }
5645
5646 /*
5647 * Copy the data portion of the sub-array. Note we assume that the
5648 * advertised data length of the sub-array is properly aligned. We do not
5649 * have to worry about detoasting elements since whatever's in the
5650 * sub-array should be OK already.
5651 */
5652 memcpy(astate->data + astate->nbytes, data, ndatabytes);
5653 astate->nbytes += ndatabytes;
5654
5655 /* Deal with null bitmap if needed */
5656 if (astate->nullbitmap || ARR_HASNULL(arg))
5657 {
5658 int newnitems = astate->nitems + nitems;
5659
5660 if (astate->nullbitmap == NULL)
5661 {
5662 /*
5663 * First input with nulls; we must retrospectively handle any
5664 * previous inputs by marking all their items non-null.
5665 */
5666 astate->aitems = pg_nextpower2_32(Max(256, newnitems + 1));
5667 astate->nullbitmap = (bits8 *) palloc((astate->aitems + 7) / 8);
5668 array_bitmap_copy(astate->nullbitmap, 0,
5669 NULL, 0,
5670 astate->nitems);
5671 }
5672 else if (newnitems > astate->aitems)
5673 {
5674 astate->aitems = Max(astate->aitems * 2, newnitems);
5675 astate->nullbitmap = (bits8 *)
5676 repalloc(astate->nullbitmap, (astate->aitems + 7) / 8);
5677 }
5678 array_bitmap_copy(astate->nullbitmap, astate->nitems,
5679 ARR_NULLBITMAP(arg), 0,
5680 nitems);
5681 }
5682
5683 astate->nitems += nitems;
5684 astate->dims[0] += 1;
5685
5686 MemoryContextSwitchTo(oldcontext);
5687
5688 /* Release detoasted copy if any */
5689 if ((Pointer) arg != DatumGetPointer(dvalue))
5690 pfree(arg);
5691
5692 return astate;
5693}
5694
5695/*
5696 * makeArrayResultArr - produce N+1-D final result of accumArrayResultArr
5697 *
5698 * astate is working state (must not be NULL)
5699 * rcontext is where to construct result
5700 * release is true if okay to release working state
5701 */
5702Datum
5704 MemoryContext rcontext,
5705 bool release)
5706{
5707 ArrayType *result;
5708 MemoryContext oldcontext;
5709
5710 /* Build the final array result in rcontext */
5711 oldcontext = MemoryContextSwitchTo(rcontext);
5712
5713 if (astate->ndims == 0)
5714 {
5715 /* No inputs, return empty array */
5716 result = construct_empty_array(astate->element_type);
5717 }
5718 else
5719 {
5720 int dataoffset,
5721 nbytes;
5722
5723 /* Check for overflow of the array dimensions */
5724 (void) ArrayGetNItems(astate->ndims, astate->dims);
5725 ArrayCheckBounds(astate->ndims, astate->dims, astate->lbs);
5726
5727 /* Compute required space */
5728 nbytes = astate->nbytes;
5729 if (astate->nullbitmap != NULL)
5730 {
5731 dataoffset = ARR_OVERHEAD_WITHNULLS(astate->ndims, astate->nitems);
5732 nbytes += dataoffset;
5733 }
5734 else
5735 {
5736 dataoffset = 0;
5737 nbytes += ARR_OVERHEAD_NONULLS(astate->ndims);
5738 }
5739
5740 result = (ArrayType *) palloc0(nbytes);
5741 SET_VARSIZE(result, nbytes);
5742 result->ndim = astate->ndims;
5743 result->dataoffset = dataoffset;
5744 result->elemtype = astate->element_type;
5745
5746 memcpy(ARR_DIMS(result), astate->dims, astate->ndims * sizeof(int));
5747 memcpy(ARR_LBOUND(result), astate->lbs, astate->ndims * sizeof(int));
5748 memcpy(ARR_DATA_PTR(result), astate->data, astate->nbytes);
5749
5750 if (astate->nullbitmap != NULL)
5752 astate->nullbitmap, 0,
5753 astate->nitems);
5754 }
5755
5756 MemoryContextSwitchTo(oldcontext);
5757
5758 /* Clean up all the junk */
5759 if (release)
5760 {
5761 Assert(astate->private_cxt);
5763 }
5764
5765 return PointerGetDatum(result);
5766}
5767
5768/*
5769 * The following three functions provide essentially the same API as
5770 * initArrayResult/accumArrayResult/makeArrayResult, but can accept either
5771 * scalar or array inputs, invoking the appropriate set of functions above.
5772 */
5773
5774/*
5775 * initArrayResultAny - initialize an empty ArrayBuildStateAny
5776 *
5777 * input_type is the input datatype (either element or array type)
5778 * rcontext is where to keep working state
5779 * subcontext is a flag determining whether to use a separate memory context
5780 */
5782initArrayResultAny(Oid input_type, MemoryContext rcontext, bool subcontext)
5783{
5784 ArrayBuildStateAny *astate;
5785
5786 /*
5787 * int2vector and oidvector will satisfy both get_element_type and
5788 * get_array_type. We prefer to treat them as scalars, to be consistent
5789 * with get_promoted_array_type. Hence, check get_array_type not
5790 * get_element_type.
5791 */
5792 if (!OidIsValid(get_array_type(input_type)))
5793 {
5794 /* Array case */
5795 ArrayBuildStateArr *arraystate;
5796
5797 arraystate = initArrayResultArr(input_type, InvalidOid, rcontext, subcontext);
5798 astate = (ArrayBuildStateAny *)
5799 MemoryContextAlloc(arraystate->mcontext,
5800 sizeof(ArrayBuildStateAny));
5801 astate->scalarstate = NULL;
5802 astate->arraystate = arraystate;
5803 }
5804 else
5805 {
5806 /* Scalar case */
5807 ArrayBuildState *scalarstate;
5808
5809 scalarstate = initArrayResult(input_type, rcontext, subcontext);
5810 astate = (ArrayBuildStateAny *)
5811 MemoryContextAlloc(scalarstate->mcontext,
5812 sizeof(ArrayBuildStateAny));
5813 astate->scalarstate = scalarstate;
5814 astate->arraystate = NULL;
5815 }
5816
5817 return astate;
5818}
5819
5820/*
5821 * accumArrayResultAny - accumulate one (more) input for an array result
5822 *
5823 * astate is working state (can be NULL on first call)
5824 * dvalue/disnull represent the new input to append to the array
5825 * input_type is the input datatype (either element or array type)
5826 * rcontext is where to keep working state
5827 */
5830 Datum dvalue, bool disnull,
5831 Oid input_type,
5832 MemoryContext rcontext)
5833{
5834 if (astate == NULL)
5835 astate = initArrayResultAny(input_type, rcontext, true);
5836
5837 if (astate->scalarstate)
5838 (void) accumArrayResult(astate->scalarstate,
5839 dvalue, disnull,
5840 input_type, rcontext);
5841 else
5842 (void) accumArrayResultArr(astate->arraystate,
5843 dvalue, disnull,
5844 input_type, rcontext);
5845
5846 return astate;
5847}
5848
5849/*
5850 * makeArrayResultAny - produce final result of accumArrayResultAny
5851 *
5852 * astate is working state (must not be NULL)
5853 * rcontext is where to construct result
5854 * release is true if okay to release working state
5855 */
5856Datum
5858 MemoryContext rcontext, bool release)
5859{
5860 Datum result;
5861
5862 if (astate->scalarstate)
5863 {
5864 /* Must use makeMdArrayResult to support "release" parameter */
5865 int ndims;
5866 int dims[1];
5867 int lbs[1];
5868
5869 /* If no elements were presented, we want to create an empty array */
5870 ndims = (astate->scalarstate->nelems > 0) ? 1 : 0;
5871 dims[0] = astate->scalarstate->nelems;
5872 lbs[0] = 1;
5873
5874 result = makeMdArrayResult(astate->scalarstate, ndims, dims, lbs,
5875 rcontext, release);
5876 }
5877 else
5878 {
5879 result = makeArrayResultArr(astate->arraystate,
5880 rcontext, release);
5881 }
5882 return result;
5883}
5884
5885
5886Datum
5888{
5889 if (array_cmp(fcinfo) > 0)
5891 else
5893}
5894
5895Datum
5897{
5898 if (array_cmp(fcinfo) < 0)
5900 else
5902}
5903
5904
5906{
5911
5912/*
5913 * generate_subscripts(array anyarray, dim int [, reverse bool])
5914 * Returns all subscripts of the array for any dimension
5915 */
5916Datum
5918{
5919 FuncCallContext *funcctx;
5920 MemoryContext oldcontext;
5922
5923 /* stuff done only on the first call of the function */
5924 if (SRF_IS_FIRSTCALL())
5925 {
5927 int reqdim = PG_GETARG_INT32(1);
5928 int *lb,
5929 *dimv;
5930
5931 /* create a function context for cross-call persistence */
5932 funcctx = SRF_FIRSTCALL_INIT();
5933
5934 /* Sanity check: does it look like an array at all? */
5935 if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
5936 SRF_RETURN_DONE(funcctx);
5937
5938 /* Sanity check: was the requested dim valid */
5939 if (reqdim <= 0 || reqdim > AARR_NDIM(v))
5940 SRF_RETURN_DONE(funcctx);
5941
5942 /*
5943 * switch to memory context appropriate for multiple function calls
5944 */
5945 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
5947
5948 lb = AARR_LBOUND(v);
5949 dimv = AARR_DIMS(v);
5950
5951 fctx->lower = lb[reqdim - 1];
5952 fctx->upper = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
5953 fctx->reverse = (PG_NARGS() < 3) ? false : PG_GETARG_BOOL(2);
5954
5955 funcctx->user_fctx = fctx;
5956
5957 MemoryContextSwitchTo(oldcontext);
5958 }
5959
5960 funcctx = SRF_PERCALL_SETUP();
5961
5962 fctx = funcctx->user_fctx;
5963
5964 if (fctx->lower <= fctx->upper)
5965 {
5966 if (!fctx->reverse)
5967 SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->lower++));
5968 else
5969 SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->upper--));
5970 }
5971 else
5972 /* done when there are no more elements left */
5973 SRF_RETURN_DONE(funcctx);
5974}
5975
5976/*
5977 * generate_subscripts_nodir
5978 * Implements the 2-argument version of generate_subscripts
5979 */
5980Datum
5982{
5983 /* just call the other one -- it can handle both cases */
5984 return generate_subscripts(fcinfo);
5985}
5986
5987/*
5988 * array_fill_with_lower_bounds
5989 * Create and fill array with defined lower bounds.
5990 */
5991Datum
5993{
5994 ArrayType *dims;
5995 ArrayType *lbs;
5996 ArrayType *result;
5997 Oid elmtype;
5998 Datum value;
5999 bool isnull;
6000
6001 if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
6002 ereport(ERROR,
6003 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
6004 errmsg("dimension array or low bound array cannot be null")));
6005
6006 dims = PG_GETARG_ARRAYTYPE_P(1);
6007 lbs = PG_GETARG_ARRAYTYPE_P(2);
6008
6009 if (!PG_ARGISNULL(0))
6010 {
6012 isnull = false;
6013 }
6014 else
6015 {
6016 value = 0;
6017 isnull = true;
6018 }
6019
6020 elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
6021 if (!OidIsValid(elmtype))
6022 elog(ERROR, "could not determine data type of input");
6023
6024 result = array_fill_internal(dims, lbs, value, isnull, elmtype, fcinfo);
6025 PG_RETURN_ARRAYTYPE_P(result);
6026}
6027
6028/*
6029 * array_fill
6030 * Create and fill array with default lower bounds.
6031 */
6032Datum
6034{
6035 ArrayType *dims;
6036 ArrayType *result;
6037 Oid elmtype;
6038 Datum value;
6039 bool isnull;
6040
6041 if (PG_ARGISNULL(1))
6042 ereport(ERROR,
6043 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
6044 errmsg("dimension array or low bound array cannot be null")));
6045
6046 dims = PG_GETARG_ARRAYTYPE_P(1);
6047
6048 if (!PG_ARGISNULL(0))
6049 {
6051 isnull = false;
6052 }
6053 else
6054 {
6055 value = 0;
6056 isnull = true;
6057 }
6058
6059 elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
6060 if (!OidIsValid(elmtype))
6061 elog(ERROR, "could not determine data type of input");
6062
6063 result = array_fill_internal(dims, NULL, value, isnull, elmtype, fcinfo);
6064 PG_RETURN_ARRAYTYPE_P(result);
6065}
6066
6067static ArrayType *
6068create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
6069 Oid elmtype, int dataoffset)
6070{
6071 ArrayType *result;
6072
6073 result = (ArrayType *) palloc0(nbytes);
6074 SET_VARSIZE(result, nbytes);
6075 result->ndim = ndims;
6076 result->dataoffset = dataoffset;
6077 result->elemtype = elmtype;
6078 memcpy(ARR_DIMS(result), dimv, ndims * sizeof(int));
6079 memcpy(ARR_LBOUND(result), lbsv, ndims * sizeof(int));
6080
6081 return result;
6082}
6083
6084static ArrayType *
6086 Datum value, bool isnull, Oid elmtype,
6087 FunctionCallInfo fcinfo)
6088{
6089 ArrayType *result;
6090 int *dimv;
6091 int *lbsv;
6092 int ndims;
6093 int nitems;
6094 int deflbs[MAXDIM];
6095 int16 elmlen;
6096 bool elmbyval;
6097 char elmalign;
6098 ArrayMetaState *my_extra;
6099
6100 /*
6101 * Params checks
6102 */
6103 if (ARR_NDIM(dims) > 1)
6104 ereport(ERROR,
6105 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
6106 errmsg("wrong number of array subscripts"),
6107 errdetail("Dimension array must be one dimensional.")));
6108
6109 if (array_contains_nulls(dims))
6110 ereport(ERROR,
6111 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
6112 errmsg("dimension values cannot be null")));
6113
6114 dimv = (int *) ARR_DATA_PTR(dims);
6115 ndims = (ARR_NDIM(dims) > 0) ? ARR_DIMS(dims)[0] : 0;
6116
6117 if (ndims < 0) /* we do allow zero-dimension arrays */
6118 ereport(ERROR,
6119 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
6120 errmsg("invalid number of dimensions: %d", ndims)));
6121 if (ndims > MAXDIM)
6122 ereport(ERROR,
6123 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
6124 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
6125 ndims, MAXDIM)));
6126
6127 if (lbs != NULL)
6128 {
6129 if (ARR_NDIM(lbs) > 1)
6130 ereport(ERROR,
6131 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
6132 errmsg("wrong number of array subscripts"),
6133 errdetail("Dimension array must be one dimensional.")));
6134
6135 if (array_contains_nulls(lbs))
6136 ereport(ERROR,
6137 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
6138 errmsg("dimension values cannot be null")));
6139
6140 if (ndims != ((ARR_NDIM(lbs) > 0) ? ARR_DIMS(lbs)[0] : 0))
6141 ereport(ERROR,
6142 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
6143 errmsg("wrong number of array subscripts"),
6144 errdetail("Low bound array has different size than dimensions array.")));
6145
6146 lbsv = (int *) ARR_DATA_PTR(lbs);
6147 }
6148 else
6149 {
6150 int i;
6151
6152 for (i = 0; i < MAXDIM; i++)
6153 deflbs[i] = 1;
6154
6155 lbsv = deflbs;
6156 }
6157
6158 /* This checks for overflow of the array dimensions */
6159 nitems = ArrayGetNItems(ndims, dimv);
6160 ArrayCheckBounds(ndims, dimv, lbsv);
6161
6162 /* fast track for empty array */
6163 if (nitems <= 0)
6164 return construct_empty_array(elmtype);
6165
6166 /*
6167 * We arrange to look up info about element type only once per series of
6168 * calls, assuming the element type doesn't change underneath us.
6169 */
6170 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
6171 if (my_extra == NULL)
6172 {
6173 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
6174 sizeof(ArrayMetaState));
6175 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
6176 my_extra->element_type = InvalidOid;
6177 }
6178
6179 if (my_extra->element_type != elmtype)
6180 {
6181 /* Get info about element type */
6182 get_typlenbyvalalign(elmtype,
6183 &my_extra->typlen,
6184 &my_extra->typbyval,
6185 &my_extra->typalign);
6186 my_extra->element_type = elmtype;
6187 }
6188
6189 elmlen = my_extra->typlen;
6190 elmbyval = my_extra->typbyval;
6191 elmalign = my_extra->typalign;
6192
6193 /* compute required space */
6194 if (!isnull)
6195 {
6196 int i;
6197 char *p;
6198 int nbytes;
6199 int totbytes;
6200
6201 /* make sure data is not toasted */
6202 if (elmlen == -1)
6204
6205 nbytes = att_addlength_datum(0, elmlen, value);
6206 nbytes = att_align_nominal(nbytes, elmalign);
6207 Assert(nbytes > 0);
6208
6209 totbytes = nbytes * nitems;
6210
6211 /* check for overflow of multiplication or total request */
6212 if (totbytes / nbytes != nitems ||
6213 !AllocSizeIsValid(totbytes))
6214 ereport(ERROR,
6215 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
6216 errmsg("array size exceeds the maximum allowed (%d)",
6217 (int) MaxAllocSize)));
6218
6219 /*
6220 * This addition can't overflow, but it might cause us to go past
6221 * MaxAllocSize. We leave it to palloc to complain in that case.
6222 */
6223 totbytes += ARR_OVERHEAD_NONULLS(ndims);
6224
6225 result = create_array_envelope(ndims, dimv, lbsv, totbytes,
6226 elmtype, 0);
6227
6228 p = ARR_DATA_PTR(result);
6229 for (i = 0; i < nitems; i++)
6230 p += ArrayCastAndSet(value, elmlen, elmbyval, elmalign, p);
6231 }
6232 else
6233 {
6234 int nbytes;
6235 int dataoffset;
6236
6237 dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
6238 nbytes = dataoffset;
6239
6240 result = create_array_envelope(ndims, dimv, lbsv, nbytes,
6241 elmtype, dataoffset);
6242
6243 /* create_array_envelope already zeroed the bitmap, so we're done */
6244 }
6245
6246 return result;
6247}
6248
6249
6250/*
6251 * UNNEST
6252 */
6253Datum
6255{
6256 typedef struct
6257 {
6258 array_iter iter;
6259 int nextelem;
6260 int numelems;
6261 int16 elmlen;
6262 bool elmbyval;
6263 char elmalign;
6264 } array_unnest_fctx;
6265
6266 FuncCallContext *funcctx;
6267 array_unnest_fctx *fctx;
6268 MemoryContext oldcontext;
6269
6270 /* stuff done only on the first call of the function */
6271 if (SRF_IS_FIRSTCALL())
6272 {
6273 AnyArrayType *arr;
6274
6275 /* create a function context for cross-call persistence */
6276 funcctx = SRF_FIRSTCALL_INIT();
6277
6278 /*
6279 * switch to memory context appropriate for multiple function calls
6280 */
6281 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
6282
6283 /*
6284 * Get the array value and detoast if needed. We can't do this
6285 * earlier because if we have to detoast, we want the detoasted copy
6286 * to be in multi_call_memory_ctx, so it will go away when we're done
6287 * and not before. (If no detoast happens, we assume the originally
6288 * passed array will stick around till then.)
6289 */
6290 arr = PG_GETARG_ANY_ARRAY_P(0);
6291
6292 /* allocate memory for user context */
6293 fctx = (array_unnest_fctx *) palloc(sizeof(array_unnest_fctx));
6294
6295 /* initialize state */
6296 array_iter_setup(&fctx->iter, arr);
6297 fctx->nextelem = 0;
6298 fctx->numelems = ArrayGetNItems(AARR_NDIM(arr), AARR_DIMS(arr));
6299
6301 {
6302 /* we can just grab the type data from expanded array */
6303 fctx->elmlen = arr->xpn.typlen;
6304 fctx->elmbyval = arr->xpn.typbyval;
6305 fctx->elmalign = arr->xpn.typalign;
6306 }
6307 else
6309 &fctx->elmlen,
6310 &fctx->elmbyval,
6311 &fctx->elmalign);
6312
6313 funcctx->user_fctx = fctx;
6314 MemoryContextSwitchTo(oldcontext);
6315 }
6316
6317 /* stuff done on every call of the function */
6318 funcctx = SRF_PERCALL_SETUP();
6319 fctx = funcctx->user_fctx;
6320
6321 if (fctx->nextelem < fctx->numelems)
6322 {
6323 int offset = fctx->nextelem++;
6324 Datum elem;
6325
6326 elem = array_iter_next(&fctx->iter, &fcinfo->isnull, offset,
6327 fctx->elmlen, fctx->elmbyval, fctx->elmalign);
6328
6329 SRF_RETURN_NEXT(funcctx, elem);
6330 }
6331 else
6332 {
6333 /* do when there is no more left */
6334 SRF_RETURN_DONE(funcctx);
6335 }
6336}
6337
6338/*
6339 * Planner support function for array_unnest(anyarray)
6340 *
6341 * Note: this is now also used for information_schema._pg_expandarray(),
6342 * which is simply a wrapper around array_unnest().
6343 */
6344Datum
6346{
6347 Node *rawreq = (Node *) PG_GETARG_POINTER(0);
6348 Node *ret = NULL;
6349
6350 if (IsA(rawreq, SupportRequestRows))
6351 {
6352 /* Try to estimate the number of rows returned */
6353 SupportRequestRows *req = (SupportRequestRows *) rawreq;
6354
6355 if (is_funcclause(req->node)) /* be paranoid */
6356 {
6357 List *args = ((FuncExpr *) req->node)->args;
6358 Node *arg1;
6359
6360 /* We can use estimated argument values here */
6362
6363 req->rows = estimate_array_length(req->root, arg1);
6364 ret = (Node *) req;
6365 }
6366 }
6367
6368 PG_RETURN_POINTER(ret);
6369}
6370
6371
6372/*
6373 * array_replace/array_remove support
6374 *
6375 * Find all array entries matching (not distinct from) search/search_isnull,
6376 * and delete them if remove is true, else replace them with
6377 * replace/replace_isnull. Comparisons are done using the specified
6378 * collation. fcinfo is passed only for caching purposes.
6379 */
6380static ArrayType *
6382 Datum search, bool search_isnull,
6383 Datum replace, bool replace_isnull,
6384 bool remove, Oid collation,
6385 FunctionCallInfo fcinfo)
6386{
6387 LOCAL_FCINFO(locfcinfo, 2);
6388 ArrayType *result;
6389 Oid element_type;
6390 Datum *values;
6391 bool *nulls;
6392 int *dim;
6393 int ndim;
6394 int nitems,
6395 nresult;
6396 int i;
6397 int32 nbytes = 0;
6398 int32 dataoffset;
6399 bool hasnulls;
6400 int typlen;
6401 bool typbyval;
6402 char typalign;
6403 char *arraydataptr;
6404 bits8 *bitmap;
6405 int bitmask;
6406 bool changed = false;
6407 TypeCacheEntry *typentry;
6408
6409 element_type = ARR_ELEMTYPE(array);
6410 ndim = ARR_NDIM(array);
6411 dim = ARR_DIMS(array);
6412 nitems = ArrayGetNItems(ndim, dim);
6413
6414 /* Return input array unmodified if it is empty */
6415 if (nitems <= 0)
6416 return array;
6417
6418 /*
6419 * We can't remove elements from multi-dimensional arrays, since the
6420 * result might not be rectangular.
6421 */
6422 if (remove && ndim > 1)
6423 ereport(ERROR,
6424 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6425 errmsg("removing elements from multidimensional arrays is not supported")));
6426
6427 /*
6428 * We arrange to look up the equality function only once per series of
6429 * calls, assuming the element type doesn't change underneath us.
6430 */
6431 typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
6432 if (typentry == NULL ||
6433 typentry->type_id != element_type)
6434 {
6435 typentry = lookup_type_cache(element_type,
6437 if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
6438 ereport(ERROR,
6439 (errcode(ERRCODE_UNDEFINED_FUNCTION),
6440 errmsg("could not identify an equality operator for type %s",
6441 format_type_be(element_type))));
6442 fcinfo->flinfo->fn_extra = typentry;
6443 }
6444 typlen = typentry->typlen;
6445 typbyval = typentry->typbyval;
6446 typalign = typentry->typalign;
6447
6448 /*
6449 * Detoast values if they are toasted. The replacement value must be
6450 * detoasted for insertion into the result array, while detoasting the
6451 * search value only once saves cycles.
6452 */
6453 if (typlen == -1)
6454 {
6455 if (!search_isnull)
6456 search = PointerGetDatum(PG_DETOAST_DATUM(search));
6457 if (!replace_isnull)
6458 replace = PointerGetDatum(PG_DETOAST_DATUM(replace));
6459 }
6460
6461 /* Prepare to apply the comparison operator */
6462 InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
6463 collation, NULL, NULL);
6464
6465 /* Allocate temporary arrays for new values */
6466 values = (Datum *) palloc(nitems * sizeof(Datum));
6467 nulls = (bool *) palloc(nitems * sizeof(bool));
6468
6469 /* Loop over source data */
6470 arraydataptr = ARR_DATA_PTR(array);
6471 bitmap = ARR_NULLBITMAP(array);
6472 bitmask = 1;
6473 hasnulls = false;
6474 nresult = 0;
6475
6476 for (i = 0; i < nitems; i++)
6477 {
6478 Datum elt;
6479 bool isNull;
6480 bool oprresult;
6481 bool skip = false;
6482
6483 /* Get source element, checking for NULL */
6484 if (bitmap && (*bitmap & bitmask) == 0)
6485 {
6486 isNull = true;
6487 /* If searching for NULL, we have a match */
6488 if (search_isnull)
6489 {
6490 if (remove)
6491 {
6492 skip = true;
6493 changed = true;
6494 }
6495 else if (!replace_isnull)
6496 {
6497 values[nresult] = replace;
6498 isNull = false;
6499 changed = true;
6500 }
6501 }
6502 }
6503 else
6504 {
6505 isNull = false;
6506 elt = fetch_att(arraydataptr, typbyval, typlen);
6507 arraydataptr = att_addlength_datum(arraydataptr, typlen, elt);
6508 arraydataptr = (char *) att_align_nominal(arraydataptr, typalign);
6509
6510 if (search_isnull)
6511 {
6512 /* no match possible, keep element */
6513 values[nresult] = elt;
6514 }
6515 else
6516 {
6517 /*
6518 * Apply the operator to the element pair; treat NULL as false
6519 */
6520 locfcinfo->args[0].value = elt;
6521 locfcinfo->args[0].isnull = false;
6522 locfcinfo->args[1].value = search;
6523 locfcinfo->args[1].isnull = false;
6524 locfcinfo->isnull = false;
6525 oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
6526 if (locfcinfo->isnull || !oprresult)
6527 {
6528 /* no match, keep element */
6529 values[nresult] = elt;
6530 }
6531 else
6532 {
6533 /* match, so replace or delete */
6534 changed = true;
6535 if (remove)
6536 skip = true;
6537 else
6538 {
6539 values[nresult] = replace;
6540 isNull = replace_isnull;
6541 }
6542 }
6543 }
6544 }
6545
6546 if (!skip)
6547 {
6548 nulls[nresult] = isNull;
6549 if (isNull)
6550 hasnulls = true;
6551 else
6552 {
6553 /* Update total result size */
6554 nbytes = att_addlength_datum(nbytes, typlen, values[nresult]);
6555 nbytes = att_align_nominal(nbytes, typalign);
6556 /* check for overflow of total request */
6557 if (!AllocSizeIsValid(nbytes))
6558 ereport(ERROR,
6559 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
6560 errmsg("array size exceeds the maximum allowed (%d)",
6561 (int) MaxAllocSize)));
6562 }
6563 nresult++;
6564 }
6565
6566 /* advance bitmap pointer if any */
6567 if (bitmap)
6568 {
6569 bitmask <<= 1;
6570 if (bitmask == 0x100)
6571 {
6572 bitmap++;
6573 bitmask = 1;
6574 }
6575 }
6576 }
6577
6578 /*
6579 * If not changed just return the original array
6580 */
6581 if (!changed)
6582 {
6583 pfree(values);
6584 pfree(nulls);
6585 return array;
6586 }
6587
6588 /* If all elements were removed return an empty array */
6589 if (nresult == 0)
6590 {
6591 pfree(values);
6592 pfree(nulls);
6593 return construct_empty_array(element_type);
6594 }
6595
6596 /* Allocate and initialize the result array */
6597 if (hasnulls)
6598 {
6599 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nresult);
6600 nbytes += dataoffset;
6601 }
6602 else
6603 {
6604 dataoffset = 0; /* marker for no null bitmap */
6605 nbytes += ARR_OVERHEAD_NONULLS(ndim);
6606 }
6607 result = (ArrayType *) palloc0(nbytes);
6608 SET_VARSIZE(result, nbytes);
6609 result->ndim = ndim;
6610 result->dataoffset = dataoffset;
6611 result->elemtype = element_type;
6612 memcpy(ARR_DIMS(result), ARR_DIMS(array), ndim * sizeof(int));
6613 memcpy(ARR_LBOUND(result), ARR_LBOUND(array), ndim * sizeof(int));
6614
6615 if (remove)
6616 {
6617 /* Adjust the result length */
6618 ARR_DIMS(result)[0] = nresult;
6619 }
6620
6621 /* Insert data into result array */
6622 CopyArrayEls(result,
6623 values, nulls, nresult,
6624 typlen, typbyval, typalign,
6625 false);
6626
6627 pfree(values);
6628 pfree(nulls);
6629
6630 return result;
6631}
6632
6633/*
6634 * Remove any occurrences of an element from an array
6635 *
6636 * If used on a multi-dimensional array this will raise an error.
6637 */
6638Datum
6640{
6641 ArrayType *array;
6642 Datum search = PG_GETARG_DATUM(1);
6643 bool search_isnull = PG_ARGISNULL(1);
6644
6645 if (PG_ARGISNULL(0))
6647 array = PG_GETARG_ARRAYTYPE_P(0);
6648
6649 array = array_replace_internal(array,
6650 search, search_isnull,
6651 (Datum) 0, true,
6652 true, PG_GET_COLLATION(),
6653 fcinfo);
6654 PG_RETURN_ARRAYTYPE_P(array);
6655}
6656
6657/*
6658 * Replace any occurrences of an element in an array
6659 */
6660Datum
6662{
6663 ArrayType *array;
6664 Datum search = PG_GETARG_DATUM(1);
6665 bool search_isnull = PG_ARGISNULL(1);
6666 Datum replace = PG_GETARG_DATUM(2);
6667 bool replace_isnull = PG_ARGISNULL(2);
6668
6669 if (PG_ARGISNULL(0))
6671 array = PG_GETARG_ARRAYTYPE_P(0);
6672
6673 array = array_replace_internal(array,
6674 search, search_isnull,
6675 replace, replace_isnull,
6676 false, PG_GET_COLLATION(),
6677 fcinfo);
6678 PG_RETURN_ARRAYTYPE_P(array);
6679}
6680
6681/*
6682 * Implements width_bucket(anyelement, anyarray).
6683 *
6684 * 'thresholds' is an array containing lower bound values for each bucket;
6685 * these must be sorted from smallest to largest, or bogus results will be
6686 * produced. If N thresholds are supplied, the output is from 0 to N:
6687 * 0 is for inputs < first threshold, N is for inputs >= last threshold.
6688 */
6689Datum
6691{
6692 Datum operand = PG_GETARG_DATUM(0);
6693 ArrayType *thresholds = PG_GETARG_ARRAYTYPE_P(1);
6694 Oid collation = PG_GET_COLLATION();
6695 Oid element_type = ARR_ELEMTYPE(thresholds);
6696 int result;
6697
6698 /* Check input */
6699 if (ARR_NDIM(thresholds) > 1)
6700 ereport(ERROR,
6701 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
6702 errmsg("thresholds must be one-dimensional array")));
6703
6704 if (array_contains_nulls(thresholds))
6705 ereport(ERROR,
6706 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
6707 errmsg("thresholds array must not contain NULLs")));
6708
6709 /* We have a dedicated implementation for float8 data */
6710 if (element_type == FLOAT8OID)
6711 result = width_bucket_array_float8(operand, thresholds);
6712 else
6713 {
6714 TypeCacheEntry *typentry;
6715
6716 /* Cache information about the input type */
6717 typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
6718 if (typentry == NULL ||
6719 typentry->type_id != element_type)
6720 {
6721 typentry = lookup_type_cache(element_type,
6723 if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
6724 ereport(ERROR,
6725 (errcode(ERRCODE_UNDEFINED_FUNCTION),
6726 errmsg("could not identify a comparison function for type %s",
6727 format_type_be(element_type))));
6728 fcinfo->flinfo->fn_extra = typentry;
6729 }
6730
6731 /*
6732 * We have separate implementation paths for fixed- and variable-width
6733 * types, since indexing the array is a lot cheaper in the first case.
6734 */
6735 if (typentry->typlen > 0)
6736 result = width_bucket_array_fixed(operand, thresholds,
6737 collation, typentry);
6738 else
6739 result = width_bucket_array_variable(operand, thresholds,
6740 collation, typentry);
6741 }
6742
6743 /* Avoid leaking memory when handed toasted input. */
6744 PG_FREE_IF_COPY(thresholds, 1);
6745
6746 PG_RETURN_INT32(result);
6747}
6748
6749/*
6750 * width_bucket_array for float8 data.
6751 */
6752static int
6754{
6755 float8 op = DatumGetFloat8(operand);
6756 float8 *thresholds_data;
6757 int left;
6758 int right;
6759
6760 /*
6761 * Since we know the array contains no NULLs, we can just index it
6762 * directly.
6763 */
6764 thresholds_data = (float8 *) ARR_DATA_PTR(thresholds);
6765
6766 left = 0;
6767 right = ArrayGetNItems(ARR_NDIM(thresholds), ARR_DIMS(thresholds));
6768
6769 /*
6770 * If the probe value is a NaN, it's greater than or equal to all possible
6771 * threshold values (including other NaNs), so we need not search. Note
6772 * that this would give the same result as searching even if the array
6773 * contains multiple NaNs (as long as they're correctly sorted), since the
6774 * loop logic will find the rightmost of multiple equal threshold values.
6775 */
6776 if (isnan(op))
6777 return right;
6778
6779 /* Find the bucket */
6780 while (left < right)
6781 {
6782 int mid = (left + right) / 2;
6783
6784 if (isnan(thresholds_data[mid]) || op < thresholds_data[mid])
6785 right = mid;
6786 else
6787 left = mid + 1;
6788 }
6789
6790 return left;
6791}
6792
6793/*
6794 * width_bucket_array for generic fixed-width data types.
6795 */
6796static int
6798 ArrayType *thresholds,
6799 Oid collation,
6800 TypeCacheEntry *typentry)
6801{
6802 LOCAL_FCINFO(locfcinfo, 2);
6803 char *thresholds_data;
6804 int typlen = typentry->typlen;
6805 bool typbyval = typentry->typbyval;
6806 int left;
6807 int right;
6808
6809 /*
6810 * Since we know the array contains no NULLs, we can just index it
6811 * directly.
6812 */
6813 thresholds_data = (char *) ARR_DATA_PTR(thresholds);
6814
6815 InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
6816 collation, NULL, NULL);
6817
6818 /* Find the bucket */
6819 left = 0;
6820 right = ArrayGetNItems(ARR_NDIM(thresholds), ARR_DIMS(thresholds));
6821 while (left < right)
6822 {
6823 int mid = (left + right) / 2;
6824 char *ptr;
6825 int32 cmpresult;
6826
6827 ptr = thresholds_data + mid * typlen;
6828
6829 locfcinfo->args[0].value = operand;
6830 locfcinfo->args[0].isnull = false;
6831 locfcinfo->args[1].value = fetch_att(ptr, typbyval, typlen);
6832 locfcinfo->args[1].isnull = false;
6833
6834 cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
6835
6836 /* We don't expect comparison support functions to return null */
6837 Assert(!locfcinfo->isnull);
6838
6839 if (cmpresult < 0)
6840 right = mid;
6841 else
6842 left = mid + 1;
6843 }
6844
6845 return left;
6846}
6847
6848/*
6849 * width_bucket_array for generic variable-width data types.
6850 */
6851static int
6853 ArrayType *thresholds,
6854 Oid collation,
6855 TypeCacheEntry *typentry)
6856{
6857 LOCAL_FCINFO(locfcinfo, 2);
6858 char *thresholds_data;
6859 int typlen = typentry->typlen;
6860 bool typbyval = typentry->typbyval;
6861 char typalign = typentry->typalign;
6862 int left;
6863 int right;
6864
6865 thresholds_data = (char *) ARR_DATA_PTR(thresholds);
6866
6867 InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
6868 collation, NULL, NULL);
6869
6870 /* Find the bucket */
6871 left = 0;
6872 right = ArrayGetNItems(ARR_NDIM(thresholds), ARR_DIMS(thresholds));
6873 while (left < right)
6874 {
6875 int mid = (left + right) / 2;
6876 char *ptr;
6877 int i;
6878 int32 cmpresult;
6879
6880 /* Locate mid'th array element by advancing from left element */
6881 ptr = thresholds_data;
6882 for (i = left; i < mid; i++)
6883 {
6884 ptr = att_addlength_pointer(ptr, typlen, ptr);
6885 ptr = (char *) att_align_nominal(ptr, typalign);
6886 }
6887
6888 locfcinfo->args[0].value = operand;
6889 locfcinfo->args[0].isnull = false;
6890 locfcinfo->args[1].value = fetch_att(ptr, typbyval, typlen);
6891 locfcinfo->args[1].isnull = false;
6892
6893 cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
6894
6895 /* We don't expect comparison support functions to return null */
6896 Assert(!locfcinfo->isnull);
6897
6898 if (cmpresult < 0)
6899 right = mid;
6900 else
6901 {
6902 left = mid + 1;
6903
6904 /*
6905 * Move the thresholds pointer to match new "left" index, so we
6906 * don't have to seek over those elements again. This trick
6907 * ensures we do only O(N) array indexing work, not O(N^2).
6908 */
6909 ptr = att_addlength_pointer(ptr, typlen, ptr);
6910 thresholds_data = (char *) att_align_nominal(ptr, typalign);
6911 }
6912 }
6913
6914 return left;
6915}
6916
6917/*
6918 * Trim the last N elements from an array by building an appropriate slice.
6919 * Only the first dimension is trimmed.
6920 */
6921Datum
6923{
6925 int n = PG_GETARG_INT32(1);
6926 int array_length = (ARR_NDIM(v) > 0) ? ARR_DIMS(v)[0] : 0;
6927 int16 elmlen;
6928 bool elmbyval;
6929 char elmalign;
6930 int lower[MAXDIM];
6931 int upper[MAXDIM];
6932 bool lowerProvided[MAXDIM];
6933 bool upperProvided[MAXDIM];
6934 Datum result;
6935
6936 /* Per spec, throw an error if out of bounds */
6937 if (n < 0 || n > array_length)
6938 ereport(ERROR,
6939 (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
6940 errmsg("number of elements to trim must be between 0 and %d",
6941 array_length)));
6942
6943 /* Set all the bounds as unprovided except the first upper bound */
6944 memset(lowerProvided, false, sizeof(lowerProvided));
6945 memset(upperProvided, false, sizeof(upperProvided));
6946 if (ARR_NDIM(v) > 0)
6947 {
6948 upper[0] = ARR_LBOUND(v)[0] + array_length - n - 1;
6949 upperProvided[0] = true;
6950 }
6951
6952 /* Fetch the needed information about the element type */
6953 get_typlenbyvalalign(ARR_ELEMTYPE(v), &elmlen, &elmbyval, &elmalign);
6954
6955 /* Get the slice */
6956 result = array_get_slice(PointerGetDatum(v), 1,
6957 upper, lower, upperProvided, lowerProvided,
6958 -1, elmlen, elmbyval, elmalign);
6959
6960 PG_RETURN_DATUM(result);
6961}
#define AARR_DIMS(a)
Definition: array.h:338
#define AARR_ELEMTYPE(a)
Definition: array.h:335
#define PG_GETARG_ANY_ARRAY_P(n)
Definition: array.h:274
#define AARR_LBOUND(a)
Definition: array.h:341
#define ARR_NDIM(a)
Definition: array.h:290
#define PG_GETARG_ARRAYTYPE_P(n)
Definition: array.h:263
#define ARR_DATA_PTR(a)
Definition: array.h:322
#define MAXDIM
Definition: array.h:75
#define AARR_NDIM(a)
Definition: array.h:328
#define ARR_NULLBITMAP(a)
Definition: array.h:300
#define MaxArraySize
Definition: array.h:82
#define ARR_OVERHEAD_WITHNULLS(ndims, nitems)
Definition: array.h:312
#define DatumGetArrayTypeP(X)
Definition: array.h:261
#define ARR_ELEMTYPE(a)
Definition: array.h:292
#define PG_RETURN_ARRAYTYPE_P(x)
Definition: array.h:265
#define AARR_HASNULL(a)
Definition: array.h:331
#define ARR_SIZE(a)
Definition: array.h:289
#define ARR_OVERHEAD_NONULLS(ndims)
Definition: array.h:310
#define ARR_DATA_OFFSET(a)
Definition: array.h:316
#define ARR_DIMS(a)
Definition: array.h:294
#define ARR_HASNULL(a)
Definition: array.h:291
#define ARR_LBOUND(a)
Definition: array.h:296
#define EA_MAGIC
Definition: array.h:113
Datum expand_array(Datum arraydatum, MemoryContext parentcontext, ArrayMetaState *metacache)
void deconstruct_expanded_array(ExpandedArrayHeader *eah)
ExpandedArrayHeader * DatumGetExpandedArray(Datum d)
AnyArrayType * DatumGetAnyArrayP(Datum d)
static Datum array_iter_next(array_iter *it, bool *isnull, int i, int elmlen, bool elmbyval, char elmalign)
Definition: arrayaccess.h:81
static void array_iter_setup(array_iter *it, AnyArrayType *a)
Definition: arrayaccess.h:49
Datum array_eq(PG_FUNCTION_ARGS)
Definition: arrayfuncs.c:3814
Datum arraycontained(PG_FUNCTION_ARGS)
Definition: arrayfuncs.c:4560
Datum array_lt(PG_FUNCTION_ARGS)
Definition: arrayfuncs.c:3949
ArrayType * array_set(ArrayType *array, int nSubscripts, int *indx, Datum dataValue, bool isNull, int arraytyplen, int elmlen, bool elmbyval, char elmalign)
Definition: arrayfuncs.c:3163
static bool ReadArrayStr(char **srcptr, FmgrInfo *inputproc, Oid typioparam, int32 typmod, char typdelim, int typlen, bool typbyval, char typalign, int *ndim_p, int *dim, int *nitems_p, Datum **values_p, bool **nulls_p, const char *origStr, Node *escontext)
Definition: arrayfuncs.c:579
Datum array_fill(PG_FUNCTION_ARGS)
Definition: arrayfuncs.c:6033
bool array_contains_nulls(ArrayType *array)
Definition: arrayfuncs.c:3767
Datum array_replace(PG_FUNCTION_ARGS)
Definition: arrayfuncs.c:6661
static ArrayType * array_replace_internal(ArrayType *array, Datum search, bool search_isnull, Datum replace, bool replace_isnull, bool remove, Oid collation, FunctionCallInfo fcinfo)
Definition: arrayfuncs.c:6381
Datum array_smaller(PG_FUNCTION_ARGS)
Definition: arrayfuncs.c:5896
ArrayBuildState * accumArrayResult(ArrayBuildState *astate, Datum dvalue, bool disnull, Oid element_type, MemoryContext rcontext)
Definition: arrayfuncs.c:5350
static bool ReadArrayDimensions(char **srcptr, int *ndim_p, int *dim, int *lBound, const char *origStr, Node *escontext)
Definition: arrayfuncs.c:402
Datum array_cardinality(PG_FUNCTION_ARGS)
Definition: arrayfuncs.c:1790
ExpandedArrayHeader * construct_empty_expanded_array(Oid element_type, MemoryContext parentcontext, ArrayMetaState *metacache)
Definition: arrayfuncs.c:3597
bool array_iterate(ArrayIterator iterator, Datum *value, bool *isnull)
Definition: arrayfuncs.c:4676
static Datum array_set_element_expanded(Datum arraydatum, int nSubscripts, int *indx, Datum dataValue, bool isNull, int arraytyplen, int elmlen, bool elmbyval, char elmalign)
Definition: arrayfuncs.c:2501
void array_free_iterator(ArrayIterator iterator)
Definition: arrayfuncs.c:4759
Datum array_recv(PG_FUNCTION_ARGS)
Definition: arrayfuncs.c:1271
Datum generate_subscripts_nodir(PG_FUNCTION_ARGS)
Definition: arrayfuncs.c:5981
Datum hash_array(PG_FUNCTION_ARGS)
Definition: arrayfuncs.c:4158
static int width_bucket_array_float8(Datum operand, ArrayType *thresholds)
Definition: arrayfuncs.c:6753
ArrayBuildStateAny * initArrayResultAny(Oid input_type, MemoryContext rcontext, bool subcontext)
Definition: arrayfuncs.c:5782
ArrayBuildStateAny * accumArrayResultAny(ArrayBuildStateAny *astate, Datum dvalue, bool disnull, Oid input_type, MemoryContext rcontext)
Definition: arrayfuncs.c:5829
ArrayType * construct_empty_array(Oid elmtype)
Definition: arrayfuncs.c:3580
void CopyArrayEls(ArrayType *array, Datum *values, bool *nulls, int nitems, int typlen, bool typbyval, char typalign, bool freedata)
Definition: arrayfuncs.c:961
Datum array_dims(PG_FUNCTION_ARGS)
Definition: arrayfuncs.c:1668
#define APPENDSTR(str)
static ArrayToken ReadArrayToken(char **srcptr, StringInfo elembuf, char typdelim, const char *origStr, Node *escontext)
Definition: arrayfuncs.c:796
Datum arraycontains(PG_FUNCTION_ARGS)
Definition: arrayfuncs.c:4542
static int array_slice_size(char *arraydataptr, bits8 *arraynullsptr, int ndim, int *dim, int *lb, int *st, int *endp, int typlen, bool typbyval, char typalign)
Definition: arrayfuncs.c:5037
static bool array_get_isnull(const bits8 *nullbitmap, int offset)
Definition: arrayfuncs.c:4781
Datum makeArrayResultArr(ArrayBuildStateArr *astate, MemoryContext rcontext, bool release)
Definition: arrayfuncs.c:5703
#define AARR_FREE_IF_COPY(array, n)
Definition: arrayfuncs.c:50
Datum array_upper(PG_FUNCTION_ARGS)
Definition: arrayfuncs.c:1733
Datum makeArrayResultAny(ArrayBuildStateAny *astate, MemoryContext rcontext, bool release)
Definition: arrayfuncs.c:5857
static ArrayType * array_fill_internal(ArrayType *dims, ArrayType *lbs, Datum value, bool isnull, Oid elmtype, FunctionCallInfo fcinfo)
Definition: arrayfuncs.c:6085
ArrayType * construct_array(Datum *elems, int nelems, Oid elmtype, int elmlen, bool elmbyval, char elmalign)
Definition: arrayfuncs.c:3361
Datum array_map(Datum arrayd, ExprState *exprstate, ExprContext *econtext, Oid retType, ArrayMapState *amstate)
Definition: arrayfuncs.c:3201
Datum array_set_element(Datum arraydatum, int nSubscripts, int *indx, Datum dataValue, bool isNull, int arraytyplen, int elmlen, bool elmbyval, char elmalign)
Definition: arrayfuncs.c:2201
Datum makeMdArrayResult(ArrayBuildState *astate, int ndims, int *dims, int *lbs, MemoryContext rcontext, bool release)
Definition: arrayfuncs.c:5452
struct ArrayIteratorData ArrayIteratorData
static bool ReadDimensionInt(char **srcptr, int *result, const char *origStr, Node *escontext)
Definition: arrayfuncs.c:519
ArrayBuildStateArr * initArrayResultArr(Oid array_type, Oid element_type, MemoryContext rcontext, bool subcontext)
Definition: arrayfuncs.c:5504
static bool array_contain_compare(AnyArrayType *array1, AnyArrayType *array2, Oid collation, bool matchall, void **fn_extra)
Definition: arrayfuncs.c:4381
Datum array_lower(PG_FUNCTION_ARGS)
Definition: arrayfuncs.c:1706
static int width_bucket_array_fixed(Datum operand, ArrayType *thresholds, Oid collation, TypeCacheEntry *typentry)
Definition: arrayfuncs.c:6797
ArrayBuildStateArr * accumArrayResultArr(ArrayBuildStateArr *astate, Datum dvalue, bool disnull, Oid array_type, MemoryContext rcontext)
Definition: arrayfuncs.c:5550
static int array_cmp(FunctionCallInfo fcinfo)
Definition: arrayfuncs.c:3985
ArrayIterator array_create_iterator(ArrayType *arr, int slice_ndim, ArrayMetaState *mstate)
Definition: arrayfuncs.c:4597
Datum btarraycmp(PG_FUNCTION_ARGS)
Definition: arrayfuncs.c:3973
Datum array_ref(ArrayType *array, int nSubscripts, int *indx, int arraytyplen, int elmlen, bool elmbyval, char elmalign, bool *isNull)
Definition: arrayfuncs.c:3146
static void ReadArrayBinary(StringInfo buf, int nitems, FmgrInfo *receiveproc, Oid typioparam, int32 typmod, int typlen, bool typbyval, char typalign, Datum *values, bool *nulls, bool *hasnulls, int32 *nbytes)
Definition: arrayfuncs.c:1454
static Datum array_get_element_expanded(Datum arraydatum, int nSubscripts, int *indx, int arraytyplen, int elmlen, bool elmbyval, char elmalign, bool *isNull)
Definition: arrayfuncs.c:1921
ArrayBuildState * initArrayResultWithSize(Oid element_type, MemoryContext rcontext, bool subcontext, int initsize)
Definition: arrayfuncs.c:5310
void deconstruct_array(ArrayType *array, Oid elmtype, int elmlen, bool elmbyval, char elmalign, Datum **elemsp, bool **nullsp, int *nelemsp)
Definition: arrayfuncs.c:3631
Datum hash_array_extended(PG_FUNCTION_ARGS)
Definition: arrayfuncs.c:4291
void deconstruct_array_builtin(ArrayType *array, Oid elmtype, Datum **elemsp, bool **nullsp, int *nelemsp)
Definition: arrayfuncs.c:3697
Datum generate_subscripts(PG_FUNCTION_ARGS)
Definition: arrayfuncs.c:5917
static void array_insert_slice(ArrayType *destArray, ArrayType *origArray, ArrayType *srcArray, int ndim, int *dim, int *lb, int *st, int *endp, int typlen, bool typbyval, char typalign)
Definition: arrayfuncs.c:5170
Datum array_send(PG_FUNCTION_ARGS)
Definition: arrayfuncs.c:1548
Datum array_le(PG_FUNCTION_ARGS)
Definition: arrayfuncs.c:3961
ArrayType * construct_md_array(Datum *elems, bool *nulls, int ndims, int *dims, int *lbs, Oid elmtype, int elmlen, bool elmbyval, char elmalign)
Definition: arrayfuncs.c:3494
static int array_copy(char *destptr, int nitems, char *srcptr, int offset, bits8 *nullbitmap, int typlen, bool typbyval, char typalign)
Definition: arrayfuncs.c:4936
ArrayBuildState * initArrayResult(Oid element_type, MemoryContext rcontext, bool subcontext)
Definition: arrayfuncs.c:5293
Datum array_gt(PG_FUNCTION_ARGS)
Definition: arrayfuncs.c:3955
Datum array_unnest(PG_FUNCTION_ARGS)
Definition: arrayfuncs.c:6254
#define ASSGN
Definition: arrayfuncs.c:48
static ArrayType * create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes, Oid elmtype, int dataoffset)
Definition: arrayfuncs.c:6068
Datum array_remove(PG_FUNCTION_ARGS)
Definition: arrayfuncs.c:6639
Datum array_ne(PG_FUNCTION_ARGS)
Definition: arrayfuncs.c:3943
Datum array_larger(PG_FUNCTION_ARGS)
Definition: arrayfuncs.c:5887
Datum array_in(PG_FUNCTION_ARGS)
Definition: arrayfuncs.c:179
static void array_extract_slice(ArrayType *newarray, int ndim, int *dim, int *lb, char *arraydataptr, bits8 *arraynullsptr, int *st, int *endp, int typlen, bool typbyval, char typalign)
Definition: arrayfuncs.c:5097
ArrayType * construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
Definition: arrayfuncs.c:3381
static int width_bucket_array_variable(Datum operand, ArrayType *thresholds, Oid collation, TypeCacheEntry *typentry)
Definition: arrayfuncs.c:6852
Datum array_length(PG_FUNCTION_ARGS)
Definition: arrayfuncs.c:1763
Datum makeArrayResult(ArrayBuildState *astate, MemoryContext rcontext)
Definition: arrayfuncs.c:5420
static int array_nelems_size(char *ptr, int offset, bits8 *nullbitmap, int nitems, int typlen, bool typbyval, char typalign)
Definition: arrayfuncs.c:4914
static void array_set_isnull(bits8 *nullbitmap, int offset, bool isNull)
Definition: arrayfuncs.c:4798
#define APPENDCHAR(ch)
void array_bitmap_copy(bits8 *destbitmap, int destoffset, const bits8 *srcbitmap, int srcoffset, int nitems)
Definition: arrayfuncs.c:4966
static int ArrayCastAndSet(Datum src, int typlen, bool typbyval, char typalign, char *dest)
Definition: arrayfuncs.c:4827
static Datum ArrayCast(char *value, bool byval, int len)
Definition: arrayfuncs.c:4816
Datum array_get_element(Datum arraydatum, int nSubscripts, int *indx, int arraytyplen, int elmlen, bool elmbyval, char elmalign, bool *isNull)
Definition: arrayfuncs.c:1820
Datum array_out(PG_FUNCTION_ARGS)
Definition: arrayfuncs.c:1016
Datum array_fill_with_lower_bounds(PG_FUNCTION_ARGS)
Definition: arrayfuncs.c:5992
Datum array_ndims(PG_FUNCTION_ARGS)
Definition: arrayfuncs.c:1652
static char * array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems, int typlen, bool typbyval, char typalign)
Definition: arrayfuncs.c:4866
bool Array_nulls
Definition: arrayfuncs.c:43
Datum array_get_slice(Datum arraydatum, int nSubscripts, int *upperIndx, int *lowerIndx, bool *upperProvided, bool *lowerProvided, int arraytyplen, int elmlen, bool elmbyval, char elmalign)
Definition: arrayfuncs.c:2030
Datum array_ge(PG_FUNCTION_ARGS)
Definition: arrayfuncs.c:3967
Datum width_bucket_array(PG_FUNCTION_ARGS)
Definition: arrayfuncs.c:6690
Datum arrayoverlap(PG_FUNCTION_ARGS)
Definition: arrayfuncs.c:4524
Datum array_set_slice(Datum arraydatum, int nSubscripts, int *upperIndx, int *lowerIndx, bool *upperProvided, bool *lowerProvided, Datum srcArrayDatum, bool isNull, int arraytyplen, int elmlen, bool elmbyval, char elmalign)
Definition: arrayfuncs.c:2806
ArrayToken
Definition: arrayfuncs.c:58
@ ATOK_ELEM_NULL
Definition: arrayfuncs.c:63
@ ATOK_DELIM
Definition: arrayfuncs.c:61
@ ATOK_LEVEL_END
Definition: arrayfuncs.c:60
@ ATOK_ELEM
Definition: arrayfuncs.c:62
@ ATOK_LEVEL_START
Definition: arrayfuncs.c:59
@ ATOK_ERROR
Definition: arrayfuncs.c:64
Datum trim_array(PG_FUNCTION_ARGS)
Definition: arrayfuncs.c:6922
Datum array_unnest_support(PG_FUNCTION_ARGS)
Definition: arrayfuncs.c:6345
struct generate_subscripts_fctx generate_subscripts_fctx
void mda_get_offset_values(int n, int *dist, const int *prod, const int *span)
Definition: arrayutils.c:183
int ArrayGetOffset(int n, const int *dim, const int *lb, const int *indx)
Definition: arrayutils.c:32
void mda_get_range(int n, int *span, const int *st, const int *endp)
Definition: arrayutils.c:153
int ArrayGetNItems(int ndim, const int *dims)
Definition: arrayutils.c:57
void mda_get_prod(int n, const int *range, int *prod)
Definition: arrayutils.c:167
void ArrayCheckBounds(int ndim, const int *dims, const int *lb)
Definition: arrayutils.c:117
int mda_next_tuple(int n, int *curr, const int *span)
Definition: arrayutils.c:208
static Datum values[MAXATTR]
Definition: bootstrap.c:151
#define FORMAT_TYPE_ALLOW_INVALID
Definition: builtins.h:125
#define PG_INT32_MAX
Definition: c.h:560
#define Min(x, y)
Definition: c.h:975
#define Max(x, y)
Definition: c.h:969
char * Pointer
Definition: c.h:493
#define VARHDRSZ
Definition: c.h:663
int64_t int64
Definition: c.h:499
double float8
Definition: c.h:601
#define PointerIsValid(pointer)
Definition: c.h:734
int16_t int16
Definition: c.h:497
#define FLOAT8PASSBYVAL
Definition: c.h:606
uint8 bits8
Definition: c.h:509
int32_t int32
Definition: c.h:498
uint64_t uint64
Definition: c.h:503
uint32_t uint32
Definition: c.h:502
float float4
Definition: c.h:600
#define PG_INT32_MIN
Definition: c.h:559
uint32 TransactionId
Definition: c.h:623
#define OidIsValid(objectId)
Definition: c.h:746
size_t Size
Definition: c.h:576
Node * estimate_expression_value(PlannerInfo *root, Node *node)
Definition: clauses.c:2397
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:132
int errdetail(const char *fmt,...)
Definition: elog.c:1204
int errcode(int sqlerrcode)
Definition: elog.c:854
int errmsg(const char *fmt,...)
Definition: elog.c:1071
#define ereturn(context, dummy_value,...)
Definition: elog.h:278
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:226
#define ereport(elevel,...)
Definition: elog.h:149
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:415
ExpandedObjectHeader * DatumGetEOHP(Datum d)
Definition: expandeddatum.c:29
#define VARATT_IS_EXPANDED_HEADER(PTR)
static Datum EOHPGetRWDatum(const struct ExpandedObjectHeader *eohptr)
#define MaxAllocSize
Definition: fe_memutils.h:22
#define repalloc_array(pointer, type, count)
Definition: fe_memutils.h:78
#define palloc_array(type, count)
Definition: fe_memutils.h:76
void fmgr_info(Oid functionId, FmgrInfo *finfo)
Definition: fmgr.c:127
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition: fmgr.c:137
Datum Int64GetDatum(int64 X)
Definition: fmgr.c:1807
bytea * SendFunctionCall(FmgrInfo *flinfo, Datum val)
Definition: fmgr.c:1744
bool InputFunctionCallSafe(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod, fmNodePtr escontext, Datum *result)
Definition: fmgr.c:1585
char * OutputFunctionCall(FmgrInfo *flinfo, Datum val)
Definition: fmgr.c:1683
Oid get_fn_expr_argtype(FmgrInfo *flinfo, int argnum)
Definition: fmgr.c:1910
Datum ReceiveFunctionCall(FmgrInfo *flinfo, StringInfo buf, Oid typioparam, int32 typmod)
Definition: fmgr.c:1697
#define PG_FREE_IF_COPY(ptr, n)
Definition: fmgr.h:260
#define PG_GETARG_OID(n)
Definition: fmgr.h:275
#define PG_RETURN_UINT32(x)
Definition: fmgr.h:355
#define PG_RETURN_BYTEA_P(x)
Definition: fmgr.h:371
#define PG_DETOAST_DATUM_COPY(datum)
Definition: fmgr.h:242
#define PG_GETARG_POINTER(n)
Definition: fmgr.h:276
#define InitFunctionCallInfoData(Fcinfo, Flinfo, Nargs, Collation, Context, Resultinfo)
Definition: fmgr.h:150
#define PG_RETURN_CSTRING(x)
Definition: fmgr.h:362
#define PG_ARGISNULL(n)
Definition: fmgr.h:209
#define PG_GETARG_DATUM(n)
Definition: fmgr.h:268
#define LOCAL_FCINFO(name, nargs)
Definition: fmgr.h:110
#define PG_NARGS()
Definition: fmgr.h:203
#define PG_GETARG_CSTRING(n)
Definition: fmgr.h:277
#define PG_RETURN_NULL()
Definition: fmgr.h:345
#define PG_GETARG_INT64(n)
Definition: fmgr.h:283
#define PG_RETURN_UINT64(x)
Definition: fmgr.h:369
#define PG_DETOAST_DATUM(datum)
Definition: fmgr.h:240
#define FunctionCallInvoke(fcinfo)
Definition: fmgr.h:172
#define PG_RETURN_TEXT_P(x)
Definition: fmgr.h:372
#define PG_RETURN_INT32(x)
Definition: fmgr.h:354
#define PG_GETARG_INT32(n)
Definition: fmgr.h:269
#define PG_GETARG_BOOL(n)
Definition: fmgr.h:274
#define PG_RETURN_DATUM(x)
Definition: fmgr.h:353
#define PG_RETURN_POINTER(x)
Definition: fmgr.h:361
#define PG_GET_COLLATION()
Definition: fmgr.h:198
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:359
char * format_type_extended(Oid type_oid, int32 typemod, bits16 flags)
Definition: format_type.c:112
char * format_type_be(Oid type_oid)
Definition: format_type.c:343
#define SRF_IS_FIRSTCALL()
Definition: funcapi.h:304
#define SRF_PERCALL_SETUP()
Definition: funcapi.h:308
#define SRF_RETURN_NEXT(_funcctx, _result)
Definition: funcapi.h:310
#define SRF_FIRSTCALL_INIT()
Definition: funcapi.h:306
#define SRF_RETURN_DONE(_funcctx)
Definition: funcapi.h:328
Assert(PointerIsAligned(start, uint64))
#define nitems(x)
Definition: indent.h:31
static struct @165 value
static bool pg_sub_s32_overflow(int32 a, int32 b, int32 *result)
Definition: int.h:169
static bool pg_add_s32_overflow(int32 a, int32 b, int32 *result)
Definition: int.h:151
int j
Definition: isn.c:78
int i
Definition: isn.c:77
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:81
struct ItemPointerData ItemPointerData
Oid get_element_type(Oid typid)
Definition: lsyscache.c:2899
void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval, char *typalign)
Definition: lsyscache.c:2411
void get_type_io_data(Oid typid, IOFuncSelector which_func, int16 *typlen, bool *typbyval, char *typalign, char *typdelim, Oid *typioparam, Oid *func)
Definition: lsyscache.c:2465
Oid get_array_type(Oid typid)
Definition: lsyscache.c:2927
@ IOFunc_output
Definition: lsyscache.h:37
@ IOFunc_input
Definition: lsyscache.h:36
@ IOFunc_send
Definition: lsyscache.h:39
@ IOFunc_receive
Definition: lsyscache.h:38
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:1256
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition: mcxt.c:1290
char * pstrdup(const char *in)
Definition: mcxt.c:2322
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:2167
void pfree(void *pointer)
Definition: mcxt.c:2147
void * palloc0(Size size)
Definition: mcxt.c:1970
void * palloc(Size size)
Definition: mcxt.c:1940
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:485
#define AllocSetContextCreate
Definition: memutils.h:149
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:180
#define AllocSizeIsValid(size)
Definition: memutils.h:45
static bool is_funcclause(const void *clause)
Definition: nodeFuncs.h:69
#define IsA(nodeptr, _type_)
Definition: nodes.h:164
Datum lower(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:49
Datum upper(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:80
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
void * arg
static uint32 pg_nextpower2_32(uint32 num)
Definition: pg_bitutils.h:189
static const struct exclude_list_item skip[]
Definition: pg_checksums.c:107
#define NAMEDATALEN
const void size_t len
const void * data
#define linitial(l)
Definition: pg_list.h:178
static char * buf
Definition: pg_test_fsync.c:72
char typalign
Definition: pg_type.h:176
int pg_strcasecmp(const char *s1, const char *s2)
Definition: pgstrcasecmp.c:36
#define sprintf
Definition: port.h:241
static uint32 DatumGetUInt32(Datum X)
Definition: postgres.h:227
static uint64 DatumGetUInt64(Datum X)
Definition: postgres.h:424
static bool DatumGetBool(Datum X)
Definition: postgres.h:95
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:327
uintptr_t Datum
Definition: postgres.h:69
static float8 DatumGetFloat8(Datum X)
Definition: postgres.h:499
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:317
static Datum Int32GetDatum(int32 X)
Definition: postgres.h:217
static int32 DatumGetInt32(Datum X)
Definition: postgres.h:207
#define InvalidOid
Definition: postgres_ext.h:35
unsigned int Oid
Definition: postgres_ext.h:30
unsigned int pq_getmsgint(StringInfo msg, int b)
Definition: pqformat.c:415
void pq_sendbytes(StringInfo buf, const void *data, int datalen)
Definition: pqformat.c:126
void pq_begintypsend(StringInfo buf)
Definition: pqformat.c:326
bytea * pq_endtypsend(StringInfo buf)
Definition: pqformat.c:346
static void pq_sendint32(StringInfo buf, uint32 i)
Definition: pqformat.h:144
char string[11]
Definition: preproc-type.c:52
bool scanner_isspace(char ch)
Definition: scansup.c:117
double estimate_array_length(PlannerInfo *root, Node *arrayexpr)
Definition: selfuncs.c:2144
void resetStringInfo(StringInfo str)
Definition: stringinfo.c:126
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:242
void initStringInfo(StringInfo str)
Definition: stringinfo.c:97
StringInfoData * StringInfo
Definition: stringinfo.h:54
static void initReadOnlyStringInfo(StringInfo str, char *data, int len)
Definition: stringinfo.h:157
ArrayBuildStateArr * arraystate
Definition: array.h:230
ArrayBuildState * scalarstate
Definition: array.h:229
bits8 * nullbitmap
Definition: array.h:209
int lbs[MAXDIM]
Definition: array.h:216
bool private_cxt
Definition: array.h:219
MemoryContext mcontext
Definition: array.h:207
int dims[MAXDIM]
Definition: array.h:215
bool * dnulls
Definition: array.h:191
bool typbyval
Definition: array.h:196
MemoryContext mcontext
Definition: array.h:189
int16 typlen
Definition: array.h:195
bool private_cxt
Definition: array.h:198
char typalign
Definition: array.h:197
Oid element_type
Definition: array.h:194
Datum * dvalues
Definition: array.h:190
ArrayType * arr
Definition: arrayfuncs.c:71
bool * slice_nulls
Definition: arrayfuncs.c:84
Datum * slice_values
Definition: arrayfuncs.c:83
bits8 * nullbitmap
Definition: arrayfuncs.c:72
ArrayMetaState inp_extra
Definition: array.h:253
ArrayMetaState ret_extra
Definition: array.h:254
Oid typioparam
Definition: array.h:243
char typalign
Definition: array.h:241
Oid typiofunc
Definition: array.h:244
int16 typlen
Definition: array.h:239
Oid element_type
Definition: array.h:238
FmgrInfo proc
Definition: array.h:245
char typdelim
Definition: array.h:242
bool typbyval
Definition: array.h:240
Oid elemtype
Definition: array.h:97
int ndim
Definition: array.h:95
int32 dataoffset
Definition: array.h:96
char * fendptr
Definition: array.h:167
ExpandedObjectHeader hdr
Definition: array.h:118
Datum * dvalues
Definition: array.h:146
ArrayType * fvalue
Definition: array.h:165
MemoryContext eoh_context
bool * innermost_casenull
Definition: execnodes.h:139
Datum * innermost_caseval
Definition: execnodes.h:138
Definition: fmgr.h:57
void * fn_extra
Definition: fmgr.h:64
MemoryContext fn_mcxt
Definition: fmgr.h:65
Oid fn_oid
Definition: fmgr.h:59
void * user_fctx
Definition: funcapi.h:82
MemoryContext multi_call_memory_ctx
Definition: funcapi.h:101
FmgrInfo * flinfo
Definition: fmgr.h:87
Definition: pg_list.h:54
Definition: nodes.h:135
struct PlannerInfo * root
Definition: supportnodes.h:163
FmgrInfo hash_proc_finfo
Definition: typcache.h:78
FmgrInfo cmp_proc_finfo
Definition: typcache.h:77
char typalign
Definition: typcache.h:41
FmgrInfo hash_extended_proc_finfo
Definition: typcache.h:79
FmgrInfo eq_opr_finfo
Definition: typcache.h:76
bool typbyval
Definition: typcache.h:40
int16 typlen
Definition: typcache.h:39
Definition: c.h:658
#define FirstGenbkiObjectId
Definition: transam.h:195
#define att_align_nominal(cur_offset, attalign)
Definition: tupmacs.h:150
#define att_addlength_pointer(cur_offset, attlen, attptr)
Definition: tupmacs.h:185
static Datum fetch_att(const void *T, bool attbyval, int attlen)
Definition: tupmacs.h:53
#define att_addlength_datum(cur_offset, attlen, attdatum)
Definition: tupmacs.h:173
static void store_att_byval(void *T, Datum newdatum, int attlen)
Definition: tupmacs.h:211
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition: typcache.c:386
#define TYPECACHE_HASH_PROC_FINFO
Definition: typcache.h:145
#define TYPECACHE_EQ_OPR_FINFO
Definition: typcache.h:143
#define TYPECACHE_HASH_EXTENDED_PROC_FINFO
Definition: typcache.h:153
#define TYPECACHE_CMP_PROC_FINFO
Definition: typcache.h:144
ExpandedArrayHeader xpn
Definition: array.h:180
#define VARATT_IS_EXTERNAL_EXPANDED(PTR)
Definition: varatt.h:298
#define VARDATA(PTR)
Definition: varatt.h:278
#define SET_VARSIZE(PTR, len)
Definition: varatt.h:305
#define VARSIZE(PTR)
Definition: varatt.h:279
text * cstring_to_text(const char *s)
Definition: varlena.c:192