1 /*-------------------------------------------------------------------------
2 *
3 * arrayfuncs.c
4 * Support functions for arrays.
5 *
6 * Portions Copyright (c) 1996-2017, 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 size */
4981 astate->dvalues = (Datum *)
4982 MemoryContextAlloc(arr_context, astate->alen * sizeof(Datum));
4983 astate->dnulls = (bool *)
4984 MemoryContextAlloc(arr_context, astate->alen * sizeof(bool));
4985 astate->nelems = 0;
4986 astate->element_type = element_type;
4987 get_typlenbyvalalign(element_type,
4988 &astate->typlen,
4989 &astate->typbyval,
4990 &astate->typalign);
4991
4992 return astate;
4993 }
4994
4995 /*
4996 * accumArrayResult - accumulate one (more) Datum for an array result
4997 *
4998 * astate is working state (can be NULL on first call)
4999 * dvalue/disnull represent the new Datum to append to the array
5000 * element_type is the Datum's type (must be a valid array element type)
5001 * rcontext is where to keep working state
5002 */
5003 ArrayBuildState *
accumArrayResult(ArrayBuildState * astate,Datum dvalue,bool disnull,Oid element_type,MemoryContext rcontext)5004 accumArrayResult(ArrayBuildState *astate,
5005 Datum dvalue, bool disnull,
5006 Oid element_type,
5007 MemoryContext rcontext)
5008 {
5009 MemoryContext oldcontext;
5010
5011 if (astate == NULL)
5012 {
5013 /* First time through --- initialize */
5014 astate = initArrayResult(element_type, rcontext, true);
5015 }
5016 else
5017 {
5018 Assert(astate->element_type == element_type);
5019 }
5020
5021 oldcontext = MemoryContextSwitchTo(astate->mcontext);
5022
5023 /* enlarge dvalues[]/dnulls[] if needed */
5024 if (astate->nelems >= astate->alen)
5025 {
5026 astate->alen *= 2;
5027 astate->dvalues = (Datum *)
5028 repalloc(astate->dvalues, astate->alen * sizeof(Datum));
5029 astate->dnulls = (bool *)
5030 repalloc(astate->dnulls, astate->alen * sizeof(bool));
5031 }
5032
5033 /*
5034 * Ensure pass-by-ref stuff is copied into mcontext; and detoast it too if
5035 * it's varlena. (You might think that detoasting is not needed here
5036 * because construct_md_array can detoast the array elements later.
5037 * However, we must not let construct_md_array modify the ArrayBuildState
5038 * because that would mean array_agg_finalfn damages its input, which is
5039 * verboten. Also, this way frequently saves one copying step.)
5040 */
5041 if (!disnull && !astate->typbyval)
5042 {
5043 if (astate->typlen == -1)
5044 dvalue = PointerGetDatum(PG_DETOAST_DATUM_COPY(dvalue));
5045 else
5046 dvalue = datumCopy(dvalue, astate->typbyval, astate->typlen);
5047 }
5048
5049 astate->dvalues[astate->nelems] = dvalue;
5050 astate->dnulls[astate->nelems] = disnull;
5051 astate->nelems++;
5052
5053 MemoryContextSwitchTo(oldcontext);
5054
5055 return astate;
5056 }
5057
5058 /*
5059 * makeArrayResult - produce 1-D final result of accumArrayResult
5060 *
5061 * Note: only releases astate if it was initialized within a separate memory
5062 * context (i.e. using subcontext=true when calling initArrayResult).
5063 *
5064 * astate is working state (must not be NULL)
5065 * rcontext is where to construct result
5066 */
5067 Datum
makeArrayResult(ArrayBuildState * astate,MemoryContext rcontext)5068 makeArrayResult(ArrayBuildState *astate,
5069 MemoryContext rcontext)
5070 {
5071 int ndims;
5072 int dims[1];
5073 int lbs[1];
5074
5075 /* If no elements were presented, we want to create an empty array */
5076 ndims = (astate->nelems > 0) ? 1 : 0;
5077 dims[0] = astate->nelems;
5078 lbs[0] = 1;
5079
5080 return makeMdArrayResult(astate, ndims, dims, lbs, rcontext,
5081 astate->private_cxt);
5082 }
5083
5084 /*
5085 * makeMdArrayResult - produce multi-D final result of accumArrayResult
5086 *
5087 * beware: no check that specified dimensions match the number of values
5088 * accumulated.
5089 *
5090 * Note: if the astate was not initialized within a separate memory context
5091 * (that is, initArrayResult was called with subcontext=false), then using
5092 * release=true is illegal. Instead, release astate along with the rest of its
5093 * context when appropriate.
5094 *
5095 * astate is working state (must not be NULL)
5096 * rcontext is where to construct result
5097 * release is true if okay to release working state
5098 */
5099 Datum
makeMdArrayResult(ArrayBuildState * astate,int ndims,int * dims,int * lbs,MemoryContext rcontext,bool release)5100 makeMdArrayResult(ArrayBuildState *astate,
5101 int ndims,
5102 int *dims,
5103 int *lbs,
5104 MemoryContext rcontext,
5105 bool release)
5106 {
5107 ArrayType *result;
5108 MemoryContext oldcontext;
5109
5110 /* Build the final array result in rcontext */
5111 oldcontext = MemoryContextSwitchTo(rcontext);
5112
5113 result = construct_md_array(astate->dvalues,
5114 astate->dnulls,
5115 ndims,
5116 dims,
5117 lbs,
5118 astate->element_type,
5119 astate->typlen,
5120 astate->typbyval,
5121 astate->typalign);
5122
5123 MemoryContextSwitchTo(oldcontext);
5124
5125 /* Clean up all the junk */
5126 if (release)
5127 {
5128 Assert(astate->private_cxt);
5129 MemoryContextDelete(astate->mcontext);
5130 }
5131
5132 return PointerGetDatum(result);
5133 }
5134
5135 /*
5136 * The following three functions provide essentially the same API as
5137 * initArrayResult/accumArrayResult/makeArrayResult, but instead of accepting
5138 * inputs that are array elements, they accept inputs that are arrays and
5139 * produce an output array having N+1 dimensions. The inputs must all have
5140 * identical dimensionality as well as element type.
5141 */
5142
5143 /*
5144 * initArrayResultArr - initialize an empty ArrayBuildStateArr
5145 *
5146 * array_type is the array type (must be a valid varlena array type)
5147 * element_type is the type of the array's elements (lookup if InvalidOid)
5148 * rcontext is where to keep working state
5149 * subcontext is a flag determining whether to use a separate memory context
5150 */
5151 ArrayBuildStateArr *
initArrayResultArr(Oid array_type,Oid element_type,MemoryContext rcontext,bool subcontext)5152 initArrayResultArr(Oid array_type, Oid element_type, MemoryContext rcontext,
5153 bool subcontext)
5154 {
5155 ArrayBuildStateArr *astate;
5156 MemoryContext arr_context = rcontext; /* by default use the parent ctx */
5157
5158 /* Lookup element type, unless element_type already provided */
5159 if (!OidIsValid(element_type))
5160 {
5161 element_type = get_element_type(array_type);
5162
5163 if (!OidIsValid(element_type))
5164 ereport(ERROR,
5165 (errcode(ERRCODE_DATATYPE_MISMATCH),
5166 errmsg("data type %s is not an array type",
5167 format_type_be(array_type))));
5168 }
5169
5170 /* Make a temporary context to hold all the junk */
5171 if (subcontext)
5172 arr_context = AllocSetContextCreate(rcontext,
5173 "accumArrayResultArr",
5174 ALLOCSET_DEFAULT_SIZES);
5175
5176 /* Note we initialize all fields to zero */
5177 astate = (ArrayBuildStateArr *)
5178 MemoryContextAllocZero(arr_context, sizeof(ArrayBuildStateArr));
5179 astate->mcontext = arr_context;
5180 astate->private_cxt = subcontext;
5181
5182 /* Save relevant datatype information */
5183 astate->array_type = array_type;
5184 astate->element_type = element_type;
5185
5186 return astate;
5187 }
5188
5189 /*
5190 * accumArrayResultArr - accumulate one (more) sub-array for an array result
5191 *
5192 * astate is working state (can be NULL on first call)
5193 * dvalue/disnull represent the new sub-array to append to the array
5194 * array_type is the array type (must be a valid varlena array type)
5195 * rcontext is where to keep working state
5196 */
5197 ArrayBuildStateArr *
accumArrayResultArr(ArrayBuildStateArr * astate,Datum dvalue,bool disnull,Oid array_type,MemoryContext rcontext)5198 accumArrayResultArr(ArrayBuildStateArr *astate,
5199 Datum dvalue, bool disnull,
5200 Oid array_type,
5201 MemoryContext rcontext)
5202 {
5203 ArrayType *arg;
5204 MemoryContext oldcontext;
5205 int *dims,
5206 *lbs,
5207 ndims,
5208 nitems,
5209 ndatabytes;
5210 char *data;
5211 int i;
5212
5213 /*
5214 * We disallow accumulating null subarrays. Another plausible definition
5215 * is to ignore them, but callers that want that can just skip calling
5216 * this function.
5217 */
5218 if (disnull)
5219 ereport(ERROR,
5220 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5221 errmsg("cannot accumulate null arrays")));
5222
5223 /* Detoast input array in caller's context */
5224 arg = DatumGetArrayTypeP(dvalue);
5225
5226 if (astate == NULL)
5227 astate = initArrayResultArr(array_type, InvalidOid, rcontext, true);
5228 else
5229 Assert(astate->array_type == array_type);
5230
5231 oldcontext = MemoryContextSwitchTo(astate->mcontext);
5232
5233 /* Collect this input's dimensions */
5234 ndims = ARR_NDIM(arg);
5235 dims = ARR_DIMS(arg);
5236 lbs = ARR_LBOUND(arg);
5237 data = ARR_DATA_PTR(arg);
5238 nitems = ArrayGetNItems(ndims, dims);
5239 ndatabytes = ARR_SIZE(arg) - ARR_DATA_OFFSET(arg);
5240
5241 if (astate->ndims == 0)
5242 {
5243 /* First input; check/save the dimensionality info */
5244
5245 /* Should we allow empty inputs and just produce an empty output? */
5246 if (ndims == 0)
5247 ereport(ERROR,
5248 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5249 errmsg("cannot accumulate empty arrays")));
5250 if (ndims + 1 > MAXDIM)
5251 ereport(ERROR,
5252 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
5253 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
5254 ndims + 1, MAXDIM)));
5255
5256 /*
5257 * The output array will have n+1 dimensions, with the ones after the
5258 * first matching the input's dimensions.
5259 */
5260 astate->ndims = ndims + 1;
5261 astate->dims[0] = 0;
5262 memcpy(&astate->dims[1], dims, ndims * sizeof(int));
5263 astate->lbs[0] = 1;
5264 memcpy(&astate->lbs[1], lbs, ndims * sizeof(int));
5265
5266 /* Allocate at least enough data space for this item */
5267 astate->abytes = 1024;
5268 while (astate->abytes <= ndatabytes)
5269 astate->abytes *= 2;
5270 astate->data = (char *) palloc(astate->abytes);
5271 }
5272 else
5273 {
5274 /* Second or later input: must match first input's dimensionality */
5275 if (astate->ndims != ndims + 1)
5276 ereport(ERROR,
5277 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5278 errmsg("cannot accumulate arrays of different dimensionality")));
5279 for (i = 0; i < ndims; i++)
5280 {
5281 if (astate->dims[i + 1] != dims[i] || astate->lbs[i + 1] != lbs[i])
5282 ereport(ERROR,
5283 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5284 errmsg("cannot accumulate arrays of different dimensionality")));
5285 }
5286
5287 /* Enlarge data space if needed */
5288 if (astate->nbytes + ndatabytes > astate->abytes)
5289 {
5290 astate->abytes = Max(astate->abytes * 2,
5291 astate->nbytes + ndatabytes);
5292 astate->data = (char *) repalloc(astate->data, astate->abytes);
5293 }
5294 }
5295
5296 /*
5297 * Copy the data portion of the sub-array. Note we assume that the
5298 * advertised data length of the sub-array is properly aligned. We do not
5299 * have to worry about detoasting elements since whatever's in the
5300 * sub-array should be OK already.
5301 */
5302 memcpy(astate->data + astate->nbytes, data, ndatabytes);
5303 astate->nbytes += ndatabytes;
5304
5305 /* Deal with null bitmap if needed */
5306 if (astate->nullbitmap || ARR_HASNULL(arg))
5307 {
5308 int newnitems = astate->nitems + nitems;
5309
5310 if (astate->nullbitmap == NULL)
5311 {
5312 /*
5313 * First input with nulls; we must retrospectively handle any
5314 * previous inputs by marking all their items non-null.
5315 */
5316 astate->aitems = 256;
5317 while (astate->aitems <= newnitems)
5318 astate->aitems *= 2;
5319 astate->nullbitmap = (bits8 *) palloc((astate->aitems + 7) / 8);
5320 array_bitmap_copy(astate->nullbitmap, 0,
5321 NULL, 0,
5322 astate->nitems);
5323 }
5324 else if (newnitems > astate->aitems)
5325 {
5326 astate->aitems = Max(astate->aitems * 2, newnitems);
5327 astate->nullbitmap = (bits8 *)
5328 repalloc(astate->nullbitmap, (astate->aitems + 7) / 8);
5329 }
5330 array_bitmap_copy(astate->nullbitmap, astate->nitems,
5331 ARR_NULLBITMAP(arg), 0,
5332 nitems);
5333 }
5334
5335 astate->nitems += nitems;
5336 astate->dims[0] += 1;
5337
5338 MemoryContextSwitchTo(oldcontext);
5339
5340 /* Release detoasted copy if any */
5341 if ((Pointer) arg != DatumGetPointer(dvalue))
5342 pfree(arg);
5343
5344 return astate;
5345 }
5346
5347 /*
5348 * makeArrayResultArr - produce N+1-D final result of accumArrayResultArr
5349 *
5350 * astate is working state (must not be NULL)
5351 * rcontext is where to construct result
5352 * release is true if okay to release working state
5353 */
5354 Datum
makeArrayResultArr(ArrayBuildStateArr * astate,MemoryContext rcontext,bool release)5355 makeArrayResultArr(ArrayBuildStateArr *astate,
5356 MemoryContext rcontext,
5357 bool release)
5358 {
5359 ArrayType *result;
5360 MemoryContext oldcontext;
5361
5362 /* Build the final array result in rcontext */
5363 oldcontext = MemoryContextSwitchTo(rcontext);
5364
5365 if (astate->ndims == 0)
5366 {
5367 /* No inputs, return empty array */
5368 result = construct_empty_array(astate->element_type);
5369 }
5370 else
5371 {
5372 int dataoffset,
5373 nbytes;
5374
5375 /* Check for overflow of the array dimensions */
5376 (void) ArrayGetNItems(astate->ndims, astate->dims);
5377 ArrayCheckBounds(astate->ndims, astate->dims, astate->lbs);
5378
5379 /* Compute required space */
5380 nbytes = astate->nbytes;
5381 if (astate->nullbitmap != NULL)
5382 {
5383 dataoffset = ARR_OVERHEAD_WITHNULLS(astate->ndims, astate->nitems);
5384 nbytes += dataoffset;
5385 }
5386 else
5387 {
5388 dataoffset = 0;
5389 nbytes += ARR_OVERHEAD_NONULLS(astate->ndims);
5390 }
5391
5392 result = (ArrayType *) palloc0(nbytes);
5393 SET_VARSIZE(result, nbytes);
5394 result->ndim = astate->ndims;
5395 result->dataoffset = dataoffset;
5396 result->elemtype = astate->element_type;
5397
5398 memcpy(ARR_DIMS(result), astate->dims, astate->ndims * sizeof(int));
5399 memcpy(ARR_LBOUND(result), astate->lbs, astate->ndims * sizeof(int));
5400 memcpy(ARR_DATA_PTR(result), astate->data, astate->nbytes);
5401
5402 if (astate->nullbitmap != NULL)
5403 array_bitmap_copy(ARR_NULLBITMAP(result), 0,
5404 astate->nullbitmap, 0,
5405 astate->nitems);
5406 }
5407
5408 MemoryContextSwitchTo(oldcontext);
5409
5410 /* Clean up all the junk */
5411 if (release)
5412 {
5413 Assert(astate->private_cxt);
5414 MemoryContextDelete(astate->mcontext);
5415 }
5416
5417 return PointerGetDatum(result);
5418 }
5419
5420 /*
5421 * The following three functions provide essentially the same API as
5422 * initArrayResult/accumArrayResult/makeArrayResult, but can accept either
5423 * scalar or array inputs, invoking the appropriate set of functions above.
5424 */
5425
5426 /*
5427 * initArrayResultAny - initialize an empty ArrayBuildStateAny
5428 *
5429 * input_type is the input datatype (either element or array type)
5430 * rcontext is where to keep working state
5431 * subcontext is a flag determining whether to use a separate memory context
5432 */
5433 ArrayBuildStateAny *
initArrayResultAny(Oid input_type,MemoryContext rcontext,bool subcontext)5434 initArrayResultAny(Oid input_type, MemoryContext rcontext, bool subcontext)
5435 {
5436 ArrayBuildStateAny *astate;
5437 Oid element_type = get_element_type(input_type);
5438
5439 if (OidIsValid(element_type))
5440 {
5441 /* Array case */
5442 ArrayBuildStateArr *arraystate;
5443
5444 arraystate = initArrayResultArr(input_type, InvalidOid, rcontext, subcontext);
5445 astate = (ArrayBuildStateAny *)
5446 MemoryContextAlloc(arraystate->mcontext,
5447 sizeof(ArrayBuildStateAny));
5448 astate->scalarstate = NULL;
5449 astate->arraystate = arraystate;
5450 }
5451 else
5452 {
5453 /* Scalar case */
5454 ArrayBuildState *scalarstate;
5455
5456 /* Let's just check that we have a type that can be put into arrays */
5457 Assert(OidIsValid(get_array_type(input_type)));
5458
5459 scalarstate = initArrayResult(input_type, rcontext, subcontext);
5460 astate = (ArrayBuildStateAny *)
5461 MemoryContextAlloc(scalarstate->mcontext,
5462 sizeof(ArrayBuildStateAny));
5463 astate->scalarstate = scalarstate;
5464 astate->arraystate = NULL;
5465 }
5466
5467 return astate;
5468 }
5469
5470 /*
5471 * accumArrayResultAny - accumulate one (more) input for an array result
5472 *
5473 * astate is working state (can be NULL on first call)
5474 * dvalue/disnull represent the new input to append to the array
5475 * input_type is the input datatype (either element or array type)
5476 * rcontext is where to keep working state
5477 */
5478 ArrayBuildStateAny *
accumArrayResultAny(ArrayBuildStateAny * astate,Datum dvalue,bool disnull,Oid input_type,MemoryContext rcontext)5479 accumArrayResultAny(ArrayBuildStateAny *astate,
5480 Datum dvalue, bool disnull,
5481 Oid input_type,
5482 MemoryContext rcontext)
5483 {
5484 if (astate == NULL)
5485 astate = initArrayResultAny(input_type, rcontext, true);
5486
5487 if (astate->scalarstate)
5488 (void) accumArrayResult(astate->scalarstate,
5489 dvalue, disnull,
5490 input_type, rcontext);
5491 else
5492 (void) accumArrayResultArr(astate->arraystate,
5493 dvalue, disnull,
5494 input_type, rcontext);
5495
5496 return astate;
5497 }
5498
5499 /*
5500 * makeArrayResultAny - produce final result of accumArrayResultAny
5501 *
5502 * astate is working state (must not be NULL)
5503 * rcontext is where to construct result
5504 * release is true if okay to release working state
5505 */
5506 Datum
makeArrayResultAny(ArrayBuildStateAny * astate,MemoryContext rcontext,bool release)5507 makeArrayResultAny(ArrayBuildStateAny *astate,
5508 MemoryContext rcontext, bool release)
5509 {
5510 Datum result;
5511
5512 if (astate->scalarstate)
5513 {
5514 /* Must use makeMdArrayResult to support "release" parameter */
5515 int ndims;
5516 int dims[1];
5517 int lbs[1];
5518
5519 /* If no elements were presented, we want to create an empty array */
5520 ndims = (astate->scalarstate->nelems > 0) ? 1 : 0;
5521 dims[0] = astate->scalarstate->nelems;
5522 lbs[0] = 1;
5523
5524 result = makeMdArrayResult(astate->scalarstate, ndims, dims, lbs,
5525 rcontext, release);
5526 }
5527 else
5528 {
5529 result = makeArrayResultArr(astate->arraystate,
5530 rcontext, release);
5531 }
5532 return result;
5533 }
5534
5535
5536 Datum
array_larger(PG_FUNCTION_ARGS)5537 array_larger(PG_FUNCTION_ARGS)
5538 {
5539 if (array_cmp(fcinfo) > 0)
5540 PG_RETURN_DATUM(PG_GETARG_DATUM(0));
5541 else
5542 PG_RETURN_DATUM(PG_GETARG_DATUM(1));
5543 }
5544
5545 Datum
array_smaller(PG_FUNCTION_ARGS)5546 array_smaller(PG_FUNCTION_ARGS)
5547 {
5548 if (array_cmp(fcinfo) < 0)
5549 PG_RETURN_DATUM(PG_GETARG_DATUM(0));
5550 else
5551 PG_RETURN_DATUM(PG_GETARG_DATUM(1));
5552 }
5553
5554
5555 typedef struct generate_subscripts_fctx
5556 {
5557 int32 lower;
5558 int32 upper;
5559 bool reverse;
5560 } generate_subscripts_fctx;
5561
5562 /*
5563 * generate_subscripts(array anyarray, dim int [, reverse bool])
5564 * Returns all subscripts of the array for any dimension
5565 */
5566 Datum
generate_subscripts(PG_FUNCTION_ARGS)5567 generate_subscripts(PG_FUNCTION_ARGS)
5568 {
5569 FuncCallContext *funcctx;
5570 MemoryContext oldcontext;
5571 generate_subscripts_fctx *fctx;
5572
5573 /* stuff done only on the first call of the function */
5574 if (SRF_IS_FIRSTCALL())
5575 {
5576 AnyArrayType *v = PG_GETARG_ANY_ARRAY(0);
5577 int reqdim = PG_GETARG_INT32(1);
5578 int *lb,
5579 *dimv;
5580
5581 /* create a function context for cross-call persistence */
5582 funcctx = SRF_FIRSTCALL_INIT();
5583
5584 /* Sanity check: does it look like an array at all? */
5585 if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
5586 SRF_RETURN_DONE(funcctx);
5587
5588 /* Sanity check: was the requested dim valid */
5589 if (reqdim <= 0 || reqdim > AARR_NDIM(v))
5590 SRF_RETURN_DONE(funcctx);
5591
5592 /*
5593 * switch to memory context appropriate for multiple function calls
5594 */
5595 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
5596 fctx = (generate_subscripts_fctx *) palloc(sizeof(generate_subscripts_fctx));
5597
5598 lb = AARR_LBOUND(v);
5599 dimv = AARR_DIMS(v);
5600
5601 fctx->lower = lb[reqdim - 1];
5602 fctx->upper = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
5603 fctx->reverse = (PG_NARGS() < 3) ? false : PG_GETARG_BOOL(2);
5604
5605 funcctx->user_fctx = fctx;
5606
5607 MemoryContextSwitchTo(oldcontext);
5608 }
5609
5610 funcctx = SRF_PERCALL_SETUP();
5611
5612 fctx = funcctx->user_fctx;
5613
5614 if (fctx->lower <= fctx->upper)
5615 {
5616 if (!fctx->reverse)
5617 SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->lower++));
5618 else
5619 SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->upper--));
5620 }
5621 else
5622 /* done when there are no more elements left */
5623 SRF_RETURN_DONE(funcctx);
5624 }
5625
5626 /*
5627 * generate_subscripts_nodir
5628 * Implements the 2-argument version of generate_subscripts
5629 */
5630 Datum
generate_subscripts_nodir(PG_FUNCTION_ARGS)5631 generate_subscripts_nodir(PG_FUNCTION_ARGS)
5632 {
5633 /* just call the other one -- it can handle both cases */
5634 return generate_subscripts(fcinfo);
5635 }
5636
5637 /*
5638 * array_fill_with_lower_bounds
5639 * Create and fill array with defined lower bounds.
5640 */
5641 Datum
array_fill_with_lower_bounds(PG_FUNCTION_ARGS)5642 array_fill_with_lower_bounds(PG_FUNCTION_ARGS)
5643 {
5644 ArrayType *dims;
5645 ArrayType *lbs;
5646 ArrayType *result;
5647 Oid elmtype;
5648 Datum value;
5649 bool isnull;
5650
5651 if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
5652 ereport(ERROR,
5653 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5654 errmsg("dimension array or low bound array cannot be null")));
5655
5656 dims = PG_GETARG_ARRAYTYPE_P(1);
5657 lbs = PG_GETARG_ARRAYTYPE_P(2);
5658
5659 if (!PG_ARGISNULL(0))
5660 {
5661 value = PG_GETARG_DATUM(0);
5662 isnull = false;
5663 }
5664 else
5665 {
5666 value = 0;
5667 isnull = true;
5668 }
5669
5670 elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
5671 if (!OidIsValid(elmtype))
5672 elog(ERROR, "could not determine data type of input");
5673
5674 result = array_fill_internal(dims, lbs, value, isnull, elmtype, fcinfo);
5675 PG_RETURN_ARRAYTYPE_P(result);
5676 }
5677
5678 /*
5679 * array_fill
5680 * Create and fill array with default lower bounds.
5681 */
5682 Datum
array_fill(PG_FUNCTION_ARGS)5683 array_fill(PG_FUNCTION_ARGS)
5684 {
5685 ArrayType *dims;
5686 ArrayType *result;
5687 Oid elmtype;
5688 Datum value;
5689 bool isnull;
5690
5691 if (PG_ARGISNULL(1))
5692 ereport(ERROR,
5693 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5694 errmsg("dimension array or low bound array cannot be null")));
5695
5696 dims = PG_GETARG_ARRAYTYPE_P(1);
5697
5698 if (!PG_ARGISNULL(0))
5699 {
5700 value = PG_GETARG_DATUM(0);
5701 isnull = false;
5702 }
5703 else
5704 {
5705 value = 0;
5706 isnull = true;
5707 }
5708
5709 elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
5710 if (!OidIsValid(elmtype))
5711 elog(ERROR, "could not determine data type of input");
5712
5713 result = array_fill_internal(dims, NULL, value, isnull, elmtype, fcinfo);
5714 PG_RETURN_ARRAYTYPE_P(result);
5715 }
5716
5717 static ArrayType *
create_array_envelope(int ndims,int * dimv,int * lbsv,int nbytes,Oid elmtype,int dataoffset)5718 create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
5719 Oid elmtype, int dataoffset)
5720 {
5721 ArrayType *result;
5722
5723 result = (ArrayType *) palloc0(nbytes);
5724 SET_VARSIZE(result, nbytes);
5725 result->ndim = ndims;
5726 result->dataoffset = dataoffset;
5727 result->elemtype = elmtype;
5728 memcpy(ARR_DIMS(result), dimv, ndims * sizeof(int));
5729 memcpy(ARR_LBOUND(result), lbsv, ndims * sizeof(int));
5730
5731 return result;
5732 }
5733
5734 static ArrayType *
array_fill_internal(ArrayType * dims,ArrayType * lbs,Datum value,bool isnull,Oid elmtype,FunctionCallInfo fcinfo)5735 array_fill_internal(ArrayType *dims, ArrayType *lbs,
5736 Datum value, bool isnull, Oid elmtype,
5737 FunctionCallInfo fcinfo)
5738 {
5739 ArrayType *result;
5740 int *dimv;
5741 int *lbsv;
5742 int ndims;
5743 int nitems;
5744 int deflbs[MAXDIM];
5745 int16 elmlen;
5746 bool elmbyval;
5747 char elmalign;
5748 ArrayMetaState *my_extra;
5749
5750 /*
5751 * Params checks
5752 */
5753 if (ARR_NDIM(dims) > 1)
5754 ereport(ERROR,
5755 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5756 errmsg("wrong number of array subscripts"),
5757 errdetail("Dimension array must be one dimensional.")));
5758
5759 if (array_contains_nulls(dims))
5760 ereport(ERROR,
5761 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5762 errmsg("dimension values cannot be null")));
5763
5764 dimv = (int *) ARR_DATA_PTR(dims);
5765 ndims = (ARR_NDIM(dims) > 0) ? ARR_DIMS(dims)[0] : 0;
5766
5767 if (ndims < 0) /* we do allow zero-dimension arrays */
5768 ereport(ERROR,
5769 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
5770 errmsg("invalid number of dimensions: %d", ndims)));
5771 if (ndims > MAXDIM)
5772 ereport(ERROR,
5773 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
5774 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
5775 ndims, MAXDIM)));
5776
5777 if (lbs != NULL)
5778 {
5779 if (ARR_NDIM(lbs) > 1)
5780 ereport(ERROR,
5781 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5782 errmsg("wrong number of array subscripts"),
5783 errdetail("Dimension array must be one dimensional.")));
5784
5785 if (array_contains_nulls(lbs))
5786 ereport(ERROR,
5787 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5788 errmsg("dimension values cannot be null")));
5789
5790 if (ndims != ((ARR_NDIM(lbs) > 0) ? ARR_DIMS(lbs)[0] : 0))
5791 ereport(ERROR,
5792 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5793 errmsg("wrong number of array subscripts"),
5794 errdetail("Low bound array has different size than dimensions array.")));
5795
5796 lbsv = (int *) ARR_DATA_PTR(lbs);
5797 }
5798 else
5799 {
5800 int i;
5801
5802 for (i = 0; i < MAXDIM; i++)
5803 deflbs[i] = 1;
5804
5805 lbsv = deflbs;
5806 }
5807
5808 /* This checks for overflow of the array dimensions */
5809 nitems = ArrayGetNItems(ndims, dimv);
5810 ArrayCheckBounds(ndims, dimv, lbsv);
5811
5812 /* fast track for empty array */
5813 if (nitems <= 0)
5814 return construct_empty_array(elmtype);
5815
5816 /*
5817 * We arrange to look up info about element type only once per series of
5818 * calls, assuming the element type doesn't change underneath us.
5819 */
5820 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
5821 if (my_extra == NULL)
5822 {
5823 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
5824 sizeof(ArrayMetaState));
5825 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
5826 my_extra->element_type = InvalidOid;
5827 }
5828
5829 if (my_extra->element_type != elmtype)
5830 {
5831 /* Get info about element type */
5832 get_typlenbyvalalign(elmtype,
5833 &my_extra->typlen,
5834 &my_extra->typbyval,
5835 &my_extra->typalign);
5836 my_extra->element_type = elmtype;
5837 }
5838
5839 elmlen = my_extra->typlen;
5840 elmbyval = my_extra->typbyval;
5841 elmalign = my_extra->typalign;
5842
5843 /* compute required space */
5844 if (!isnull)
5845 {
5846 int i;
5847 char *p;
5848 int nbytes;
5849 int totbytes;
5850
5851 /* make sure data is not toasted */
5852 if (elmlen == -1)
5853 value = PointerGetDatum(PG_DETOAST_DATUM(value));
5854
5855 nbytes = att_addlength_datum(0, elmlen, value);
5856 nbytes = att_align_nominal(nbytes, elmalign);
5857 Assert(nbytes > 0);
5858
5859 totbytes = nbytes * nitems;
5860
5861 /* check for overflow of multiplication or total request */
5862 if (totbytes / nbytes != nitems ||
5863 !AllocSizeIsValid(totbytes))
5864 ereport(ERROR,
5865 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
5866 errmsg("array size exceeds the maximum allowed (%d)",
5867 (int) MaxAllocSize)));
5868
5869 /*
5870 * This addition can't overflow, but it might cause us to go past
5871 * MaxAllocSize. We leave it to palloc to complain in that case.
5872 */
5873 totbytes += ARR_OVERHEAD_NONULLS(ndims);
5874
5875 result = create_array_envelope(ndims, dimv, lbsv, totbytes,
5876 elmtype, 0);
5877
5878 p = ARR_DATA_PTR(result);
5879 for (i = 0; i < nitems; i++)
5880 p += ArrayCastAndSet(value, elmlen, elmbyval, elmalign, p);
5881 }
5882 else
5883 {
5884 int nbytes;
5885 int dataoffset;
5886
5887 dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
5888 nbytes = dataoffset;
5889
5890 result = create_array_envelope(ndims, dimv, lbsv, nbytes,
5891 elmtype, dataoffset);
5892
5893 /* create_array_envelope already zeroed the bitmap, so we're done */
5894 }
5895
5896 return result;
5897 }
5898
5899
5900 /*
5901 * UNNEST
5902 */
5903 Datum
array_unnest(PG_FUNCTION_ARGS)5904 array_unnest(PG_FUNCTION_ARGS)
5905 {
5906 typedef struct
5907 {
5908 array_iter iter;
5909 int nextelem;
5910 int numelems;
5911 int16 elmlen;
5912 bool elmbyval;
5913 char elmalign;
5914 } array_unnest_fctx;
5915
5916 FuncCallContext *funcctx;
5917 array_unnest_fctx *fctx;
5918 MemoryContext oldcontext;
5919
5920 /* stuff done only on the first call of the function */
5921 if (SRF_IS_FIRSTCALL())
5922 {
5923 AnyArrayType *arr;
5924
5925 /* create a function context for cross-call persistence */
5926 funcctx = SRF_FIRSTCALL_INIT();
5927
5928 /*
5929 * switch to memory context appropriate for multiple function calls
5930 */
5931 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
5932
5933 /*
5934 * Get the array value and detoast if needed. We can't do this
5935 * earlier because if we have to detoast, we want the detoasted copy
5936 * to be in multi_call_memory_ctx, so it will go away when we're done
5937 * and not before. (If no detoast happens, we assume the originally
5938 * passed array will stick around till then.)
5939 */
5940 arr = PG_GETARG_ANY_ARRAY(0);
5941
5942 /* allocate memory for user context */
5943 fctx = (array_unnest_fctx *) palloc(sizeof(array_unnest_fctx));
5944
5945 /* initialize state */
5946 array_iter_setup(&fctx->iter, arr);
5947 fctx->nextelem = 0;
5948 fctx->numelems = ArrayGetNItems(AARR_NDIM(arr), AARR_DIMS(arr));
5949
5950 if (VARATT_IS_EXPANDED_HEADER(arr))
5951 {
5952 /* we can just grab the type data from expanded array */
5953 fctx->elmlen = arr->xpn.typlen;
5954 fctx->elmbyval = arr->xpn.typbyval;
5955 fctx->elmalign = arr->xpn.typalign;
5956 }
5957 else
5958 get_typlenbyvalalign(AARR_ELEMTYPE(arr),
5959 &fctx->elmlen,
5960 &fctx->elmbyval,
5961 &fctx->elmalign);
5962
5963 funcctx->user_fctx = fctx;
5964 MemoryContextSwitchTo(oldcontext);
5965 }
5966
5967 /* stuff done on every call of the function */
5968 funcctx = SRF_PERCALL_SETUP();
5969 fctx = funcctx->user_fctx;
5970
5971 if (fctx->nextelem < fctx->numelems)
5972 {
5973 int offset = fctx->nextelem++;
5974 Datum elem;
5975
5976 elem = array_iter_next(&fctx->iter, &fcinfo->isnull, offset,
5977 fctx->elmlen, fctx->elmbyval, fctx->elmalign);
5978
5979 SRF_RETURN_NEXT(funcctx, elem);
5980 }
5981 else
5982 {
5983 /* do when there is no more left */
5984 SRF_RETURN_DONE(funcctx);
5985 }
5986 }
5987
5988
5989 /*
5990 * array_replace/array_remove support
5991 *
5992 * Find all array entries matching (not distinct from) search/search_isnull,
5993 * and delete them if remove is true, else replace them with
5994 * replace/replace_isnull. Comparisons are done using the specified
5995 * collation. fcinfo is passed only for caching purposes.
5996 */
5997 static ArrayType *
array_replace_internal(ArrayType * array,Datum search,bool search_isnull,Datum replace,bool replace_isnull,bool remove,Oid collation,FunctionCallInfo fcinfo)5998 array_replace_internal(ArrayType *array,
5999 Datum search, bool search_isnull,
6000 Datum replace, bool replace_isnull,
6001 bool remove, Oid collation,
6002 FunctionCallInfo fcinfo)
6003 {
6004 ArrayType *result;
6005 Oid element_type;
6006 Datum *values;
6007 bool *nulls;
6008 int *dim;
6009 int ndim;
6010 int nitems,
6011 nresult;
6012 int i;
6013 int32 nbytes = 0;
6014 int32 dataoffset;
6015 bool hasnulls;
6016 int typlen;
6017 bool typbyval;
6018 char typalign;
6019 char *arraydataptr;
6020 bits8 *bitmap;
6021 int bitmask;
6022 bool changed = false;
6023 TypeCacheEntry *typentry;
6024 FunctionCallInfoData locfcinfo;
6025
6026 element_type = ARR_ELEMTYPE(array);
6027 ndim = ARR_NDIM(array);
6028 dim = ARR_DIMS(array);
6029 nitems = ArrayGetNItems(ndim, dim);
6030
6031 /* Return input array unmodified if it is empty */
6032 if (nitems <= 0)
6033 return array;
6034
6035 /*
6036 * We can't remove elements from multi-dimensional arrays, since the
6037 * result might not be rectangular.
6038 */
6039 if (remove && ndim > 1)
6040 ereport(ERROR,
6041 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6042 errmsg("removing elements from multidimensional arrays is not supported")));
6043
6044 /*
6045 * We arrange to look up the equality function only once per series of
6046 * calls, assuming the element type doesn't change underneath us.
6047 */
6048 typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
6049 if (typentry == NULL ||
6050 typentry->type_id != element_type)
6051 {
6052 typentry = lookup_type_cache(element_type,
6053 TYPECACHE_EQ_OPR_FINFO);
6054 if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
6055 ereport(ERROR,
6056 (errcode(ERRCODE_UNDEFINED_FUNCTION),
6057 errmsg("could not identify an equality operator for type %s",
6058 format_type_be(element_type))));
6059 fcinfo->flinfo->fn_extra = (void *) typentry;
6060 }
6061 typlen = typentry->typlen;
6062 typbyval = typentry->typbyval;
6063 typalign = typentry->typalign;
6064
6065 /*
6066 * Detoast values if they are toasted. The replacement value must be
6067 * detoasted for insertion into the result array, while detoasting the
6068 * search value only once saves cycles.
6069 */
6070 if (typlen == -1)
6071 {
6072 if (!search_isnull)
6073 search = PointerGetDatum(PG_DETOAST_DATUM(search));
6074 if (!replace_isnull)
6075 replace = PointerGetDatum(PG_DETOAST_DATUM(replace));
6076 }
6077
6078 /* Prepare to apply the comparison operator */
6079 InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
6080 collation, NULL, NULL);
6081
6082 /* Allocate temporary arrays for new values */
6083 values = (Datum *) palloc(nitems * sizeof(Datum));
6084 nulls = (bool *) palloc(nitems * sizeof(bool));
6085
6086 /* Loop over source data */
6087 arraydataptr = ARR_DATA_PTR(array);
6088 bitmap = ARR_NULLBITMAP(array);
6089 bitmask = 1;
6090 hasnulls = false;
6091 nresult = 0;
6092
6093 for (i = 0; i < nitems; i++)
6094 {
6095 Datum elt;
6096 bool isNull;
6097 bool oprresult;
6098 bool skip = false;
6099
6100 /* Get source element, checking for NULL */
6101 if (bitmap && (*bitmap & bitmask) == 0)
6102 {
6103 isNull = true;
6104 /* If searching for NULL, we have a match */
6105 if (search_isnull)
6106 {
6107 if (remove)
6108 {
6109 skip = true;
6110 changed = true;
6111 }
6112 else if (!replace_isnull)
6113 {
6114 values[nresult] = replace;
6115 isNull = false;
6116 changed = true;
6117 }
6118 }
6119 }
6120 else
6121 {
6122 isNull = false;
6123 elt = fetch_att(arraydataptr, typbyval, typlen);
6124 arraydataptr = att_addlength_datum(arraydataptr, typlen, elt);
6125 arraydataptr = (char *) att_align_nominal(arraydataptr, typalign);
6126
6127 if (search_isnull)
6128 {
6129 /* no match possible, keep element */
6130 values[nresult] = elt;
6131 }
6132 else
6133 {
6134 /*
6135 * Apply the operator to the element pair
6136 */
6137 locfcinfo.arg[0] = elt;
6138 locfcinfo.arg[1] = search;
6139 locfcinfo.argnull[0] = false;
6140 locfcinfo.argnull[1] = false;
6141 locfcinfo.isnull = false;
6142 oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
6143 if (!oprresult)
6144 {
6145 /* no match, keep element */
6146 values[nresult] = elt;
6147 }
6148 else
6149 {
6150 /* match, so replace or delete */
6151 changed = true;
6152 if (remove)
6153 skip = true;
6154 else
6155 {
6156 values[nresult] = replace;
6157 isNull = replace_isnull;
6158 }
6159 }
6160 }
6161 }
6162
6163 if (!skip)
6164 {
6165 nulls[nresult] = isNull;
6166 if (isNull)
6167 hasnulls = true;
6168 else
6169 {
6170 /* Update total result size */
6171 nbytes = att_addlength_datum(nbytes, typlen, values[nresult]);
6172 nbytes = att_align_nominal(nbytes, typalign);
6173 /* check for overflow of total request */
6174 if (!AllocSizeIsValid(nbytes))
6175 ereport(ERROR,
6176 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
6177 errmsg("array size exceeds the maximum allowed (%d)",
6178 (int) MaxAllocSize)));
6179 }
6180 nresult++;
6181 }
6182
6183 /* advance bitmap pointer if any */
6184 if (bitmap)
6185 {
6186 bitmask <<= 1;
6187 if (bitmask == 0x100)
6188 {
6189 bitmap++;
6190 bitmask = 1;
6191 }
6192 }
6193 }
6194
6195 /*
6196 * If not changed just return the original array
6197 */
6198 if (!changed)
6199 {
6200 pfree(values);
6201 pfree(nulls);
6202 return array;
6203 }
6204
6205 /* If all elements were removed return an empty array */
6206 if (nresult == 0)
6207 {
6208 pfree(values);
6209 pfree(nulls);
6210 return construct_empty_array(element_type);
6211 }
6212
6213 /* Allocate and initialize the result array */
6214 if (hasnulls)
6215 {
6216 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nresult);
6217 nbytes += dataoffset;
6218 }
6219 else
6220 {
6221 dataoffset = 0; /* marker for no null bitmap */
6222 nbytes += ARR_OVERHEAD_NONULLS(ndim);
6223 }
6224 result = (ArrayType *) palloc0(nbytes);
6225 SET_VARSIZE(result, nbytes);
6226 result->ndim = ndim;
6227 result->dataoffset = dataoffset;
6228 result->elemtype = element_type;
6229 memcpy(ARR_DIMS(result), ARR_DIMS(array), ndim * sizeof(int));
6230 memcpy(ARR_LBOUND(result), ARR_LBOUND(array), ndim * sizeof(int));
6231
6232 if (remove)
6233 {
6234 /* Adjust the result length */
6235 ARR_DIMS(result)[0] = nresult;
6236 }
6237
6238 /* Insert data into result array */
6239 CopyArrayEls(result,
6240 values, nulls, nresult,
6241 typlen, typbyval, typalign,
6242 false);
6243
6244 pfree(values);
6245 pfree(nulls);
6246
6247 return result;
6248 }
6249
6250 /*
6251 * Remove any occurrences of an element from an array
6252 *
6253 * If used on a multi-dimensional array this will raise an error.
6254 */
6255 Datum
array_remove(PG_FUNCTION_ARGS)6256 array_remove(PG_FUNCTION_ARGS)
6257 {
6258 ArrayType *array;
6259 Datum search = PG_GETARG_DATUM(1);
6260 bool search_isnull = PG_ARGISNULL(1);
6261
6262 if (PG_ARGISNULL(0))
6263 PG_RETURN_NULL();
6264 array = PG_GETARG_ARRAYTYPE_P(0);
6265
6266 array = array_replace_internal(array,
6267 search, search_isnull,
6268 (Datum) 0, true,
6269 true, PG_GET_COLLATION(),
6270 fcinfo);
6271 PG_RETURN_ARRAYTYPE_P(array);
6272 }
6273
6274 /*
6275 * Replace any occurrences of an element in an array
6276 */
6277 Datum
array_replace(PG_FUNCTION_ARGS)6278 array_replace(PG_FUNCTION_ARGS)
6279 {
6280 ArrayType *array;
6281 Datum search = PG_GETARG_DATUM(1);
6282 bool search_isnull = PG_ARGISNULL(1);
6283 Datum replace = PG_GETARG_DATUM(2);
6284 bool replace_isnull = PG_ARGISNULL(2);
6285
6286 if (PG_ARGISNULL(0))
6287 PG_RETURN_NULL();
6288 array = PG_GETARG_ARRAYTYPE_P(0);
6289
6290 array = array_replace_internal(array,
6291 search, search_isnull,
6292 replace, replace_isnull,
6293 false, PG_GET_COLLATION(),
6294 fcinfo);
6295 PG_RETURN_ARRAYTYPE_P(array);
6296 }
6297
6298 /*
6299 * Implements width_bucket(anyelement, anyarray).
6300 *
6301 * 'thresholds' is an array containing lower bound values for each bucket;
6302 * these must be sorted from smallest to largest, or bogus results will be
6303 * produced. If N thresholds are supplied, the output is from 0 to N:
6304 * 0 is for inputs < first threshold, N is for inputs >= last threshold.
6305 */
6306 Datum
width_bucket_array(PG_FUNCTION_ARGS)6307 width_bucket_array(PG_FUNCTION_ARGS)
6308 {
6309 Datum operand = PG_GETARG_DATUM(0);
6310 ArrayType *thresholds = PG_GETARG_ARRAYTYPE_P(1);
6311 Oid collation = PG_GET_COLLATION();
6312 Oid element_type = ARR_ELEMTYPE(thresholds);
6313 int result;
6314
6315 /* Check input */
6316 if (ARR_NDIM(thresholds) > 1)
6317 ereport(ERROR,
6318 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
6319 errmsg("thresholds must be one-dimensional array")));
6320
6321 if (array_contains_nulls(thresholds))
6322 ereport(ERROR,
6323 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
6324 errmsg("thresholds array must not contain NULLs")));
6325
6326 /* We have a dedicated implementation for float8 data */
6327 if (element_type == FLOAT8OID)
6328 result = width_bucket_array_float8(operand, thresholds);
6329 else
6330 {
6331 TypeCacheEntry *typentry;
6332
6333 /* Cache information about the input type */
6334 typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
6335 if (typentry == NULL ||
6336 typentry->type_id != element_type)
6337 {
6338 typentry = lookup_type_cache(element_type,
6339 TYPECACHE_CMP_PROC_FINFO);
6340 if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
6341 ereport(ERROR,
6342 (errcode(ERRCODE_UNDEFINED_FUNCTION),
6343 errmsg("could not identify a comparison function for type %s",
6344 format_type_be(element_type))));
6345 fcinfo->flinfo->fn_extra = (void *) typentry;
6346 }
6347
6348 /*
6349 * We have separate implementation paths for fixed- and variable-width
6350 * types, since indexing the array is a lot cheaper in the first case.
6351 */
6352 if (typentry->typlen > 0)
6353 result = width_bucket_array_fixed(operand, thresholds,
6354 collation, typentry);
6355 else
6356 result = width_bucket_array_variable(operand, thresholds,
6357 collation, typentry);
6358 }
6359
6360 /* Avoid leaking memory when handed toasted input. */
6361 PG_FREE_IF_COPY(thresholds, 1);
6362
6363 PG_RETURN_INT32(result);
6364 }
6365
6366 /*
6367 * width_bucket_array for float8 data.
6368 */
6369 static int
width_bucket_array_float8(Datum operand,ArrayType * thresholds)6370 width_bucket_array_float8(Datum operand, ArrayType *thresholds)
6371 {
6372 float8 op = DatumGetFloat8(operand);
6373 float8 *thresholds_data;
6374 int left;
6375 int right;
6376
6377 /*
6378 * Since we know the array contains no NULLs, we can just index it
6379 * directly.
6380 */
6381 thresholds_data = (float8 *) ARR_DATA_PTR(thresholds);
6382
6383 left = 0;
6384 right = ArrayGetNItems(ARR_NDIM(thresholds), ARR_DIMS(thresholds));
6385
6386 /*
6387 * If the probe value is a NaN, it's greater than or equal to all possible
6388 * threshold values (including other NaNs), so we need not search. Note
6389 * that this would give the same result as searching even if the array
6390 * contains multiple NaNs (as long as they're correctly sorted), since the
6391 * loop logic will find the rightmost of multiple equal threshold values.
6392 */
6393 if (isnan(op))
6394 return right;
6395
6396 /* Find the bucket */
6397 while (left < right)
6398 {
6399 int mid = (left + right) / 2;
6400
6401 if (isnan(thresholds_data[mid]) || op < thresholds_data[mid])
6402 right = mid;
6403 else
6404 left = mid + 1;
6405 }
6406
6407 return left;
6408 }
6409
6410 /*
6411 * width_bucket_array for generic fixed-width data types.
6412 */
6413 static int
width_bucket_array_fixed(Datum operand,ArrayType * thresholds,Oid collation,TypeCacheEntry * typentry)6414 width_bucket_array_fixed(Datum operand,
6415 ArrayType *thresholds,
6416 Oid collation,
6417 TypeCacheEntry *typentry)
6418 {
6419 char *thresholds_data;
6420 int typlen = typentry->typlen;
6421 bool typbyval = typentry->typbyval;
6422 FunctionCallInfoData locfcinfo;
6423 int left;
6424 int right;
6425
6426 /*
6427 * Since we know the array contains no NULLs, we can just index it
6428 * directly.
6429 */
6430 thresholds_data = (char *) ARR_DATA_PTR(thresholds);
6431
6432 InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2,
6433 collation, NULL, NULL);
6434
6435 /* Find the bucket */
6436 left = 0;
6437 right = ArrayGetNItems(ARR_NDIM(thresholds), ARR_DIMS(thresholds));
6438 while (left < right)
6439 {
6440 int mid = (left + right) / 2;
6441 char *ptr;
6442 int32 cmpresult;
6443
6444 ptr = thresholds_data + mid * typlen;
6445
6446 locfcinfo.arg[0] = operand;
6447 locfcinfo.arg[1] = fetch_att(ptr, typbyval, typlen);
6448 locfcinfo.argnull[0] = false;
6449 locfcinfo.argnull[1] = false;
6450 locfcinfo.isnull = false;
6451
6452 cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
6453
6454 if (cmpresult < 0)
6455 right = mid;
6456 else
6457 left = mid + 1;
6458 }
6459
6460 return left;
6461 }
6462
6463 /*
6464 * width_bucket_array for generic variable-width data types.
6465 */
6466 static int
width_bucket_array_variable(Datum operand,ArrayType * thresholds,Oid collation,TypeCacheEntry * typentry)6467 width_bucket_array_variable(Datum operand,
6468 ArrayType *thresholds,
6469 Oid collation,
6470 TypeCacheEntry *typentry)
6471 {
6472 char *thresholds_data;
6473 int typlen = typentry->typlen;
6474 bool typbyval = typentry->typbyval;
6475 char typalign = typentry->typalign;
6476 FunctionCallInfoData locfcinfo;
6477 int left;
6478 int right;
6479
6480 thresholds_data = (char *) ARR_DATA_PTR(thresholds);
6481
6482 InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2,
6483 collation, NULL, NULL);
6484
6485 /* Find the bucket */
6486 left = 0;
6487 right = ArrayGetNItems(ARR_NDIM(thresholds), ARR_DIMS(thresholds));
6488 while (left < right)
6489 {
6490 int mid = (left + right) / 2;
6491 char *ptr;
6492 int i;
6493 int32 cmpresult;
6494
6495 /* Locate mid'th array element by advancing from left element */
6496 ptr = thresholds_data;
6497 for (i = left; i < mid; i++)
6498 {
6499 ptr = att_addlength_pointer(ptr, typlen, ptr);
6500 ptr = (char *) att_align_nominal(ptr, typalign);
6501 }
6502
6503 locfcinfo.arg[0] = operand;
6504 locfcinfo.arg[1] = fetch_att(ptr, typbyval, typlen);
6505 locfcinfo.argnull[0] = false;
6506 locfcinfo.argnull[1] = false;
6507 locfcinfo.isnull = false;
6508
6509 cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
6510
6511 if (cmpresult < 0)
6512 right = mid;
6513 else
6514 {
6515 left = mid + 1;
6516
6517 /*
6518 * Move the thresholds pointer to match new "left" index, so we
6519 * don't have to seek over those elements again. This trick
6520 * ensures we do only O(N) array indexing work, not O(N^2).
6521 */
6522 ptr = att_addlength_pointer(ptr, typlen, ptr);
6523 thresholds_data = (char *) att_align_nominal(ptr, typalign);
6524 }
6525 }
6526
6527 return left;
6528 }
6529