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