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