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