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