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