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