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