Message fixes
[PostgreSQL.git] / src / backend / utils / adt / arrayfuncs.c
blob86a6f372b3b0258b5dd7473f5a50ad676df83871
1 /*-------------------------------------------------------------------------
3 * arrayfuncs.c
4 * Support functions for arrays.
6 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
10 * IDENTIFICATION
11 * $PostgreSQL$
13 *-------------------------------------------------------------------------
15 #include "postgres.h"
17 #include <ctype.h>
19 #include "funcapi.h"
20 #include "libpq/pqformat.h"
21 #include "parser/parse_coerce.h"
22 #include "utils/array.h"
23 #include "utils/builtins.h"
24 #include "utils/datum.h"
25 #include "utils/lsyscache.h"
26 #include "utils/memutils.h"
27 #include "utils/typcache.h"
31 * GUC parameter
33 bool Array_nulls = true;
36 * Local definitions
38 #define ASSGN "="
40 typedef enum
42 ARRAY_NO_LEVEL,
43 ARRAY_LEVEL_STARTED,
44 ARRAY_ELEM_STARTED,
45 ARRAY_ELEM_COMPLETED,
46 ARRAY_QUOTED_ELEM_STARTED,
47 ARRAY_QUOTED_ELEM_COMPLETED,
48 ARRAY_ELEM_DELIMITED,
49 ARRAY_LEVEL_COMPLETED,
50 ARRAY_LEVEL_DELIMITED
51 } ArrayParseState;
53 static int ArrayCount(const char *str, int *dim, char typdelim);
54 static void ReadArrayStr(char *arrayStr, const char *origStr,
55 int nitems, int ndim, int *dim,
56 FmgrInfo *inputproc, Oid typioparam, int32 typmod,
57 char typdelim,
58 int typlen, bool typbyval, char typalign,
59 Datum *values, bool *nulls,
60 bool *hasnulls, int32 *nbytes);
61 static void ReadArrayBinary(StringInfo buf, int nitems,
62 FmgrInfo *receiveproc, Oid typioparam, int32 typmod,
63 int typlen, bool typbyval, char typalign,
64 Datum *values, bool *nulls,
65 bool *hasnulls, int32 *nbytes);
66 static void CopyArrayEls(ArrayType *array,
67 Datum *values, bool *nulls, int nitems,
68 int typlen, bool typbyval, char typalign,
69 bool freedata);
70 static bool array_get_isnull(const bits8 *nullbitmap, int offset);
71 static void array_set_isnull(bits8 *nullbitmap, int offset, bool isNull);
72 static Datum ArrayCast(char *value, bool byval, int len);
73 static int ArrayCastAndSet(Datum src,
74 int typlen, bool typbyval, char typalign,
75 char *dest);
76 static char *array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
77 int typlen, bool typbyval, char typalign);
78 static int array_nelems_size(char *ptr, int offset, bits8 *nullbitmap,
79 int nitems, int typlen, bool typbyval, char typalign);
80 static int array_copy(char *destptr, int nitems,
81 char *srcptr, int offset, bits8 *nullbitmap,
82 int typlen, bool typbyval, char typalign);
83 static int array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
84 int ndim, int *dim, int *lb,
85 int *st, int *endp,
86 int typlen, bool typbyval, char typalign);
87 static void array_extract_slice(ArrayType *newarray,
88 int ndim, int *dim, int *lb,
89 char *arraydataptr, bits8 *arraynullsptr,
90 int *st, int *endp,
91 int typlen, bool typbyval, char typalign);
92 static void array_insert_slice(ArrayType *destArray, ArrayType *origArray,
93 ArrayType *srcArray,
94 int ndim, int *dim, int *lb,
95 int *st, int *endp,
96 int typlen, bool typbyval, char typalign);
97 static int array_cmp(FunctionCallInfo fcinfo);
98 static ArrayType *create_array_envelope(int ndims, int *dimv, int *lbv, int nbytes,
99 Oid elmtype, int dataoffset);
100 static ArrayType *array_fill_internal(ArrayType *dims, ArrayType *lbs,
101 Datum value, bool isnull, Oid elmtype,
102 FunctionCallInfo fcinfo);
106 * array_in :
107 * converts an array from the external format in "string" to
108 * its internal format.
110 * return value :
111 * the internal representation of the input array
113 Datum
114 array_in(PG_FUNCTION_ARGS)
116 char *string = PG_GETARG_CSTRING(0); /* external form */
117 Oid element_type = PG_GETARG_OID(1); /* type of an array
118 * element */
119 int32 typmod = PG_GETARG_INT32(2); /* typmod for array elements */
120 int typlen;
121 bool typbyval;
122 char typalign;
123 char typdelim;
124 Oid typioparam;
125 char *string_save,
127 int i,
128 nitems;
129 Datum *dataPtr;
130 bool *nullsPtr;
131 bool hasnulls;
132 int32 nbytes;
133 int32 dataoffset;
134 ArrayType *retval;
135 int ndim,
136 dim[MAXDIM],
137 lBound[MAXDIM];
138 ArrayMetaState *my_extra;
141 * We arrange to look up info about element type, including its input
142 * conversion proc, only once per series of calls, assuming the element
143 * type doesn't change underneath us.
145 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
146 if (my_extra == NULL)
148 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
149 sizeof(ArrayMetaState));
150 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
151 my_extra->element_type = ~element_type;
154 if (my_extra->element_type != element_type)
157 * Get info about element type, including its input conversion proc
159 get_type_io_data(element_type, IOFunc_input,
160 &my_extra->typlen, &my_extra->typbyval,
161 &my_extra->typalign, &my_extra->typdelim,
162 &my_extra->typioparam, &my_extra->typiofunc);
163 fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
164 fcinfo->flinfo->fn_mcxt);
165 my_extra->element_type = element_type;
167 typlen = my_extra->typlen;
168 typbyval = my_extra->typbyval;
169 typalign = my_extra->typalign;
170 typdelim = my_extra->typdelim;
171 typioparam = my_extra->typioparam;
173 /* Make a modifiable copy of the input */
174 string_save = pstrdup(string);
177 * If the input string starts with dimension info, read and use that.
178 * Otherwise, we require the input to be in curly-brace style, and we
179 * prescan the input to determine dimensions.
181 * Dimension info takes the form of one or more [n] or [m:n] items. The
182 * outer loop iterates once per dimension item.
184 p = string_save;
185 ndim = 0;
186 for (;;)
188 char *q;
189 int ub;
192 * Note: we currently allow whitespace between, but not within,
193 * dimension items.
195 while (isspace((unsigned char) *p))
196 p++;
197 if (*p != '[')
198 break; /* no more dimension items */
199 p++;
200 if (ndim >= MAXDIM)
201 ereport(ERROR,
202 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
203 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
204 ndim, MAXDIM)));
206 for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++);
207 if (q == p) /* no digits? */
208 ereport(ERROR,
209 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
210 errmsg("missing dimension value")));
212 if (*q == ':')
214 /* [m:n] format */
215 *q = '\0';
216 lBound[ndim] = atoi(p);
217 p = q + 1;
218 for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++);
219 if (q == p) /* no digits? */
220 ereport(ERROR,
221 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
222 errmsg("missing dimension value")));
224 else
226 /* [n] format */
227 lBound[ndim] = 1;
229 if (*q != ']')
230 ereport(ERROR,
231 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
232 errmsg("missing \"]\" in array dimensions")));
234 *q = '\0';
235 ub = atoi(p);
236 p = q + 1;
237 if (ub < lBound[ndim])
238 ereport(ERROR,
239 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
240 errmsg("upper bound cannot be less than lower bound")));
242 dim[ndim] = ub - lBound[ndim] + 1;
243 ndim++;
246 if (ndim == 0)
248 /* No array dimensions, so intuit dimensions from brace structure */
249 if (*p != '{')
250 ereport(ERROR,
251 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
252 errmsg("array value must start with \"{\" or dimension information")));
253 ndim = ArrayCount(p, dim, typdelim);
254 for (i = 0; i < ndim; i++)
255 lBound[i] = 1;
257 else
259 int ndim_braces,
260 dim_braces[MAXDIM];
262 /* If array dimensions are given, expect '=' operator */
263 if (strncmp(p, ASSGN, strlen(ASSGN)) != 0)
264 ereport(ERROR,
265 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
266 errmsg("missing assignment operator")));
267 p += strlen(ASSGN);
268 while (isspace((unsigned char) *p))
269 p++;
272 * intuit dimensions from brace structure -- it better match what we
273 * were given
275 if (*p != '{')
276 ereport(ERROR,
277 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
278 errmsg("array value must start with \"{\" or dimension information")));
279 ndim_braces = ArrayCount(p, dim_braces, typdelim);
280 if (ndim_braces != ndim)
281 ereport(ERROR,
282 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
283 errmsg("array dimensions incompatible with array literal")));
284 for (i = 0; i < ndim; ++i)
286 if (dim[i] != dim_braces[i])
287 ereport(ERROR,
288 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
289 errmsg("array dimensions incompatible with array literal")));
293 #ifdef ARRAYDEBUG
294 printf("array_in- ndim %d (", ndim);
295 for (i = 0; i < ndim; i++)
297 printf(" %d", dim[i]);
299 printf(") for %s\n", string);
300 #endif
302 /* This checks for overflow of the array dimensions */
303 nitems = ArrayGetNItems(ndim, dim);
304 /* Empty array? */
305 if (nitems == 0)
306 PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
308 dataPtr = (Datum *) palloc(nitems * sizeof(Datum));
309 nullsPtr = (bool *) palloc(nitems * sizeof(bool));
310 ReadArrayStr(p, string,
311 nitems, ndim, dim,
312 &my_extra->proc, typioparam, typmod,
313 typdelim,
314 typlen, typbyval, typalign,
315 dataPtr, nullsPtr,
316 &hasnulls, &nbytes);
317 if (hasnulls)
319 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
320 nbytes += dataoffset;
322 else
324 dataoffset = 0; /* marker for no null bitmap */
325 nbytes += ARR_OVERHEAD_NONULLS(ndim);
327 retval = (ArrayType *) palloc0(nbytes);
328 SET_VARSIZE(retval, nbytes);
329 retval->ndim = ndim;
330 retval->dataoffset = dataoffset;
331 retval->elemtype = element_type;
332 memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
333 memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
335 CopyArrayEls(retval,
336 dataPtr, nullsPtr, nitems,
337 typlen, typbyval, typalign,
338 true);
340 pfree(dataPtr);
341 pfree(nullsPtr);
342 pfree(string_save);
344 PG_RETURN_ARRAYTYPE_P(retval);
348 * ArrayCount
349 * Determines the dimensions for an array string.
351 * Returns number of dimensions as function result. The axis lengths are
352 * returned in dim[], which must be of size MAXDIM.
354 static int
355 ArrayCount(const char *str, int *dim, char typdelim)
357 int nest_level = 0,
359 int ndim = 1,
360 temp[MAXDIM],
361 nelems[MAXDIM],
362 nelems_last[MAXDIM];
363 bool in_quotes = false;
364 bool eoArray = false;
365 bool empty_array = true;
366 const char *ptr;
367 ArrayParseState parse_state = ARRAY_NO_LEVEL;
369 for (i = 0; i < MAXDIM; ++i)
371 temp[i] = dim[i] = 0;
372 nelems_last[i] = nelems[i] = 1;
375 ptr = str;
376 while (!eoArray)
378 bool itemdone = false;
380 while (!itemdone)
382 if (parse_state == ARRAY_ELEM_STARTED ||
383 parse_state == ARRAY_QUOTED_ELEM_STARTED)
384 empty_array = false;
386 switch (*ptr)
388 case '\0':
389 /* Signal a premature end of the string */
390 ereport(ERROR,
391 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
392 errmsg("malformed array literal: \"%s\"", str)));
393 break;
394 case '\\':
397 * An escape must be after a level start, after an element
398 * start, or after an element delimiter. In any case we
399 * now must be past an element start.
401 if (parse_state != ARRAY_LEVEL_STARTED &&
402 parse_state != ARRAY_ELEM_STARTED &&
403 parse_state != ARRAY_QUOTED_ELEM_STARTED &&
404 parse_state != ARRAY_ELEM_DELIMITED)
405 ereport(ERROR,
406 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
407 errmsg("malformed array literal: \"%s\"", str)));
408 if (parse_state != ARRAY_QUOTED_ELEM_STARTED)
409 parse_state = ARRAY_ELEM_STARTED;
410 /* skip the escaped character */
411 if (*(ptr + 1))
412 ptr++;
413 else
414 ereport(ERROR,
415 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
416 errmsg("malformed array literal: \"%s\"", str)));
417 break;
418 case '\"':
421 * A quote must be after a level start, after a quoted
422 * element start, or after an element delimiter. In any
423 * case we now must be past an element start.
425 if (parse_state != ARRAY_LEVEL_STARTED &&
426 parse_state != ARRAY_QUOTED_ELEM_STARTED &&
427 parse_state != ARRAY_ELEM_DELIMITED)
428 ereport(ERROR,
429 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
430 errmsg("malformed array literal: \"%s\"", str)));
431 in_quotes = !in_quotes;
432 if (in_quotes)
433 parse_state = ARRAY_QUOTED_ELEM_STARTED;
434 else
435 parse_state = ARRAY_QUOTED_ELEM_COMPLETED;
436 break;
437 case '{':
438 if (!in_quotes)
441 * A left brace can occur if no nesting has occurred
442 * yet, after a level start, or after a level
443 * delimiter.
445 if (parse_state != ARRAY_NO_LEVEL &&
446 parse_state != ARRAY_LEVEL_STARTED &&
447 parse_state != ARRAY_LEVEL_DELIMITED)
448 ereport(ERROR,
449 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
450 errmsg("malformed array literal: \"%s\"", str)));
451 parse_state = ARRAY_LEVEL_STARTED;
452 if (nest_level >= MAXDIM)
453 ereport(ERROR,
454 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
455 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
456 nest_level, MAXDIM)));
457 temp[nest_level] = 0;
458 nest_level++;
459 if (ndim < nest_level)
460 ndim = nest_level;
462 break;
463 case '}':
464 if (!in_quotes)
467 * A right brace can occur after an element start, an
468 * element completion, a quoted element completion, or
469 * a level completion.
471 if (parse_state != ARRAY_ELEM_STARTED &&
472 parse_state != ARRAY_ELEM_COMPLETED &&
473 parse_state != ARRAY_QUOTED_ELEM_COMPLETED &&
474 parse_state != ARRAY_LEVEL_COMPLETED &&
475 !(nest_level == 1 && parse_state == ARRAY_LEVEL_STARTED))
476 ereport(ERROR,
477 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
478 errmsg("malformed array literal: \"%s\"", str)));
479 parse_state = ARRAY_LEVEL_COMPLETED;
480 if (nest_level == 0)
481 ereport(ERROR,
482 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
483 errmsg("malformed array literal: \"%s\"", str)));
484 nest_level--;
486 if ((nelems_last[nest_level] != 1) &&
487 (nelems[nest_level] != nelems_last[nest_level]))
488 ereport(ERROR,
489 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
490 errmsg("multidimensional arrays must have "
491 "array expressions with matching "
492 "dimensions")));
493 nelems_last[nest_level] = nelems[nest_level];
494 nelems[nest_level] = 1;
495 if (nest_level == 0)
496 eoArray = itemdone = true;
497 else
500 * We don't set itemdone here; see comments in
501 * ReadArrayStr
503 temp[nest_level - 1]++;
506 break;
507 default:
508 if (!in_quotes)
510 if (*ptr == typdelim)
513 * Delimiters can occur after an element start, an
514 * element completion, a quoted element
515 * completion, or a level completion.
517 if (parse_state != ARRAY_ELEM_STARTED &&
518 parse_state != ARRAY_ELEM_COMPLETED &&
519 parse_state != ARRAY_QUOTED_ELEM_COMPLETED &&
520 parse_state != ARRAY_LEVEL_COMPLETED)
521 ereport(ERROR,
522 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
523 errmsg("malformed array literal: \"%s\"", str)));
524 if (parse_state == ARRAY_LEVEL_COMPLETED)
525 parse_state = ARRAY_LEVEL_DELIMITED;
526 else
527 parse_state = ARRAY_ELEM_DELIMITED;
528 itemdone = true;
529 nelems[nest_level - 1]++;
531 else if (!isspace((unsigned char) *ptr))
534 * Other non-space characters must be after a
535 * level start, after an element start, or after
536 * an element delimiter. In any case we now must
537 * be past an element start.
539 if (parse_state != ARRAY_LEVEL_STARTED &&
540 parse_state != ARRAY_ELEM_STARTED &&
541 parse_state != ARRAY_ELEM_DELIMITED)
542 ereport(ERROR,
543 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
544 errmsg("malformed array literal: \"%s\"", str)));
545 parse_state = ARRAY_ELEM_STARTED;
548 break;
550 if (!itemdone)
551 ptr++;
553 temp[ndim - 1]++;
554 ptr++;
557 /* only whitespace is allowed after the closing brace */
558 while (*ptr)
560 if (!isspace((unsigned char) *ptr++))
561 ereport(ERROR,
562 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
563 errmsg("malformed array literal: \"%s\"", str)));
566 /* special case for an empty array */
567 if (empty_array)
568 return 0;
570 for (i = 0; i < ndim; ++i)
571 dim[i] = temp[i];
573 return ndim;
577 * ReadArrayStr :
578 * parses the array string pointed to by "arrayStr" and converts the values
579 * to internal format. Unspecified elements are initialized to nulls.
580 * The array dimensions must already have been determined.
582 * Inputs:
583 * arrayStr: the string to parse.
584 * CAUTION: the contents of "arrayStr" will be modified!
585 * origStr: the unmodified input string, used only in error messages.
586 * nitems: total number of array elements, as already determined.
587 * ndim: number of array dimensions
588 * dim[]: array axis lengths
589 * inputproc: type-specific input procedure for element datatype.
590 * typioparam, typmod: auxiliary values to pass to inputproc.
591 * typdelim: the value delimiter (type-specific).
592 * typlen, typbyval, typalign: storage parameters of element datatype.
594 * Outputs:
595 * values[]: filled with converted data values.
596 * nulls[]: filled with is-null markers.
597 * *hasnulls: set TRUE iff there are any null elements.
598 * *nbytes: set to total size of data area needed (including alignment
599 * padding but not including array header overhead).
601 * Note that values[] and nulls[] are allocated by the caller, and must have
602 * nitems elements.
604 static void
605 ReadArrayStr(char *arrayStr,
606 const char *origStr,
607 int nitems,
608 int ndim,
609 int *dim,
610 FmgrInfo *inputproc,
611 Oid typioparam,
612 int32 typmod,
613 char typdelim,
614 int typlen,
615 bool typbyval,
616 char typalign,
617 Datum *values,
618 bool *nulls,
619 bool *hasnulls,
620 int32 *nbytes)
622 int i,
623 nest_level = 0;
624 char *srcptr;
625 bool in_quotes = false;
626 bool eoArray = false;
627 bool hasnull;
628 int32 totbytes;
629 int indx[MAXDIM],
630 prod[MAXDIM];
632 mda_get_prod(ndim, dim, prod);
633 MemSet(indx, 0, sizeof(indx));
635 /* Initialize is-null markers to true */
636 memset(nulls, true, nitems * sizeof(bool));
639 * We have to remove " and \ characters to create a clean item value to
640 * pass to the datatype input routine. We overwrite each item value
641 * in-place within arrayStr to do this. srcptr is the current scan point,
642 * and dstptr is where we are copying to.
644 * We also want to suppress leading and trailing unquoted whitespace. We
645 * use the leadingspace flag to suppress leading space. Trailing space is
646 * tracked by using dstendptr to point to the last significant output
647 * character.
649 * The error checking in this routine is mostly pro-forma, since we expect
650 * that ArrayCount() already validated the string.
652 srcptr = arrayStr;
653 while (!eoArray)
655 bool itemdone = false;
656 bool leadingspace = true;
657 bool hasquoting = false;
658 char *itemstart;
659 char *dstptr;
660 char *dstendptr;
662 i = -1;
663 itemstart = dstptr = dstendptr = srcptr;
665 while (!itemdone)
667 switch (*srcptr)
669 case '\0':
670 /* Signal a premature end of the string */
671 ereport(ERROR,
672 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
673 errmsg("malformed array literal: \"%s\"",
674 origStr)));
675 break;
676 case '\\':
677 /* Skip backslash, copy next character as-is. */
678 srcptr++;
679 if (*srcptr == '\0')
680 ereport(ERROR,
681 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
682 errmsg("malformed array literal: \"%s\"",
683 origStr)));
684 *dstptr++ = *srcptr++;
685 /* Treat the escaped character as non-whitespace */
686 leadingspace = false;
687 dstendptr = dstptr;
688 hasquoting = true; /* can't be a NULL marker */
689 break;
690 case '\"':
691 in_quotes = !in_quotes;
692 if (in_quotes)
693 leadingspace = false;
694 else
697 * Advance dstendptr when we exit in_quotes; this
698 * saves having to do it in all the other in_quotes
699 * cases.
701 dstendptr = dstptr;
703 hasquoting = true; /* can't be a NULL marker */
704 srcptr++;
705 break;
706 case '{':
707 if (!in_quotes)
709 if (nest_level >= ndim)
710 ereport(ERROR,
711 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
712 errmsg("malformed array literal: \"%s\"",
713 origStr)));
714 nest_level++;
715 indx[nest_level - 1] = 0;
716 srcptr++;
718 else
719 *dstptr++ = *srcptr++;
720 break;
721 case '}':
722 if (!in_quotes)
724 if (nest_level == 0)
725 ereport(ERROR,
726 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
727 errmsg("malformed array literal: \"%s\"",
728 origStr)));
729 if (i == -1)
730 i = ArrayGetOffset0(ndim, indx, prod);
731 indx[nest_level - 1] = 0;
732 nest_level--;
733 if (nest_level == 0)
734 eoArray = itemdone = true;
735 else
736 indx[nest_level - 1]++;
737 srcptr++;
739 else
740 *dstptr++ = *srcptr++;
741 break;
742 default:
743 if (in_quotes)
744 *dstptr++ = *srcptr++;
745 else if (*srcptr == typdelim)
747 if (i == -1)
748 i = ArrayGetOffset0(ndim, indx, prod);
749 itemdone = true;
750 indx[ndim - 1]++;
751 srcptr++;
753 else if (isspace((unsigned char) *srcptr))
756 * If leading space, drop it immediately. Else, copy
757 * but don't advance dstendptr.
759 if (leadingspace)
760 srcptr++;
761 else
762 *dstptr++ = *srcptr++;
764 else
766 *dstptr++ = *srcptr++;
767 leadingspace = false;
768 dstendptr = dstptr;
770 break;
774 Assert(dstptr < srcptr);
775 *dstendptr = '\0';
777 if (i < 0 || i >= nitems)
778 ereport(ERROR,
779 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
780 errmsg("malformed array literal: \"%s\"",
781 origStr)));
783 if (Array_nulls && !hasquoting &&
784 pg_strcasecmp(itemstart, "NULL") == 0)
786 /* it's a NULL item */
787 values[i] = InputFunctionCall(inputproc, NULL,
788 typioparam, typmod);
789 nulls[i] = true;
791 else
793 values[i] = InputFunctionCall(inputproc, itemstart,
794 typioparam, typmod);
795 nulls[i] = false;
800 * Check for nulls, compute total data space needed
802 hasnull = false;
803 totbytes = 0;
804 for (i = 0; i < nitems; i++)
806 if (nulls[i])
807 hasnull = true;
808 else
810 /* let's just make sure data is not toasted */
811 if (typlen == -1)
812 values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
813 totbytes = att_addlength_datum(totbytes, typlen, values[i]);
814 totbytes = att_align_nominal(totbytes, typalign);
815 /* check for overflow of total request */
816 if (!AllocSizeIsValid(totbytes))
817 ereport(ERROR,
818 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
819 errmsg("array size exceeds the maximum allowed (%d)",
820 (int) MaxAllocSize)));
823 *hasnulls = hasnull;
824 *nbytes = totbytes;
829 * Copy data into an array object from a temporary array of Datums.
831 * array: array object (with header fields already filled in)
832 * values: array of Datums to be copied
833 * nulls: array of is-null flags (can be NULL if no nulls)
834 * nitems: number of Datums to be copied
835 * typbyval, typlen, typalign: info about element datatype
836 * freedata: if TRUE and element type is pass-by-ref, pfree data values
837 * referenced by Datums after copying them.
839 * If the input data is of varlena type, the caller must have ensured that
840 * the values are not toasted. (Doing it here doesn't work since the
841 * caller has already allocated space for the array...)
843 static void
844 CopyArrayEls(ArrayType *array,
845 Datum *values,
846 bool *nulls,
847 int nitems,
848 int typlen,
849 bool typbyval,
850 char typalign,
851 bool freedata)
853 char *p = ARR_DATA_PTR(array);
854 bits8 *bitmap = ARR_NULLBITMAP(array);
855 int bitval = 0;
856 int bitmask = 1;
857 int i;
859 if (typbyval)
860 freedata = false;
862 for (i = 0; i < nitems; i++)
864 if (nulls && nulls[i])
866 if (!bitmap) /* shouldn't happen */
867 elog(ERROR, "null array element where not supported");
868 /* bitmap bit stays 0 */
870 else
872 bitval |= bitmask;
873 p += ArrayCastAndSet(values[i], typlen, typbyval, typalign, p);
874 if (freedata)
875 pfree(DatumGetPointer(values[i]));
877 if (bitmap)
879 bitmask <<= 1;
880 if (bitmask == 0x100)
882 *bitmap++ = bitval;
883 bitval = 0;
884 bitmask = 1;
889 if (bitmap && bitmask != 1)
890 *bitmap = bitval;
894 * array_out :
895 * takes the internal representation of an array and returns a string
896 * containing the array in its external format.
898 Datum
899 array_out(PG_FUNCTION_ARGS)
901 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
902 Oid element_type = ARR_ELEMTYPE(v);
903 int typlen;
904 bool typbyval;
905 char typalign;
906 char typdelim;
907 char *p,
908 *tmp,
909 *retval,
910 **values,
911 dims_str[(MAXDIM * 33) + 2];
914 * 33 per dim since we assume 15 digits per number + ':' +'[]'
916 * +2 allows for assignment operator + trailing null
918 bits8 *bitmap;
919 int bitmask;
920 bool *needquotes,
921 needdims = false;
922 int nitems,
923 overall_length,
927 indx[MAXDIM];
928 int ndim,
929 *dims,
930 *lb;
931 ArrayMetaState *my_extra;
934 * We arrange to look up info about element type, including its output
935 * conversion proc, only once per series of calls, assuming the element
936 * type doesn't change underneath us.
938 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
939 if (my_extra == NULL)
941 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
942 sizeof(ArrayMetaState));
943 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
944 my_extra->element_type = ~element_type;
947 if (my_extra->element_type != element_type)
950 * Get info about element type, including its output conversion proc
952 get_type_io_data(element_type, IOFunc_output,
953 &my_extra->typlen, &my_extra->typbyval,
954 &my_extra->typalign, &my_extra->typdelim,
955 &my_extra->typioparam, &my_extra->typiofunc);
956 fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
957 fcinfo->flinfo->fn_mcxt);
958 my_extra->element_type = element_type;
960 typlen = my_extra->typlen;
961 typbyval = my_extra->typbyval;
962 typalign = my_extra->typalign;
963 typdelim = my_extra->typdelim;
965 ndim = ARR_NDIM(v);
966 dims = ARR_DIMS(v);
967 lb = ARR_LBOUND(v);
968 nitems = ArrayGetNItems(ndim, dims);
970 if (nitems == 0)
972 retval = pstrdup("{}");
973 PG_RETURN_CSTRING(retval);
977 * we will need to add explicit dimensions if any dimension has a lower
978 * bound other than one
980 for (i = 0; i < ndim; i++)
982 if (lb[i] != 1)
984 needdims = true;
985 break;
990 * Convert all values to string form, count total space needed (including
991 * any overhead such as escaping backslashes), and detect whether each
992 * item needs double quotes.
994 values = (char **) palloc(nitems * sizeof(char *));
995 needquotes = (bool *) palloc(nitems * sizeof(bool));
996 overall_length = 1; /* don't forget to count \0 at end. */
998 p = ARR_DATA_PTR(v);
999 bitmap = ARR_NULLBITMAP(v);
1000 bitmask = 1;
1002 for (i = 0; i < nitems; i++)
1004 bool needquote;
1006 /* Get source element, checking for NULL */
1007 if (bitmap && (*bitmap & bitmask) == 0)
1009 values[i] = pstrdup("NULL");
1010 overall_length += 4;
1011 needquote = false;
1013 else
1015 Datum itemvalue;
1017 itemvalue = fetch_att(p, typbyval, typlen);
1018 values[i] = OutputFunctionCall(&my_extra->proc, itemvalue);
1019 p = att_addlength_pointer(p, typlen, p);
1020 p = (char *) att_align_nominal(p, typalign);
1022 /* count data plus backslashes; detect chars needing quotes */
1023 if (values[i][0] == '\0')
1024 needquote = true; /* force quotes for empty string */
1025 else if (pg_strcasecmp(values[i], "NULL") == 0)
1026 needquote = true; /* force quotes for literal NULL */
1027 else
1028 needquote = false;
1030 for (tmp = values[i]; *tmp != '\0'; tmp++)
1032 char ch = *tmp;
1034 overall_length += 1;
1035 if (ch == '"' || ch == '\\')
1037 needquote = true;
1038 overall_length += 1;
1040 else if (ch == '{' || ch == '}' || ch == typdelim ||
1041 isspace((unsigned char) ch))
1042 needquote = true;
1046 needquotes[i] = needquote;
1048 /* Count the pair of double quotes, if needed */
1049 if (needquote)
1050 overall_length += 2;
1051 /* and the comma */
1052 overall_length += 1;
1054 /* advance bitmap pointer if any */
1055 if (bitmap)
1057 bitmask <<= 1;
1058 if (bitmask == 0x100)
1060 bitmap++;
1061 bitmask = 1;
1067 * count total number of curly braces in output string
1069 for (i = j = 0, k = 1; i < ndim; i++)
1070 k *= dims[i], j += k;
1072 dims_str[0] = '\0';
1074 /* add explicit dimensions if required */
1075 if (needdims)
1077 char *ptr = dims_str;
1079 for (i = 0; i < ndim; i++)
1081 sprintf(ptr, "[%d:%d]", lb[i], lb[i] + dims[i] - 1);
1082 ptr += strlen(ptr);
1084 *ptr++ = *ASSGN;
1085 *ptr = '\0';
1088 retval = (char *) palloc(strlen(dims_str) + overall_length + 2 * j);
1089 p = retval;
1091 #define APPENDSTR(str) (strcpy(p, (str)), p += strlen(p))
1092 #define APPENDCHAR(ch) (*p++ = (ch), *p = '\0')
1094 if (needdims)
1095 APPENDSTR(dims_str);
1096 APPENDCHAR('{');
1097 for (i = 0; i < ndim; i++)
1098 indx[i] = 0;
1099 j = 0;
1100 k = 0;
1103 for (i = j; i < ndim - 1; i++)
1104 APPENDCHAR('{');
1106 if (needquotes[k])
1108 APPENDCHAR('"');
1109 for (tmp = values[k]; *tmp; tmp++)
1111 char ch = *tmp;
1113 if (ch == '"' || ch == '\\')
1114 *p++ = '\\';
1115 *p++ = ch;
1117 *p = '\0';
1118 APPENDCHAR('"');
1120 else
1121 APPENDSTR(values[k]);
1122 pfree(values[k++]);
1124 for (i = ndim - 1; i >= 0; i--)
1126 indx[i] = (indx[i] + 1) % dims[i];
1127 if (indx[i])
1129 APPENDCHAR(typdelim);
1130 break;
1132 else
1133 APPENDCHAR('}');
1135 j = i;
1136 } while (j != -1);
1138 #undef APPENDSTR
1139 #undef APPENDCHAR
1141 pfree(values);
1142 pfree(needquotes);
1144 PG_RETURN_CSTRING(retval);
1148 * array_recv :
1149 * converts an array from the external binary format to
1150 * its internal format.
1152 * return value :
1153 * the internal representation of the input array
1155 Datum
1156 array_recv(PG_FUNCTION_ARGS)
1158 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
1159 Oid spec_element_type = PG_GETARG_OID(1); /* type of an array
1160 * element */
1161 int32 typmod = PG_GETARG_INT32(2); /* typmod for array elements */
1162 Oid element_type;
1163 int typlen;
1164 bool typbyval;
1165 char typalign;
1166 Oid typioparam;
1167 int i,
1168 nitems;
1169 Datum *dataPtr;
1170 bool *nullsPtr;
1171 bool hasnulls;
1172 int32 nbytes;
1173 int32 dataoffset;
1174 ArrayType *retval;
1175 int ndim,
1176 flags,
1177 dim[MAXDIM],
1178 lBound[MAXDIM];
1179 ArrayMetaState *my_extra;
1181 /* Get the array header information */
1182 ndim = pq_getmsgint(buf, 4);
1183 if (ndim < 0) /* we do allow zero-dimension arrays */
1184 ereport(ERROR,
1185 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1186 errmsg("invalid number of dimensions: %d", ndim)));
1187 if (ndim > MAXDIM)
1188 ereport(ERROR,
1189 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1190 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
1191 ndim, MAXDIM)));
1193 flags = pq_getmsgint(buf, 4);
1194 if (flags != 0 && flags != 1)
1195 ereport(ERROR,
1196 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1197 errmsg("invalid array flags")));
1199 element_type = pq_getmsgint(buf, sizeof(Oid));
1200 if (element_type != spec_element_type)
1202 /* XXX Can we allow taking the input element type in any cases? */
1203 ereport(ERROR,
1204 (errcode(ERRCODE_DATATYPE_MISMATCH),
1205 errmsg("wrong element type")));
1208 for (i = 0; i < ndim; i++)
1210 dim[i] = pq_getmsgint(buf, 4);
1211 lBound[i] = pq_getmsgint(buf, 4);
1214 /* This checks for overflow of array dimensions */
1215 nitems = ArrayGetNItems(ndim, dim);
1218 * We arrange to look up info about element type, including its receive
1219 * conversion proc, only once per series of calls, assuming the element
1220 * type doesn't change underneath us.
1222 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1223 if (my_extra == NULL)
1225 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1226 sizeof(ArrayMetaState));
1227 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1228 my_extra->element_type = ~element_type;
1231 if (my_extra->element_type != element_type)
1233 /* Get info about element type, including its receive proc */
1234 get_type_io_data(element_type, IOFunc_receive,
1235 &my_extra->typlen, &my_extra->typbyval,
1236 &my_extra->typalign, &my_extra->typdelim,
1237 &my_extra->typioparam, &my_extra->typiofunc);
1238 if (!OidIsValid(my_extra->typiofunc))
1239 ereport(ERROR,
1240 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1241 errmsg("no binary input function available for type %s",
1242 format_type_be(element_type))));
1243 fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1244 fcinfo->flinfo->fn_mcxt);
1245 my_extra->element_type = element_type;
1248 if (nitems == 0)
1250 /* Return empty array ... but not till we've validated element_type */
1251 PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
1254 typlen = my_extra->typlen;
1255 typbyval = my_extra->typbyval;
1256 typalign = my_extra->typalign;
1257 typioparam = my_extra->typioparam;
1259 dataPtr = (Datum *) palloc(nitems * sizeof(Datum));
1260 nullsPtr = (bool *) palloc(nitems * sizeof(bool));
1261 ReadArrayBinary(buf, nitems,
1262 &my_extra->proc, typioparam, typmod,
1263 typlen, typbyval, typalign,
1264 dataPtr, nullsPtr,
1265 &hasnulls, &nbytes);
1266 if (hasnulls)
1268 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
1269 nbytes += dataoffset;
1271 else
1273 dataoffset = 0; /* marker for no null bitmap */
1274 nbytes += ARR_OVERHEAD_NONULLS(ndim);
1276 retval = (ArrayType *) palloc(nbytes);
1277 SET_VARSIZE(retval, nbytes);
1278 retval->ndim = ndim;
1279 retval->dataoffset = dataoffset;
1280 retval->elemtype = element_type;
1281 memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
1282 memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
1284 CopyArrayEls(retval,
1285 dataPtr, nullsPtr, nitems,
1286 typlen, typbyval, typalign,
1287 true);
1289 pfree(dataPtr);
1290 pfree(nullsPtr);
1292 PG_RETURN_ARRAYTYPE_P(retval);
1296 * ReadArrayBinary:
1297 * collect the data elements of an array being read in binary style.
1299 * Inputs:
1300 * buf: the data buffer to read from.
1301 * nitems: total number of array elements (already read).
1302 * receiveproc: type-specific receive procedure for element datatype.
1303 * typioparam, typmod: auxiliary values to pass to receiveproc.
1304 * typlen, typbyval, typalign: storage parameters of element datatype.
1306 * Outputs:
1307 * values[]: filled with converted data values.
1308 * nulls[]: filled with is-null markers.
1309 * *hasnulls: set TRUE iff there are any null elements.
1310 * *nbytes: set to total size of data area needed (including alignment
1311 * padding but not including array header overhead).
1313 * Note that values[] and nulls[] are allocated by the caller, and must have
1314 * nitems elements.
1316 static void
1317 ReadArrayBinary(StringInfo buf,
1318 int nitems,
1319 FmgrInfo *receiveproc,
1320 Oid typioparam,
1321 int32 typmod,
1322 int typlen,
1323 bool typbyval,
1324 char typalign,
1325 Datum *values,
1326 bool *nulls,
1327 bool *hasnulls,
1328 int32 *nbytes)
1330 int i;
1331 bool hasnull;
1332 int32 totbytes;
1334 for (i = 0; i < nitems; i++)
1336 int itemlen;
1337 StringInfoData elem_buf;
1338 char csave;
1340 /* Get and check the item length */
1341 itemlen = pq_getmsgint(buf, 4);
1342 if (itemlen < -1 || itemlen > (buf->len - buf->cursor))
1343 ereport(ERROR,
1344 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1345 errmsg("insufficient data left in message")));
1347 if (itemlen == -1)
1349 /* -1 length means NULL */
1350 values[i] = ReceiveFunctionCall(receiveproc, NULL,
1351 typioparam, typmod);
1352 nulls[i] = true;
1353 continue;
1357 * Rather than copying data around, we just set up a phony StringInfo
1358 * pointing to the correct portion of the input buffer. We assume we
1359 * can scribble on the input buffer so as to maintain the convention
1360 * that StringInfos have a trailing null.
1362 elem_buf.data = &buf->data[buf->cursor];
1363 elem_buf.maxlen = itemlen + 1;
1364 elem_buf.len = itemlen;
1365 elem_buf.cursor = 0;
1367 buf->cursor += itemlen;
1369 csave = buf->data[buf->cursor];
1370 buf->data[buf->cursor] = '\0';
1372 /* Now call the element's receiveproc */
1373 values[i] = ReceiveFunctionCall(receiveproc, &elem_buf,
1374 typioparam, typmod);
1375 nulls[i] = false;
1377 /* Trouble if it didn't eat the whole buffer */
1378 if (elem_buf.cursor != itemlen)
1379 ereport(ERROR,
1380 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1381 errmsg("improper binary format in array element %d",
1382 i + 1)));
1384 buf->data[buf->cursor] = csave;
1388 * Check for nulls, compute total data space needed
1390 hasnull = false;
1391 totbytes = 0;
1392 for (i = 0; i < nitems; i++)
1394 if (nulls[i])
1395 hasnull = true;
1396 else
1398 /* let's just make sure data is not toasted */
1399 if (typlen == -1)
1400 values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
1401 totbytes = att_addlength_datum(totbytes, typlen, values[i]);
1402 totbytes = att_align_nominal(totbytes, typalign);
1403 /* check for overflow of total request */
1404 if (!AllocSizeIsValid(totbytes))
1405 ereport(ERROR,
1406 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1407 errmsg("array size exceeds the maximum allowed (%d)",
1408 (int) MaxAllocSize)));
1411 *hasnulls = hasnull;
1412 *nbytes = totbytes;
1417 * array_send :
1418 * takes the internal representation of an array and returns a bytea
1419 * containing the array in its external binary format.
1421 Datum
1422 array_send(PG_FUNCTION_ARGS)
1424 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1425 Oid element_type = ARR_ELEMTYPE(v);
1426 int typlen;
1427 bool typbyval;
1428 char typalign;
1429 char *p;
1430 bits8 *bitmap;
1431 int bitmask;
1432 int nitems,
1434 int ndim,
1435 *dim;
1436 StringInfoData buf;
1437 ArrayMetaState *my_extra;
1440 * We arrange to look up info about element type, including its send
1441 * conversion proc, only once per series of calls, assuming the element
1442 * type doesn't change underneath us.
1444 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1445 if (my_extra == NULL)
1447 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1448 sizeof(ArrayMetaState));
1449 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1450 my_extra->element_type = ~element_type;
1453 if (my_extra->element_type != element_type)
1455 /* Get info about element type, including its send proc */
1456 get_type_io_data(element_type, IOFunc_send,
1457 &my_extra->typlen, &my_extra->typbyval,
1458 &my_extra->typalign, &my_extra->typdelim,
1459 &my_extra->typioparam, &my_extra->typiofunc);
1460 if (!OidIsValid(my_extra->typiofunc))
1461 ereport(ERROR,
1462 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1463 errmsg("no binary output function available for type %s",
1464 format_type_be(element_type))));
1465 fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1466 fcinfo->flinfo->fn_mcxt);
1467 my_extra->element_type = element_type;
1469 typlen = my_extra->typlen;
1470 typbyval = my_extra->typbyval;
1471 typalign = my_extra->typalign;
1473 ndim = ARR_NDIM(v);
1474 dim = ARR_DIMS(v);
1475 nitems = ArrayGetNItems(ndim, dim);
1477 pq_begintypsend(&buf);
1479 /* Send the array header information */
1480 pq_sendint(&buf, ndim, 4);
1481 pq_sendint(&buf, ARR_HASNULL(v) ? 1 : 0, 4);
1482 pq_sendint(&buf, element_type, sizeof(Oid));
1483 for (i = 0; i < ndim; i++)
1485 pq_sendint(&buf, ARR_DIMS(v)[i], 4);
1486 pq_sendint(&buf, ARR_LBOUND(v)[i], 4);
1489 /* Send the array elements using the element's own sendproc */
1490 p = ARR_DATA_PTR(v);
1491 bitmap = ARR_NULLBITMAP(v);
1492 bitmask = 1;
1494 for (i = 0; i < nitems; i++)
1496 /* Get source element, checking for NULL */
1497 if (bitmap && (*bitmap & bitmask) == 0)
1499 /* -1 length means a NULL */
1500 pq_sendint(&buf, -1, 4);
1502 else
1504 Datum itemvalue;
1505 bytea *outputbytes;
1507 itemvalue = fetch_att(p, typbyval, typlen);
1508 outputbytes = SendFunctionCall(&my_extra->proc, itemvalue);
1509 pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
1510 pq_sendbytes(&buf, VARDATA(outputbytes),
1511 VARSIZE(outputbytes) - VARHDRSZ);
1512 pfree(outputbytes);
1514 p = att_addlength_pointer(p, typlen, p);
1515 p = (char *) att_align_nominal(p, typalign);
1518 /* advance bitmap pointer if any */
1519 if (bitmap)
1521 bitmask <<= 1;
1522 if (bitmask == 0x100)
1524 bitmap++;
1525 bitmask = 1;
1530 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
1534 * array_ndims :
1535 * returns the number of dimensions of the array pointed to by "v"
1537 Datum
1538 array_ndims(PG_FUNCTION_ARGS)
1540 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1542 /* Sanity check: does it look like an array at all? */
1543 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1544 PG_RETURN_NULL();
1546 PG_RETURN_INT32(ARR_NDIM(v));
1550 * array_dims :
1551 * returns the dimensions of the array pointed to by "v", as a "text"
1553 Datum
1554 array_dims(PG_FUNCTION_ARGS)
1556 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1557 char *p;
1558 int i;
1559 int *dimv,
1560 *lb;
1563 * 33 since we assume 15 digits per number + ':' +'[]'
1565 * +1 for trailing null
1567 char buf[MAXDIM * 33 + 1];
1569 /* Sanity check: does it look like an array at all? */
1570 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1571 PG_RETURN_NULL();
1573 dimv = ARR_DIMS(v);
1574 lb = ARR_LBOUND(v);
1576 p = buf;
1577 for (i = 0; i < ARR_NDIM(v); i++)
1579 sprintf(p, "[%d:%d]", lb[i], dimv[i] + lb[i] - 1);
1580 p += strlen(p);
1583 PG_RETURN_TEXT_P(cstring_to_text(buf));
1587 * array_lower :
1588 * returns the lower dimension, of the DIM requested, for
1589 * the array pointed to by "v", as an int4
1591 Datum
1592 array_lower(PG_FUNCTION_ARGS)
1594 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1595 int reqdim = PG_GETARG_INT32(1);
1596 int *lb;
1597 int result;
1599 /* Sanity check: does it look like an array at all? */
1600 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1601 PG_RETURN_NULL();
1603 /* Sanity check: was the requested dim valid */
1604 if (reqdim <= 0 || reqdim > ARR_NDIM(v))
1605 PG_RETURN_NULL();
1607 lb = ARR_LBOUND(v);
1608 result = lb[reqdim - 1];
1610 PG_RETURN_INT32(result);
1614 * array_upper :
1615 * returns the upper dimension, of the DIM requested, for
1616 * the array pointed to by "v", as an int4
1618 Datum
1619 array_upper(PG_FUNCTION_ARGS)
1621 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1622 int reqdim = PG_GETARG_INT32(1);
1623 int *dimv,
1624 *lb;
1625 int result;
1627 /* Sanity check: does it look like an array at all? */
1628 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1629 PG_RETURN_NULL();
1631 /* Sanity check: was the requested dim valid */
1632 if (reqdim <= 0 || reqdim > ARR_NDIM(v))
1633 PG_RETURN_NULL();
1635 lb = ARR_LBOUND(v);
1636 dimv = ARR_DIMS(v);
1638 result = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
1640 PG_RETURN_INT32(result);
1644 * array_length :
1645 * returns the length, of the dimension requested, for
1646 * the array pointed to by "v", as an int4
1648 Datum
1649 array_length(PG_FUNCTION_ARGS)
1651 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1652 int reqdim = PG_GETARG_INT32(1);
1653 int *dimv;
1654 int result;
1656 /* Sanity check: does it look like an array at all? */
1657 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1658 PG_RETURN_NULL();
1660 /* Sanity check: was the requested dim valid */
1661 if (reqdim <= 0 || reqdim > ARR_NDIM(v))
1662 PG_RETURN_NULL();
1664 dimv = ARR_DIMS(v);
1666 result = dimv[reqdim - 1];
1668 PG_RETURN_INT32(result);
1672 * array_ref :
1673 * This routine takes an array pointer and a subscript array and returns
1674 * the referenced item as a Datum. Note that for a pass-by-reference
1675 * datatype, the returned Datum is a pointer into the array object.
1677 * This handles both ordinary varlena arrays and fixed-length arrays.
1679 * Inputs:
1680 * array: the array object (mustn't be NULL)
1681 * nSubscripts: number of subscripts supplied
1682 * indx[]: the subscript values
1683 * arraytyplen: pg_type.typlen for the array type
1684 * elmlen: pg_type.typlen for the array's element type
1685 * elmbyval: pg_type.typbyval for the array's element type
1686 * elmalign: pg_type.typalign for the array's element type
1688 * Outputs:
1689 * The return value is the element Datum.
1690 * *isNull is set to indicate whether the element is NULL.
1692 Datum
1693 array_ref(ArrayType *array,
1694 int nSubscripts,
1695 int *indx,
1696 int arraytyplen,
1697 int elmlen,
1698 bool elmbyval,
1699 char elmalign,
1700 bool *isNull)
1702 int i,
1703 ndim,
1704 *dim,
1705 *lb,
1706 offset,
1707 fixedDim[1],
1708 fixedLb[1];
1709 char *arraydataptr,
1710 *retptr;
1711 bits8 *arraynullsptr;
1713 if (arraytyplen > 0)
1716 * fixed-length arrays -- these are assumed to be 1-d, 0-based
1718 ndim = 1;
1719 fixedDim[0] = arraytyplen / elmlen;
1720 fixedLb[0] = 0;
1721 dim = fixedDim;
1722 lb = fixedLb;
1723 arraydataptr = (char *) array;
1724 arraynullsptr = NULL;
1726 else
1728 /* detoast input array if necessary */
1729 array = DatumGetArrayTypeP(PointerGetDatum(array));
1731 ndim = ARR_NDIM(array);
1732 dim = ARR_DIMS(array);
1733 lb = ARR_LBOUND(array);
1734 arraydataptr = ARR_DATA_PTR(array);
1735 arraynullsptr = ARR_NULLBITMAP(array);
1739 * Return NULL for invalid subscript
1741 if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
1743 *isNull = true;
1744 return (Datum) 0;
1746 for (i = 0; i < ndim; i++)
1748 if (indx[i] < lb[i] || indx[i] >= (dim[i] + lb[i]))
1750 *isNull = true;
1751 return (Datum) 0;
1756 * Calculate the element number
1758 offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
1761 * Check for NULL array element
1763 if (array_get_isnull(arraynullsptr, offset))
1765 *isNull = true;
1766 return (Datum) 0;
1770 * OK, get the element
1772 *isNull = false;
1773 retptr = array_seek(arraydataptr, 0, arraynullsptr, offset,
1774 elmlen, elmbyval, elmalign);
1775 return ArrayCast(retptr, elmbyval, elmlen);
1779 * array_get_slice :
1780 * This routine takes an array and a range of indices (upperIndex and
1781 * lowerIndx), creates a new array structure for the referred elements
1782 * and returns a pointer to it.
1784 * This handles both ordinary varlena arrays and fixed-length arrays.
1786 * Inputs:
1787 * array: the array object (mustn't be NULL)
1788 * nSubscripts: number of subscripts supplied (must be same for upper/lower)
1789 * upperIndx[]: the upper subscript values
1790 * lowerIndx[]: the lower subscript values
1791 * arraytyplen: pg_type.typlen for the array type
1792 * elmlen: pg_type.typlen for the array's element type
1793 * elmbyval: pg_type.typbyval for the array's element type
1794 * elmalign: pg_type.typalign for the array's element type
1796 * Outputs:
1797 * The return value is the new array Datum (it's never NULL)
1799 * NOTE: we assume it is OK to scribble on the provided subscript arrays
1800 * lowerIndx[] and upperIndx[]. These are generally just temporaries.
1802 ArrayType *
1803 array_get_slice(ArrayType *array,
1804 int nSubscripts,
1805 int *upperIndx,
1806 int *lowerIndx,
1807 int arraytyplen,
1808 int elmlen,
1809 bool elmbyval,
1810 char elmalign)
1812 ArrayType *newarray;
1813 int i,
1814 ndim,
1815 *dim,
1816 *lb,
1817 *newlb;
1818 int fixedDim[1],
1819 fixedLb[1];
1820 Oid elemtype;
1821 char *arraydataptr;
1822 bits8 *arraynullsptr;
1823 int32 dataoffset;
1824 int bytes,
1825 span[MAXDIM];
1827 if (arraytyplen > 0)
1830 * fixed-length arrays -- currently, cannot slice these because parser
1831 * labels output as being of the fixed-length array type! Code below
1832 * shows how we could support it if the parser were changed to label
1833 * output as a suitable varlena array type.
1835 ereport(ERROR,
1836 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1837 errmsg("slices of fixed-length arrays not implemented")));
1840 * fixed-length arrays -- these are assumed to be 1-d, 0-based
1842 * XXX where would we get the correct ELEMTYPE from?
1844 ndim = 1;
1845 fixedDim[0] = arraytyplen / elmlen;
1846 fixedLb[0] = 0;
1847 dim = fixedDim;
1848 lb = fixedLb;
1849 elemtype = InvalidOid; /* XXX */
1850 arraydataptr = (char *) array;
1851 arraynullsptr = NULL;
1853 else
1855 /* detoast input array if necessary */
1856 array = DatumGetArrayTypeP(PointerGetDatum(array));
1858 ndim = ARR_NDIM(array);
1859 dim = ARR_DIMS(array);
1860 lb = ARR_LBOUND(array);
1861 elemtype = ARR_ELEMTYPE(array);
1862 arraydataptr = ARR_DATA_PTR(array);
1863 arraynullsptr = ARR_NULLBITMAP(array);
1867 * Check provided subscripts. A slice exceeding the current array limits
1868 * is silently truncated to the array limits. If we end up with an empty
1869 * slice, return an empty array.
1871 if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
1872 return construct_empty_array(elemtype);
1874 for (i = 0; i < nSubscripts; i++)
1876 if (lowerIndx[i] < lb[i])
1877 lowerIndx[i] = lb[i];
1878 if (upperIndx[i] >= (dim[i] + lb[i]))
1879 upperIndx[i] = dim[i] + lb[i] - 1;
1880 if (lowerIndx[i] > upperIndx[i])
1881 return construct_empty_array(elemtype);
1883 /* fill any missing subscript positions with full array range */
1884 for (; i < ndim; i++)
1886 lowerIndx[i] = lb[i];
1887 upperIndx[i] = dim[i] + lb[i] - 1;
1888 if (lowerIndx[i] > upperIndx[i])
1889 return construct_empty_array(elemtype);
1892 mda_get_range(ndim, span, lowerIndx, upperIndx);
1894 bytes = array_slice_size(arraydataptr, arraynullsptr,
1895 ndim, dim, lb,
1896 lowerIndx, upperIndx,
1897 elmlen, elmbyval, elmalign);
1900 * Currently, we put a null bitmap in the result if the source has one;
1901 * could be smarter ...
1903 if (arraynullsptr)
1905 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, ArrayGetNItems(ndim, span));
1906 bytes += dataoffset;
1908 else
1910 dataoffset = 0; /* marker for no null bitmap */
1911 bytes += ARR_OVERHEAD_NONULLS(ndim);
1914 newarray = (ArrayType *) palloc(bytes);
1915 SET_VARSIZE(newarray, bytes);
1916 newarray->ndim = ndim;
1917 newarray->dataoffset = dataoffset;
1918 newarray->elemtype = elemtype;
1919 memcpy(ARR_DIMS(newarray), span, ndim * sizeof(int));
1922 * Lower bounds of the new array are set to 1. Formerly (before 7.3) we
1923 * copied the given lowerIndx values ... but that seems confusing.
1925 newlb = ARR_LBOUND(newarray);
1926 for (i = 0; i < ndim; i++)
1927 newlb[i] = 1;
1929 array_extract_slice(newarray,
1930 ndim, dim, lb,
1931 arraydataptr, arraynullsptr,
1932 lowerIndx, upperIndx,
1933 elmlen, elmbyval, elmalign);
1935 return newarray;
1939 * array_set :
1940 * This routine sets the value of an array element (specified by
1941 * a subscript array) to a new value specified by "dataValue".
1943 * This handles both ordinary varlena arrays and fixed-length arrays.
1945 * Inputs:
1946 * array: the initial array object (mustn't be NULL)
1947 * nSubscripts: number of subscripts supplied
1948 * indx[]: the subscript values
1949 * dataValue: the datum to be inserted at the given position
1950 * isNull: whether dataValue is NULL
1951 * arraytyplen: pg_type.typlen for the array type
1952 * elmlen: pg_type.typlen for the array's element type
1953 * elmbyval: pg_type.typbyval for the array's element type
1954 * elmalign: pg_type.typalign for the array's element type
1956 * Result:
1957 * A new array is returned, just like the old except for the one
1958 * modified entry. The original array object is not changed.
1960 * For one-dimensional arrays only, we allow the array to be extended
1961 * by assigning to a position outside the existing subscript range; any
1962 * positions between the existing elements and the new one are set to NULLs.
1963 * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
1965 * NOTE: For assignments, we throw an error for invalid subscripts etc,
1966 * rather than returning a NULL as the fetch operations do.
1968 ArrayType *
1969 array_set(ArrayType *array,
1970 int nSubscripts,
1971 int *indx,
1972 Datum dataValue,
1973 bool isNull,
1974 int arraytyplen,
1975 int elmlen,
1976 bool elmbyval,
1977 char elmalign)
1979 ArrayType *newarray;
1980 int i,
1981 ndim,
1982 dim[MAXDIM],
1983 lb[MAXDIM],
1984 offset;
1985 char *elt_ptr;
1986 bool newhasnulls;
1987 bits8 *oldnullbitmap;
1988 int oldnitems,
1989 newnitems,
1990 olddatasize,
1991 newsize,
1992 olditemlen,
1993 newitemlen,
1994 overheadlen,
1995 oldoverheadlen,
1996 addedbefore,
1997 addedafter,
1998 lenbefore,
1999 lenafter;
2001 if (arraytyplen > 0)
2004 * fixed-length arrays -- these are assumed to be 1-d, 0-based. We
2005 * cannot extend them, either.
2007 if (nSubscripts != 1)
2008 ereport(ERROR,
2009 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2010 errmsg("wrong number of array subscripts")));
2012 if (indx[0] < 0 || indx[0] * elmlen >= arraytyplen)
2013 ereport(ERROR,
2014 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2015 errmsg("array subscript out of range")));
2017 if (isNull)
2018 ereport(ERROR,
2019 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
2020 errmsg("cannot assign null value to an element of a fixed-length array")));
2022 newarray = (ArrayType *) palloc(arraytyplen);
2023 memcpy(newarray, array, arraytyplen);
2024 elt_ptr = (char *) newarray + indx[0] * elmlen;
2025 ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign, elt_ptr);
2026 return newarray;
2029 if (nSubscripts <= 0 || nSubscripts > MAXDIM)
2030 ereport(ERROR,
2031 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2032 errmsg("wrong number of array subscripts")));
2034 /* make sure item to be inserted is not toasted */
2035 if (elmlen == -1 && !isNull)
2036 dataValue = PointerGetDatum(PG_DETOAST_DATUM(dataValue));
2038 /* detoast input array if necessary */
2039 array = DatumGetArrayTypeP(PointerGetDatum(array));
2041 ndim = ARR_NDIM(array);
2044 * if number of dims is zero, i.e. an empty array, create an array with
2045 * nSubscripts dimensions, and set the lower bounds to the supplied
2046 * subscripts
2048 if (ndim == 0)
2050 Oid elmtype = ARR_ELEMTYPE(array);
2052 for (i = 0; i < nSubscripts; i++)
2054 dim[i] = 1;
2055 lb[i] = indx[i];
2058 return construct_md_array(&dataValue, &isNull, nSubscripts,
2059 dim, lb, elmtype,
2060 elmlen, elmbyval, elmalign);
2063 if (ndim != nSubscripts)
2064 ereport(ERROR,
2065 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2066 errmsg("wrong number of array subscripts")));
2068 /* copy dim/lb since we may modify them */
2069 memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
2070 memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
2072 newhasnulls = (ARR_HASNULL(array) || isNull);
2073 addedbefore = addedafter = 0;
2076 * Check subscripts
2078 if (ndim == 1)
2080 if (indx[0] < lb[0])
2082 addedbefore = lb[0] - indx[0];
2083 dim[0] += addedbefore;
2084 lb[0] = indx[0];
2085 if (addedbefore > 1)
2086 newhasnulls = true; /* will insert nulls */
2088 if (indx[0] >= (dim[0] + lb[0]))
2090 addedafter = indx[0] - (dim[0] + lb[0]) + 1;
2091 dim[0] += addedafter;
2092 if (addedafter > 1)
2093 newhasnulls = true; /* will insert nulls */
2096 else
2099 * XXX currently we do not support extending multi-dimensional arrays
2100 * during assignment
2102 for (i = 0; i < ndim; i++)
2104 if (indx[i] < lb[i] ||
2105 indx[i] >= (dim[i] + lb[i]))
2106 ereport(ERROR,
2107 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2108 errmsg("array subscript out of range")));
2113 * Compute sizes of items and areas to copy
2115 newnitems = ArrayGetNItems(ndim, dim);
2116 if (newhasnulls)
2117 overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, newnitems);
2118 else
2119 overheadlen = ARR_OVERHEAD_NONULLS(ndim);
2120 oldnitems = ArrayGetNItems(ndim, ARR_DIMS(array));
2121 oldnullbitmap = ARR_NULLBITMAP(array);
2122 oldoverheadlen = ARR_DATA_OFFSET(array);
2123 olddatasize = ARR_SIZE(array) - oldoverheadlen;
2124 if (addedbefore)
2126 offset = 0;
2127 lenbefore = 0;
2128 olditemlen = 0;
2129 lenafter = olddatasize;
2131 else if (addedafter)
2133 offset = oldnitems;
2134 lenbefore = olddatasize;
2135 olditemlen = 0;
2136 lenafter = 0;
2138 else
2140 offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
2141 elt_ptr = array_seek(ARR_DATA_PTR(array), 0, oldnullbitmap, offset,
2142 elmlen, elmbyval, elmalign);
2143 lenbefore = (int) (elt_ptr - ARR_DATA_PTR(array));
2144 if (array_get_isnull(oldnullbitmap, offset))
2145 olditemlen = 0;
2146 else
2148 olditemlen = att_addlength_pointer(0, elmlen, elt_ptr);
2149 olditemlen = att_align_nominal(olditemlen, elmalign);
2151 lenafter = (int) (olddatasize - lenbefore - olditemlen);
2154 if (isNull)
2155 newitemlen = 0;
2156 else
2158 newitemlen = att_addlength_datum(0, elmlen, dataValue);
2159 newitemlen = att_align_nominal(newitemlen, elmalign);
2162 newsize = overheadlen + lenbefore + newitemlen + lenafter;
2165 * OK, create the new array and fill in header/dimensions
2167 newarray = (ArrayType *) palloc(newsize);
2168 SET_VARSIZE(newarray, newsize);
2169 newarray->ndim = ndim;
2170 newarray->dataoffset = newhasnulls ? overheadlen : 0;
2171 newarray->elemtype = ARR_ELEMTYPE(array);
2172 memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
2173 memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
2176 * Fill in data
2178 memcpy((char *) newarray + overheadlen,
2179 (char *) array + oldoverheadlen,
2180 lenbefore);
2181 if (!isNull)
2182 ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign,
2183 (char *) newarray + overheadlen + lenbefore);
2184 memcpy((char *) newarray + overheadlen + lenbefore + newitemlen,
2185 (char *) array + oldoverheadlen + lenbefore + olditemlen,
2186 lenafter);
2189 * Fill in nulls bitmap if needed
2191 * Note: it's possible we just replaced the last NULL with a non-NULL, and
2192 * could get rid of the bitmap. Seems not worth testing for though.
2194 if (newhasnulls)
2196 bits8 *newnullbitmap = ARR_NULLBITMAP(newarray);
2198 /* Zero the bitmap to take care of marking inserted positions null */
2199 MemSet(newnullbitmap, 0, (newnitems + 7) / 8);
2200 /* Fix the inserted value */
2201 if (addedafter)
2202 array_set_isnull(newnullbitmap, newnitems - 1, isNull);
2203 else
2204 array_set_isnull(newnullbitmap, offset, isNull);
2205 /* Fix the copied range(s) */
2206 if (addedbefore)
2207 array_bitmap_copy(newnullbitmap, addedbefore,
2208 oldnullbitmap, 0,
2209 oldnitems);
2210 else
2212 array_bitmap_copy(newnullbitmap, 0,
2213 oldnullbitmap, 0,
2214 offset);
2215 if (addedafter == 0)
2216 array_bitmap_copy(newnullbitmap, offset + 1,
2217 oldnullbitmap, offset + 1,
2218 oldnitems - offset - 1);
2222 return newarray;
2226 * array_set_slice :
2227 * This routine sets the value of a range of array locations (specified
2228 * by upper and lower subscript values) to new values passed as
2229 * another array.
2231 * This handles both ordinary varlena arrays and fixed-length arrays.
2233 * Inputs:
2234 * array: the initial array object (mustn't be NULL)
2235 * nSubscripts: number of subscripts supplied (must be same for upper/lower)
2236 * upperIndx[]: the upper subscript values
2237 * lowerIndx[]: the lower subscript values
2238 * srcArray: the source for the inserted values
2239 * isNull: indicates whether srcArray is NULL
2240 * arraytyplen: pg_type.typlen for the array type
2241 * elmlen: pg_type.typlen for the array's element type
2242 * elmbyval: pg_type.typbyval for the array's element type
2243 * elmalign: pg_type.typalign for the array's element type
2245 * Result:
2246 * A new array is returned, just like the old except for the
2247 * modified range. The original array object is not changed.
2249 * For one-dimensional arrays only, we allow the array to be extended
2250 * by assigning to positions outside the existing subscript range; any
2251 * positions between the existing elements and the new ones are set to NULLs.
2252 * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
2254 * NOTE: we assume it is OK to scribble on the provided index arrays
2255 * lowerIndx[] and upperIndx[]. These are generally just temporaries.
2257 * NOTE: For assignments, we throw an error for silly subscripts etc,
2258 * rather than returning a NULL or empty array as the fetch operations do.
2260 ArrayType *
2261 array_set_slice(ArrayType *array,
2262 int nSubscripts,
2263 int *upperIndx,
2264 int *lowerIndx,
2265 ArrayType *srcArray,
2266 bool isNull,
2267 int arraytyplen,
2268 int elmlen,
2269 bool elmbyval,
2270 char elmalign)
2272 ArrayType *newarray;
2273 int i,
2274 ndim,
2275 dim[MAXDIM],
2276 lb[MAXDIM],
2277 span[MAXDIM];
2278 bool newhasnulls;
2279 int nitems,
2280 nsrcitems,
2281 olddatasize,
2282 newsize,
2283 olditemsize,
2284 newitemsize,
2285 overheadlen,
2286 oldoverheadlen,
2287 addedbefore,
2288 addedafter,
2289 lenbefore,
2290 lenafter,
2291 itemsbefore,
2292 itemsafter,
2293 nolditems;
2295 /* Currently, assignment from a NULL source array is a no-op */
2296 if (isNull)
2297 return array;
2299 if (arraytyplen > 0)
2302 * fixed-length arrays -- not got round to doing this...
2304 ereport(ERROR,
2305 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2306 errmsg("updates on slices of fixed-length arrays not implemented")));
2309 /* detoast arrays if necessary */
2310 array = DatumGetArrayTypeP(PointerGetDatum(array));
2311 srcArray = DatumGetArrayTypeP(PointerGetDatum(srcArray));
2313 /* note: we assume srcArray contains no toasted elements */
2315 ndim = ARR_NDIM(array);
2318 * if number of dims is zero, i.e. an empty array, create an array with
2319 * nSubscripts dimensions, and set the upper and lower bounds to the
2320 * supplied subscripts
2322 if (ndim == 0)
2324 Datum *dvalues;
2325 bool *dnulls;
2326 int nelems;
2327 Oid elmtype = ARR_ELEMTYPE(array);
2329 deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
2330 &dvalues, &dnulls, &nelems);
2332 for (i = 0; i < nSubscripts; i++)
2334 dim[i] = 1 + upperIndx[i] - lowerIndx[i];
2335 lb[i] = lowerIndx[i];
2338 /* complain if too few source items; we ignore extras, however */
2339 if (nelems < ArrayGetNItems(nSubscripts, dim))
2340 ereport(ERROR,
2341 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2342 errmsg("source array too small")));
2344 return construct_md_array(dvalues, dnulls, nSubscripts,
2345 dim, lb, elmtype,
2346 elmlen, elmbyval, elmalign);
2349 if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
2350 ereport(ERROR,
2351 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2352 errmsg("wrong number of array subscripts")));
2354 /* copy dim/lb since we may modify them */
2355 memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
2356 memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
2358 newhasnulls = (ARR_HASNULL(array) || ARR_HASNULL(srcArray));
2359 addedbefore = addedafter = 0;
2362 * Check subscripts
2364 if (ndim == 1)
2366 Assert(nSubscripts == 1);
2367 if (lowerIndx[0] > upperIndx[0])
2368 ereport(ERROR,
2369 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2370 errmsg("upper bound cannot be less than lower bound")));
2371 if (lowerIndx[0] < lb[0])
2373 if (upperIndx[0] < lb[0] - 1)
2374 newhasnulls = true; /* will insert nulls */
2375 addedbefore = lb[0] - lowerIndx[0];
2376 dim[0] += addedbefore;
2377 lb[0] = lowerIndx[0];
2379 if (upperIndx[0] >= (dim[0] + lb[0]))
2381 if (lowerIndx[0] > (dim[0] + lb[0]))
2382 newhasnulls = true; /* will insert nulls */
2383 addedafter = upperIndx[0] - (dim[0] + lb[0]) + 1;
2384 dim[0] += addedafter;
2387 else
2390 * XXX currently we do not support extending multi-dimensional arrays
2391 * during assignment
2393 for (i = 0; i < nSubscripts; i++)
2395 if (lowerIndx[i] > upperIndx[i])
2396 ereport(ERROR,
2397 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2398 errmsg("upper bound cannot be less than lower bound")));
2399 if (lowerIndx[i] < lb[i] ||
2400 upperIndx[i] >= (dim[i] + lb[i]))
2401 ereport(ERROR,
2402 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2403 errmsg("array subscript out of range")));
2405 /* fill any missing subscript positions with full array range */
2406 for (; i < ndim; i++)
2408 lowerIndx[i] = lb[i];
2409 upperIndx[i] = dim[i] + lb[i] - 1;
2410 if (lowerIndx[i] > upperIndx[i])
2411 ereport(ERROR,
2412 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2413 errmsg("upper bound cannot be less than lower bound")));
2417 /* Do this mainly to check for overflow */
2418 nitems = ArrayGetNItems(ndim, dim);
2421 * Make sure source array has enough entries. Note we ignore the shape of
2422 * the source array and just read entries serially.
2424 mda_get_range(ndim, span, lowerIndx, upperIndx);
2425 nsrcitems = ArrayGetNItems(ndim, span);
2426 if (nsrcitems > ArrayGetNItems(ARR_NDIM(srcArray), ARR_DIMS(srcArray)))
2427 ereport(ERROR,
2428 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2429 errmsg("source array too small")));
2432 * Compute space occupied by new entries, space occupied by replaced
2433 * entries, and required space for new array.
2435 if (newhasnulls)
2436 overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
2437 else
2438 overheadlen = ARR_OVERHEAD_NONULLS(ndim);
2439 newitemsize = array_nelems_size(ARR_DATA_PTR(srcArray), 0,
2440 ARR_NULLBITMAP(srcArray), nsrcitems,
2441 elmlen, elmbyval, elmalign);
2442 oldoverheadlen = ARR_DATA_OFFSET(array);
2443 olddatasize = ARR_SIZE(array) - oldoverheadlen;
2444 if (ndim > 1)
2447 * here we do not need to cope with extension of the array; it would
2448 * be a lot more complicated if we had to do so...
2450 olditemsize = array_slice_size(ARR_DATA_PTR(array),
2451 ARR_NULLBITMAP(array),
2452 ndim, dim, lb,
2453 lowerIndx, upperIndx,
2454 elmlen, elmbyval, elmalign);
2455 lenbefore = lenafter = 0; /* keep compiler quiet */
2456 itemsbefore = itemsafter = nolditems = 0;
2458 else
2461 * here we must allow for possibility of slice larger than orig array
2463 int oldlb = ARR_LBOUND(array)[0];
2464 int oldub = oldlb + ARR_DIMS(array)[0] - 1;
2465 int slicelb = Max(oldlb, lowerIndx[0]);
2466 int sliceub = Min(oldub, upperIndx[0]);
2467 char *oldarraydata = ARR_DATA_PTR(array);
2468 bits8 *oldarraybitmap = ARR_NULLBITMAP(array);
2470 itemsbefore = Min(slicelb, oldub + 1) - oldlb;
2471 lenbefore = array_nelems_size(oldarraydata, 0, oldarraybitmap,
2472 itemsbefore,
2473 elmlen, elmbyval, elmalign);
2474 if (slicelb > sliceub)
2476 nolditems = 0;
2477 olditemsize = 0;
2479 else
2481 nolditems = sliceub - slicelb + 1;
2482 olditemsize = array_nelems_size(oldarraydata + lenbefore,
2483 itemsbefore, oldarraybitmap,
2484 nolditems,
2485 elmlen, elmbyval, elmalign);
2487 itemsafter = oldub - sliceub;
2488 lenafter = olddatasize - lenbefore - olditemsize;
2491 newsize = overheadlen + olddatasize - olditemsize + newitemsize;
2493 newarray = (ArrayType *) palloc(newsize);
2494 SET_VARSIZE(newarray, newsize);
2495 newarray->ndim = ndim;
2496 newarray->dataoffset = newhasnulls ? overheadlen : 0;
2497 newarray->elemtype = ARR_ELEMTYPE(array);
2498 memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
2499 memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
2501 if (ndim > 1)
2504 * here we do not need to cope with extension of the array; it would
2505 * be a lot more complicated if we had to do so...
2507 array_insert_slice(newarray, array, srcArray,
2508 ndim, dim, lb,
2509 lowerIndx, upperIndx,
2510 elmlen, elmbyval, elmalign);
2512 else
2514 /* fill in data */
2515 memcpy((char *) newarray + overheadlen,
2516 (char *) array + oldoverheadlen,
2517 lenbefore);
2518 memcpy((char *) newarray + overheadlen + lenbefore,
2519 ARR_DATA_PTR(srcArray),
2520 newitemsize);
2521 memcpy((char *) newarray + overheadlen + lenbefore + newitemsize,
2522 (char *) array + oldoverheadlen + lenbefore + olditemsize,
2523 lenafter);
2524 /* fill in nulls bitmap if needed */
2525 if (newhasnulls)
2527 bits8 *newnullbitmap = ARR_NULLBITMAP(newarray);
2528 bits8 *oldnullbitmap = ARR_NULLBITMAP(array);
2530 /* Zero the bitmap to handle marking inserted positions null */
2531 MemSet(newnullbitmap, 0, (nitems + 7) / 8);
2532 array_bitmap_copy(newnullbitmap, addedbefore,
2533 oldnullbitmap, 0,
2534 itemsbefore);
2535 array_bitmap_copy(newnullbitmap, lowerIndx[0] - lb[0],
2536 ARR_NULLBITMAP(srcArray), 0,
2537 nsrcitems);
2538 array_bitmap_copy(newnullbitmap, addedbefore + itemsbefore + nolditems,
2539 oldnullbitmap, itemsbefore + nolditems,
2540 itemsafter);
2544 return newarray;
2548 * array_map()
2550 * Map an array through an arbitrary function. Return a new array with
2551 * same dimensions and each source element transformed by fn(). Each
2552 * source element is passed as the first argument to fn(); additional
2553 * arguments to be passed to fn() can be specified by the caller.
2554 * The output array can have a different element type than the input.
2556 * Parameters are:
2557 * * fcinfo: a function-call data structure pre-constructed by the caller
2558 * to be ready to call the desired function, with everything except the
2559 * first argument position filled in. In particular, flinfo identifies
2560 * the function fn(), and if nargs > 1 then argument positions after the
2561 * first must be preset to the additional values to be passed. The
2562 * first argument position initially holds the input array value.
2563 * * inpType: OID of element type of input array. This must be the same as,
2564 * or binary-compatible with, the first argument type of fn().
2565 * * retType: OID of element type of output array. This must be the same as,
2566 * or binary-compatible with, the result type of fn().
2567 * * amstate: workspace for array_map. Must be zeroed by caller before
2568 * first call, and not touched after that.
2570 * It is legitimate to pass a freshly-zeroed ArrayMapState on each call,
2571 * but better performance can be had if the state can be preserved across
2572 * a series of calls.
2574 * NB: caller must assure that input array is not NULL. NULL elements in
2575 * the array are OK however.
2577 Datum
2578 array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType,
2579 ArrayMapState *amstate)
2581 ArrayType *v;
2582 ArrayType *result;
2583 Datum *values;
2584 bool *nulls;
2585 Datum elt;
2586 int *dim;
2587 int ndim;
2588 int nitems;
2589 int i;
2590 int32 nbytes = 0;
2591 int32 dataoffset;
2592 bool hasnulls;
2593 int inp_typlen;
2594 bool inp_typbyval;
2595 char inp_typalign;
2596 int typlen;
2597 bool typbyval;
2598 char typalign;
2599 char *s;
2600 bits8 *bitmap;
2601 int bitmask;
2602 ArrayMetaState *inp_extra;
2603 ArrayMetaState *ret_extra;
2605 /* Get input array */
2606 if (fcinfo->nargs < 1)
2607 elog(ERROR, "invalid nargs: %d", fcinfo->nargs);
2608 if (PG_ARGISNULL(0))
2609 elog(ERROR, "null input array");
2610 v = PG_GETARG_ARRAYTYPE_P(0);
2612 Assert(ARR_ELEMTYPE(v) == inpType);
2614 ndim = ARR_NDIM(v);
2615 dim = ARR_DIMS(v);
2616 nitems = ArrayGetNItems(ndim, dim);
2618 /* Check for empty array */
2619 if (nitems <= 0)
2621 /* Return empty array */
2622 PG_RETURN_ARRAYTYPE_P(construct_empty_array(retType));
2626 * We arrange to look up info about input and return element types only
2627 * once per series of calls, assuming the element type doesn't change
2628 * underneath us.
2630 inp_extra = &amstate->inp_extra;
2631 ret_extra = &amstate->ret_extra;
2633 if (inp_extra->element_type != inpType)
2635 get_typlenbyvalalign(inpType,
2636 &inp_extra->typlen,
2637 &inp_extra->typbyval,
2638 &inp_extra->typalign);
2639 inp_extra->element_type = inpType;
2641 inp_typlen = inp_extra->typlen;
2642 inp_typbyval = inp_extra->typbyval;
2643 inp_typalign = inp_extra->typalign;
2645 if (ret_extra->element_type != retType)
2647 get_typlenbyvalalign(retType,
2648 &ret_extra->typlen,
2649 &ret_extra->typbyval,
2650 &ret_extra->typalign);
2651 ret_extra->element_type = retType;
2653 typlen = ret_extra->typlen;
2654 typbyval = ret_extra->typbyval;
2655 typalign = ret_extra->typalign;
2657 /* Allocate temporary arrays for new values */
2658 values = (Datum *) palloc(nitems * sizeof(Datum));
2659 nulls = (bool *) palloc(nitems * sizeof(bool));
2661 /* Loop over source data */
2662 s = ARR_DATA_PTR(v);
2663 bitmap = ARR_NULLBITMAP(v);
2664 bitmask = 1;
2665 hasnulls = false;
2667 for (i = 0; i < nitems; i++)
2669 bool callit = true;
2671 /* Get source element, checking for NULL */
2672 if (bitmap && (*bitmap & bitmask) == 0)
2674 fcinfo->argnull[0] = true;
2676 else
2678 elt = fetch_att(s, inp_typbyval, inp_typlen);
2679 s = att_addlength_datum(s, inp_typlen, elt);
2680 s = (char *) att_align_nominal(s, inp_typalign);
2681 fcinfo->arg[0] = elt;
2682 fcinfo->argnull[0] = false;
2686 * Apply the given function to source elt and extra args.
2688 if (fcinfo->flinfo->fn_strict)
2690 int j;
2692 for (j = 0; j < fcinfo->nargs; j++)
2694 if (fcinfo->argnull[j])
2696 callit = false;
2697 break;
2702 if (callit)
2704 fcinfo->isnull = false;
2705 values[i] = FunctionCallInvoke(fcinfo);
2707 else
2708 fcinfo->isnull = true;
2710 nulls[i] = fcinfo->isnull;
2711 if (fcinfo->isnull)
2712 hasnulls = true;
2713 else
2715 /* Ensure data is not toasted */
2716 if (typlen == -1)
2717 values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
2718 /* Update total result size */
2719 nbytes = att_addlength_datum(nbytes, typlen, values[i]);
2720 nbytes = att_align_nominal(nbytes, typalign);
2721 /* check for overflow of total request */
2722 if (!AllocSizeIsValid(nbytes))
2723 ereport(ERROR,
2724 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2725 errmsg("array size exceeds the maximum allowed (%d)",
2726 (int) MaxAllocSize)));
2729 /* advance bitmap pointer if any */
2730 if (bitmap)
2732 bitmask <<= 1;
2733 if (bitmask == 0x100)
2735 bitmap++;
2736 bitmask = 1;
2741 /* Allocate and initialize the result array */
2742 if (hasnulls)
2744 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
2745 nbytes += dataoffset;
2747 else
2749 dataoffset = 0; /* marker for no null bitmap */
2750 nbytes += ARR_OVERHEAD_NONULLS(ndim);
2752 result = (ArrayType *) palloc(nbytes);
2753 SET_VARSIZE(result, nbytes);
2754 result->ndim = ndim;
2755 result->dataoffset = dataoffset;
2756 result->elemtype = retType;
2757 memcpy(ARR_DIMS(result), ARR_DIMS(v), 2 * ndim * sizeof(int));
2760 * Note: do not risk trying to pfree the results of the called function
2762 CopyArrayEls(result,
2763 values, nulls, nitems,
2764 typlen, typbyval, typalign,
2765 false);
2767 pfree(values);
2768 pfree(nulls);
2770 PG_RETURN_ARRAYTYPE_P(result);
2774 * construct_array --- simple method for constructing an array object
2776 * elems: array of Datum items to become the array contents
2777 * (NULL element values are not supported).
2778 * nelems: number of items
2779 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
2781 * A palloc'd 1-D array object is constructed and returned. Note that
2782 * elem values will be copied into the object even if pass-by-ref type.
2784 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
2785 * from the system catalogs, given the elmtype. However, the caller is
2786 * in a better position to cache this info across multiple uses, or even
2787 * to hard-wire values if the element type is hard-wired.
2789 ArrayType *
2790 construct_array(Datum *elems, int nelems,
2791 Oid elmtype,
2792 int elmlen, bool elmbyval, char elmalign)
2794 int dims[1];
2795 int lbs[1];
2797 dims[0] = nelems;
2798 lbs[0] = 1;
2800 return construct_md_array(elems, NULL, 1, dims, lbs,
2801 elmtype, elmlen, elmbyval, elmalign);
2805 * construct_md_array --- simple method for constructing an array object
2806 * with arbitrary dimensions and possible NULLs
2808 * elems: array of Datum items to become the array contents
2809 * nulls: array of is-null flags (can be NULL if no nulls)
2810 * ndims: number of dimensions
2811 * dims: integer array with size of each dimension
2812 * lbs: integer array with lower bound of each dimension
2813 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
2815 * A palloc'd ndims-D array object is constructed and returned. Note that
2816 * elem values will be copied into the object even if pass-by-ref type.
2818 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
2819 * from the system catalogs, given the elmtype. However, the caller is
2820 * in a better position to cache this info across multiple uses, or even
2821 * to hard-wire values if the element type is hard-wired.
2823 ArrayType *
2824 construct_md_array(Datum *elems,
2825 bool *nulls,
2826 int ndims,
2827 int *dims,
2828 int *lbs,
2829 Oid elmtype, int elmlen, bool elmbyval, char elmalign)
2831 ArrayType *result;
2832 bool hasnulls;
2833 int32 nbytes;
2834 int32 dataoffset;
2835 int i;
2836 int nelems;
2838 if (ndims < 0) /* we do allow zero-dimension arrays */
2839 ereport(ERROR,
2840 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2841 errmsg("invalid number of dimensions: %d", ndims)));
2842 if (ndims > MAXDIM)
2843 ereport(ERROR,
2844 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2845 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
2846 ndims, MAXDIM)));
2848 /* fast track for empty array */
2849 if (ndims == 0)
2850 return construct_empty_array(elmtype);
2852 nelems = ArrayGetNItems(ndims, dims);
2854 /* compute required space */
2855 nbytes = 0;
2856 hasnulls = false;
2857 for (i = 0; i < nelems; i++)
2859 if (nulls && nulls[i])
2861 hasnulls = true;
2862 continue;
2864 /* make sure data is not toasted */
2865 if (elmlen == -1)
2866 elems[i] = PointerGetDatum(PG_DETOAST_DATUM(elems[i]));
2867 nbytes = att_addlength_datum(nbytes, elmlen, elems[i]);
2868 nbytes = att_align_nominal(nbytes, elmalign);
2869 /* check for overflow of total request */
2870 if (!AllocSizeIsValid(nbytes))
2871 ereport(ERROR,
2872 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2873 errmsg("array size exceeds the maximum allowed (%d)",
2874 (int) MaxAllocSize)));
2877 /* Allocate and initialize result array */
2878 if (hasnulls)
2880 dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nelems);
2881 nbytes += dataoffset;
2883 else
2885 dataoffset = 0; /* marker for no null bitmap */
2886 nbytes += ARR_OVERHEAD_NONULLS(ndims);
2888 result = (ArrayType *) palloc(nbytes);
2889 SET_VARSIZE(result, nbytes);
2890 result->ndim = ndims;
2891 result->dataoffset = dataoffset;
2892 result->elemtype = elmtype;
2893 memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
2894 memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
2896 CopyArrayEls(result,
2897 elems, nulls, nelems,
2898 elmlen, elmbyval, elmalign,
2899 false);
2901 return result;
2905 * construct_empty_array --- make a zero-dimensional array of given type
2907 ArrayType *
2908 construct_empty_array(Oid elmtype)
2910 ArrayType *result;
2912 result = (ArrayType *) palloc(sizeof(ArrayType));
2913 SET_VARSIZE(result, sizeof(ArrayType));
2914 result->ndim = 0;
2915 result->dataoffset = 0;
2916 result->elemtype = elmtype;
2917 return result;
2921 * deconstruct_array --- simple method for extracting data from an array
2923 * array: array object to examine (must not be NULL)
2924 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
2925 * elemsp: return value, set to point to palloc'd array of Datum values
2926 * nullsp: return value, set to point to palloc'd array of isnull markers
2927 * nelemsp: return value, set to number of extracted values
2929 * The caller may pass nullsp == NULL if it does not support NULLs in the
2930 * array. Note that this produces a very uninformative error message,
2931 * so do it only in cases where a NULL is really not expected.
2933 * If array elements are pass-by-ref data type, the returned Datums will
2934 * be pointers into the array object.
2936 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
2937 * from the system catalogs, given the elmtype. However, in most current
2938 * uses the type is hard-wired into the caller and so we can save a lookup
2939 * cycle by hard-wiring the type info as well.
2941 void
2942 deconstruct_array(ArrayType *array,
2943 Oid elmtype,
2944 int elmlen, bool elmbyval, char elmalign,
2945 Datum **elemsp, bool **nullsp, int *nelemsp)
2947 Datum *elems;
2948 bool *nulls;
2949 int nelems;
2950 char *p;
2951 bits8 *bitmap;
2952 int bitmask;
2953 int i;
2955 Assert(ARR_ELEMTYPE(array) == elmtype);
2957 nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
2958 *elemsp = elems = (Datum *) palloc(nelems * sizeof(Datum));
2959 if (nullsp)
2960 *nullsp = nulls = (bool *) palloc(nelems * sizeof(bool));
2961 else
2962 nulls = NULL;
2963 *nelemsp = nelems;
2965 p = ARR_DATA_PTR(array);
2966 bitmap = ARR_NULLBITMAP(array);
2967 bitmask = 1;
2969 for (i = 0; i < nelems; i++)
2971 /* Get source element, checking for NULL */
2972 if (bitmap && (*bitmap & bitmask) == 0)
2974 elems[i] = (Datum) 0;
2975 if (nulls)
2976 nulls[i] = true;
2977 else
2978 ereport(ERROR,
2979 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
2980 errmsg("null array element not allowed in this context")));
2982 else
2984 elems[i] = fetch_att(p, elmbyval, elmlen);
2985 if (nulls)
2986 nulls[i] = false;
2987 p = att_addlength_pointer(p, elmlen, p);
2988 p = (char *) att_align_nominal(p, elmalign);
2991 /* advance bitmap pointer if any */
2992 if (bitmap)
2994 bitmask <<= 1;
2995 if (bitmask == 0x100)
2997 bitmap++;
2998 bitmask = 1;
3006 * array_eq :
3007 * compares two arrays for equality
3008 * result :
3009 * returns true if the arrays are equal, false otherwise.
3011 * Note: we do not use array_cmp here, since equality may be meaningful in
3012 * datatypes that don't have a total ordering (and hence no btree support).
3014 Datum
3015 array_eq(PG_FUNCTION_ARGS)
3017 ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
3018 ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
3019 int ndims1 = ARR_NDIM(array1);
3020 int ndims2 = ARR_NDIM(array2);
3021 int *dims1 = ARR_DIMS(array1);
3022 int *dims2 = ARR_DIMS(array2);
3023 Oid element_type = ARR_ELEMTYPE(array1);
3024 bool result = true;
3025 int nitems;
3026 TypeCacheEntry *typentry;
3027 int typlen;
3028 bool typbyval;
3029 char typalign;
3030 char *ptr1;
3031 char *ptr2;
3032 bits8 *bitmap1;
3033 bits8 *bitmap2;
3034 int bitmask;
3035 int i;
3036 FunctionCallInfoData locfcinfo;
3038 if (element_type != ARR_ELEMTYPE(array2))
3039 ereport(ERROR,
3040 (errcode(ERRCODE_DATATYPE_MISMATCH),
3041 errmsg("cannot compare arrays of different element types")));
3043 /* fast path if the arrays do not have the same dimensionality */
3044 if (ndims1 != ndims2 ||
3045 memcmp(dims1, dims2, 2 * ndims1 * sizeof(int)) != 0)
3046 result = false;
3047 else
3050 * We arrange to look up the equality function only once per series of
3051 * calls, assuming the element type doesn't change underneath us. The
3052 * typcache is used so that we have no memory leakage when being used
3053 * as an index support function.
3055 typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3056 if (typentry == NULL ||
3057 typentry->type_id != element_type)
3059 typentry = lookup_type_cache(element_type,
3060 TYPECACHE_EQ_OPR_FINFO);
3061 if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
3062 ereport(ERROR,
3063 (errcode(ERRCODE_UNDEFINED_FUNCTION),
3064 errmsg("could not identify an equality operator for type %s",
3065 format_type_be(element_type))));
3066 fcinfo->flinfo->fn_extra = (void *) typentry;
3068 typlen = typentry->typlen;
3069 typbyval = typentry->typbyval;
3070 typalign = typentry->typalign;
3073 * apply the operator to each pair of array elements.
3075 InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
3076 NULL, NULL);
3078 /* Loop over source data */
3079 nitems = ArrayGetNItems(ndims1, dims1);
3080 ptr1 = ARR_DATA_PTR(array1);
3081 ptr2 = ARR_DATA_PTR(array2);
3082 bitmap1 = ARR_NULLBITMAP(array1);
3083 bitmap2 = ARR_NULLBITMAP(array2);
3084 bitmask = 1; /* use same bitmask for both arrays */
3086 for (i = 0; i < nitems; i++)
3088 Datum elt1;
3089 Datum elt2;
3090 bool isnull1;
3091 bool isnull2;
3092 bool oprresult;
3094 /* Get elements, checking for NULL */
3095 if (bitmap1 && (*bitmap1 & bitmask) == 0)
3097 isnull1 = true;
3098 elt1 = (Datum) 0;
3100 else
3102 isnull1 = false;
3103 elt1 = fetch_att(ptr1, typbyval, typlen);
3104 ptr1 = att_addlength_pointer(ptr1, typlen, ptr1);
3105 ptr1 = (char *) att_align_nominal(ptr1, typalign);
3108 if (bitmap2 && (*bitmap2 & bitmask) == 0)
3110 isnull2 = true;
3111 elt2 = (Datum) 0;
3113 else
3115 isnull2 = false;
3116 elt2 = fetch_att(ptr2, typbyval, typlen);
3117 ptr2 = att_addlength_pointer(ptr2, typlen, ptr2);
3118 ptr2 = (char *) att_align_nominal(ptr2, typalign);
3121 /* advance bitmap pointers if any */
3122 bitmask <<= 1;
3123 if (bitmask == 0x100)
3125 if (bitmap1)
3126 bitmap1++;
3127 if (bitmap2)
3128 bitmap2++;
3129 bitmask = 1;
3133 * We consider two NULLs equal; NULL and not-NULL are unequal.
3135 if (isnull1 && isnull2)
3136 continue;
3137 if (isnull1 || isnull2)
3139 result = false;
3140 break;
3144 * Apply the operator to the element pair
3146 locfcinfo.arg[0] = elt1;
3147 locfcinfo.arg[1] = elt2;
3148 locfcinfo.argnull[0] = false;
3149 locfcinfo.argnull[1] = false;
3150 locfcinfo.isnull = false;
3151 oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
3152 if (!oprresult)
3154 result = false;
3155 break;
3160 /* Avoid leaking memory when handed toasted input. */
3161 PG_FREE_IF_COPY(array1, 0);
3162 PG_FREE_IF_COPY(array2, 1);
3164 PG_RETURN_BOOL(result);
3168 /*-----------------------------------------------------------------------------
3169 * array-array bool operators:
3170 * Given two arrays, iterate comparison operators
3171 * over the array. Uses logic similar to text comparison
3172 * functions, except element-by-element instead of
3173 * character-by-character.
3174 *----------------------------------------------------------------------------
3177 Datum
3178 array_ne(PG_FUNCTION_ARGS)
3180 PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo)));
3183 Datum
3184 array_lt(PG_FUNCTION_ARGS)
3186 PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
3189 Datum
3190 array_gt(PG_FUNCTION_ARGS)
3192 PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
3195 Datum
3196 array_le(PG_FUNCTION_ARGS)
3198 PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
3201 Datum
3202 array_ge(PG_FUNCTION_ARGS)
3204 PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
3207 Datum
3208 btarraycmp(PG_FUNCTION_ARGS)
3210 PG_RETURN_INT32(array_cmp(fcinfo));
3214 * array_cmp()
3215 * Internal comparison function for arrays.
3217 * Returns -1, 0 or 1
3219 static int
3220 array_cmp(FunctionCallInfo fcinfo)
3222 ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
3223 ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
3224 int ndims1 = ARR_NDIM(array1);
3225 int ndims2 = ARR_NDIM(array2);
3226 int *dims1 = ARR_DIMS(array1);
3227 int *dims2 = ARR_DIMS(array2);
3228 int nitems1 = ArrayGetNItems(ndims1, dims1);
3229 int nitems2 = ArrayGetNItems(ndims2, dims2);
3230 Oid element_type = ARR_ELEMTYPE(array1);
3231 int result = 0;
3232 TypeCacheEntry *typentry;
3233 int typlen;
3234 bool typbyval;
3235 char typalign;
3236 int min_nitems;
3237 char *ptr1;
3238 char *ptr2;
3239 bits8 *bitmap1;
3240 bits8 *bitmap2;
3241 int bitmask;
3242 int i;
3243 FunctionCallInfoData locfcinfo;
3245 if (element_type != ARR_ELEMTYPE(array2))
3246 ereport(ERROR,
3247 (errcode(ERRCODE_DATATYPE_MISMATCH),
3248 errmsg("cannot compare arrays of different element types")));
3251 * We arrange to look up the comparison function only once per series of
3252 * calls, assuming the element type doesn't change underneath us. The
3253 * typcache is used so that we have no memory leakage when being used as
3254 * an index support function.
3256 typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3257 if (typentry == NULL ||
3258 typentry->type_id != element_type)
3260 typentry = lookup_type_cache(element_type,
3261 TYPECACHE_CMP_PROC_FINFO);
3262 if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
3263 ereport(ERROR,
3264 (errcode(ERRCODE_UNDEFINED_FUNCTION),
3265 errmsg("could not identify a comparison function for type %s",
3266 format_type_be(element_type))));
3267 fcinfo->flinfo->fn_extra = (void *) typentry;
3269 typlen = typentry->typlen;
3270 typbyval = typentry->typbyval;
3271 typalign = typentry->typalign;
3274 * apply the operator to each pair of array elements.
3276 InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2,
3277 NULL, NULL);
3279 /* Loop over source data */
3280 min_nitems = Min(nitems1, nitems2);
3281 ptr1 = ARR_DATA_PTR(array1);
3282 ptr2 = ARR_DATA_PTR(array2);
3283 bitmap1 = ARR_NULLBITMAP(array1);
3284 bitmap2 = ARR_NULLBITMAP(array2);
3285 bitmask = 1; /* use same bitmask for both arrays */
3287 for (i = 0; i < min_nitems; i++)
3289 Datum elt1;
3290 Datum elt2;
3291 bool isnull1;
3292 bool isnull2;
3293 int32 cmpresult;
3295 /* Get elements, checking for NULL */
3296 if (bitmap1 && (*bitmap1 & bitmask) == 0)
3298 isnull1 = true;
3299 elt1 = (Datum) 0;
3301 else
3303 isnull1 = false;
3304 elt1 = fetch_att(ptr1, typbyval, typlen);
3305 ptr1 = att_addlength_pointer(ptr1, typlen, ptr1);
3306 ptr1 = (char *) att_align_nominal(ptr1, typalign);
3309 if (bitmap2 && (*bitmap2 & bitmask) == 0)
3311 isnull2 = true;
3312 elt2 = (Datum) 0;
3314 else
3316 isnull2 = false;
3317 elt2 = fetch_att(ptr2, typbyval, typlen);
3318 ptr2 = att_addlength_pointer(ptr2, typlen, ptr2);
3319 ptr2 = (char *) att_align_nominal(ptr2, typalign);
3322 /* advance bitmap pointers if any */
3323 bitmask <<= 1;
3324 if (bitmask == 0x100)
3326 if (bitmap1)
3327 bitmap1++;
3328 if (bitmap2)
3329 bitmap2++;
3330 bitmask = 1;
3334 * We consider two NULLs equal; NULL > not-NULL.
3336 if (isnull1 && isnull2)
3337 continue;
3338 if (isnull1)
3340 /* arg1 is greater than arg2 */
3341 result = 1;
3342 break;
3344 if (isnull2)
3346 /* arg1 is less than arg2 */
3347 result = -1;
3348 break;
3351 /* Compare the pair of elements */
3352 locfcinfo.arg[0] = elt1;
3353 locfcinfo.arg[1] = elt2;
3354 locfcinfo.argnull[0] = false;
3355 locfcinfo.argnull[1] = false;
3356 locfcinfo.isnull = false;
3357 cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
3359 if (cmpresult == 0)
3360 continue; /* equal */
3362 if (cmpresult < 0)
3364 /* arg1 is less than arg2 */
3365 result = -1;
3366 break;
3368 else
3370 /* arg1 is greater than arg2 */
3371 result = 1;
3372 break;
3377 * If arrays contain same data (up to end of shorter one), apply
3378 * additional rules to sort by dimensionality. The relative significance
3379 * of the different bits of information is historical; mainly we just care
3380 * that we don't say "equal" for arrays of different dimensionality.
3382 if (result == 0)
3384 if (nitems1 != nitems2)
3385 result = (nitems1 < nitems2) ? -1 : 1;
3386 else if (ndims1 != ndims2)
3387 result = (ndims1 < ndims2) ? -1 : 1;
3388 else
3390 /* this relies on LB array immediately following DIMS array */
3391 for (i = 0; i < ndims1 * 2; i++)
3393 if (dims1[i] != dims2[i])
3395 result = (dims1[i] < dims2[i]) ? -1 : 1;
3396 break;
3402 /* Avoid leaking memory when handed toasted input. */
3403 PG_FREE_IF_COPY(array1, 0);
3404 PG_FREE_IF_COPY(array2, 1);
3406 return result;
3410 /*-----------------------------------------------------------------------------
3411 * array overlap/containment comparisons
3412 * These use the same methods of comparing array elements as array_eq.
3413 * We consider only the elements of the arrays, ignoring dimensionality.
3414 *----------------------------------------------------------------------------
3418 * array_contain_compare :
3419 * compares two arrays for overlap/containment
3421 * When matchall is true, return true if all members of array1 are in array2.
3422 * When matchall is false, return true if any members of array1 are in array2.
3424 static bool
3425 array_contain_compare(ArrayType *array1, ArrayType *array2, bool matchall,
3426 void **fn_extra)
3428 bool result = matchall;
3429 Oid element_type = ARR_ELEMTYPE(array1);
3430 TypeCacheEntry *typentry;
3431 int nelems1;
3432 Datum *values2;
3433 bool *nulls2;
3434 int nelems2;
3435 int typlen;
3436 bool typbyval;
3437 char typalign;
3438 char *ptr1;
3439 bits8 *bitmap1;
3440 int bitmask;
3441 int i;
3442 int j;
3443 FunctionCallInfoData locfcinfo;
3445 if (element_type != ARR_ELEMTYPE(array2))
3446 ereport(ERROR,
3447 (errcode(ERRCODE_DATATYPE_MISMATCH),
3448 errmsg("cannot compare arrays of different element types")));
3451 * We arrange to look up the equality function only once per series of
3452 * calls, assuming the element type doesn't change underneath us. The
3453 * typcache is used so that we have no memory leakage when being used as
3454 * an index support function.
3456 typentry = (TypeCacheEntry *) *fn_extra;
3457 if (typentry == NULL ||
3458 typentry->type_id != element_type)
3460 typentry = lookup_type_cache(element_type,
3461 TYPECACHE_EQ_OPR_FINFO);
3462 if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
3463 ereport(ERROR,
3464 (errcode(ERRCODE_UNDEFINED_FUNCTION),
3465 errmsg("could not identify an equality operator for type %s",
3466 format_type_be(element_type))));
3467 *fn_extra = (void *) typentry;
3469 typlen = typentry->typlen;
3470 typbyval = typentry->typbyval;
3471 typalign = typentry->typalign;
3474 * Since we probably will need to scan array2 multiple times, it's
3475 * worthwhile to use deconstruct_array on it. We scan array1 the hard way
3476 * however, since we very likely won't need to look at all of it.
3478 deconstruct_array(array2, element_type, typlen, typbyval, typalign,
3479 &values2, &nulls2, &nelems2);
3482 * Apply the comparison operator to each pair of array elements.
3484 InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
3485 NULL, NULL);
3487 /* Loop over source data */
3488 nelems1 = ArrayGetNItems(ARR_NDIM(array1), ARR_DIMS(array1));
3489 ptr1 = ARR_DATA_PTR(array1);
3490 bitmap1 = ARR_NULLBITMAP(array1);
3491 bitmask = 1;
3493 for (i = 0; i < nelems1; i++)
3495 Datum elt1;
3496 bool isnull1;
3498 /* Get element, checking for NULL */
3499 if (bitmap1 && (*bitmap1 & bitmask) == 0)
3501 isnull1 = true;
3502 elt1 = (Datum) 0;
3504 else
3506 isnull1 = false;
3507 elt1 = fetch_att(ptr1, typbyval, typlen);
3508 ptr1 = att_addlength_pointer(ptr1, typlen, ptr1);
3509 ptr1 = (char *) att_align_nominal(ptr1, typalign);
3512 /* advance bitmap pointer if any */
3513 bitmask <<= 1;
3514 if (bitmask == 0x100)
3516 if (bitmap1)
3517 bitmap1++;
3518 bitmask = 1;
3522 * We assume that the comparison operator is strict, so a NULL can't
3523 * match anything. XXX this diverges from the "NULL=NULL" behavior of
3524 * array_eq, should we act like that?
3526 if (isnull1)
3528 if (matchall)
3530 result = false;
3531 break;
3533 continue;
3536 for (j = 0; j < nelems2; j++)
3538 Datum elt2 = values2[j];
3539 bool isnull2 = nulls2[j];
3540 bool oprresult;
3542 if (isnull2)
3543 continue; /* can't match */
3546 * Apply the operator to the element pair
3548 locfcinfo.arg[0] = elt1;
3549 locfcinfo.arg[1] = elt2;
3550 locfcinfo.argnull[0] = false;
3551 locfcinfo.argnull[1] = false;
3552 locfcinfo.isnull = false;
3553 oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
3554 if (oprresult)
3555 break;
3558 if (j < nelems2)
3560 /* found a match for elt1 */
3561 if (!matchall)
3563 result = true;
3564 break;
3567 else
3569 /* no match for elt1 */
3570 if (matchall)
3572 result = false;
3573 break;
3578 pfree(values2);
3579 pfree(nulls2);
3581 return result;
3584 Datum
3585 arrayoverlap(PG_FUNCTION_ARGS)
3587 ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
3588 ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
3589 bool result;
3591 result = array_contain_compare(array1, array2, false,
3592 &fcinfo->flinfo->fn_extra);
3594 /* Avoid leaking memory when handed toasted input. */
3595 PG_FREE_IF_COPY(array1, 0);
3596 PG_FREE_IF_COPY(array2, 1);
3598 PG_RETURN_BOOL(result);
3601 Datum
3602 arraycontains(PG_FUNCTION_ARGS)
3604 ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
3605 ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
3606 bool result;
3608 result = array_contain_compare(array2, array1, true,
3609 &fcinfo->flinfo->fn_extra);
3611 /* Avoid leaking memory when handed toasted input. */
3612 PG_FREE_IF_COPY(array1, 0);
3613 PG_FREE_IF_COPY(array2, 1);
3615 PG_RETURN_BOOL(result);
3618 Datum
3619 arraycontained(PG_FUNCTION_ARGS)
3621 ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
3622 ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
3623 bool result;
3625 result = array_contain_compare(array1, array2, true,
3626 &fcinfo->flinfo->fn_extra);
3628 /* Avoid leaking memory when handed toasted input. */
3629 PG_FREE_IF_COPY(array1, 0);
3630 PG_FREE_IF_COPY(array2, 1);
3632 PG_RETURN_BOOL(result);
3636 /***************************************************************************/
3637 /******************| Support Routines |*****************/
3638 /***************************************************************************/
3641 * Check whether a specific array element is NULL
3643 * nullbitmap: pointer to array's null bitmap (NULL if none)
3644 * offset: 0-based linear element number of array element
3646 static bool
3647 array_get_isnull(const bits8 *nullbitmap, int offset)
3649 if (nullbitmap == NULL)
3650 return false; /* assume not null */
3651 if (nullbitmap[offset / 8] & (1 << (offset % 8)))
3652 return false; /* not null */
3653 return true;
3657 * Set a specific array element's null-bitmap entry
3659 * nullbitmap: pointer to array's null bitmap (mustn't be NULL)
3660 * offset: 0-based linear element number of array element
3661 * isNull: null status to set
3663 static void
3664 array_set_isnull(bits8 *nullbitmap, int offset, bool isNull)
3666 int bitmask;
3668 nullbitmap += offset / 8;
3669 bitmask = 1 << (offset % 8);
3670 if (isNull)
3671 *nullbitmap &= ~bitmask;
3672 else
3673 *nullbitmap |= bitmask;
3677 * Fetch array element at pointer, converted correctly to a Datum
3679 * Caller must have handled case of NULL element
3681 static Datum
3682 ArrayCast(char *value, bool byval, int len)
3684 return fetch_att(value, byval, len);
3688 * Copy datum to *dest and return total space used (including align padding)
3690 * Caller must have handled case of NULL element
3692 static int
3693 ArrayCastAndSet(Datum src,
3694 int typlen,
3695 bool typbyval,
3696 char typalign,
3697 char *dest)
3699 int inc;
3701 if (typlen > 0)
3703 if (typbyval)
3704 store_att_byval(dest, src, typlen);
3705 else
3706 memmove(dest, DatumGetPointer(src), typlen);
3707 inc = att_align_nominal(typlen, typalign);
3709 else
3711 Assert(!typbyval);
3712 inc = att_addlength_datum(0, typlen, src);
3713 memmove(dest, DatumGetPointer(src), inc);
3714 inc = att_align_nominal(inc, typalign);
3717 return inc;
3721 * Advance ptr over nitems array elements
3723 * ptr: starting location in array
3724 * offset: 0-based linear element number of first element (the one at *ptr)
3725 * nullbitmap: start of array's null bitmap, or NULL if none
3726 * nitems: number of array elements to advance over (>= 0)
3727 * typlen, typbyval, typalign: storage parameters of array element datatype
3729 * It is caller's responsibility to ensure that nitems is within range
3731 static char *
3732 array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
3733 int typlen, bool typbyval, char typalign)
3735 int bitmask;
3736 int i;
3738 /* easy if fixed-size elements and no NULLs */
3739 if (typlen > 0 && !nullbitmap)
3740 return ptr + nitems * ((Size) att_align_nominal(typlen, typalign));
3742 /* seems worth having separate loops for NULL and no-NULLs cases */
3743 if (nullbitmap)
3745 nullbitmap += offset / 8;
3746 bitmask = 1 << (offset % 8);
3748 for (i = 0; i < nitems; i++)
3750 if (*nullbitmap & bitmask)
3752 ptr = att_addlength_pointer(ptr, typlen, ptr);
3753 ptr = (char *) att_align_nominal(ptr, typalign);
3755 bitmask <<= 1;
3756 if (bitmask == 0x100)
3758 nullbitmap++;
3759 bitmask = 1;
3763 else
3765 for (i = 0; i < nitems; i++)
3767 ptr = att_addlength_pointer(ptr, typlen, ptr);
3768 ptr = (char *) att_align_nominal(ptr, typalign);
3771 return ptr;
3775 * Compute total size of the nitems array elements starting at *ptr
3777 * Parameters same as for array_seek
3779 static int
3780 array_nelems_size(char *ptr, int offset, bits8 *nullbitmap, int nitems,
3781 int typlen, bool typbyval, char typalign)
3783 return array_seek(ptr, offset, nullbitmap, nitems,
3784 typlen, typbyval, typalign) - ptr;
3788 * Copy nitems array elements from srcptr to destptr
3790 * destptr: starting destination location (must be enough room!)
3791 * nitems: number of array elements to copy (>= 0)
3792 * srcptr: starting location in source array
3793 * offset: 0-based linear element number of first element (the one at *srcptr)
3794 * nullbitmap: start of source array's null bitmap, or NULL if none
3795 * typlen, typbyval, typalign: storage parameters of array element datatype
3797 * Returns number of bytes copied
3799 * NB: this does not take care of setting up the destination's null bitmap!
3801 static int
3802 array_copy(char *destptr, int nitems,
3803 char *srcptr, int offset, bits8 *nullbitmap,
3804 int typlen, bool typbyval, char typalign)
3806 int numbytes;
3808 numbytes = array_nelems_size(srcptr, offset, nullbitmap, nitems,
3809 typlen, typbyval, typalign);
3810 memcpy(destptr, srcptr, numbytes);
3811 return numbytes;
3815 * Copy nitems null-bitmap bits from source to destination
3817 * destbitmap: start of destination array's null bitmap (mustn't be NULL)
3818 * destoffset: 0-based linear element number of first dest element
3819 * srcbitmap: start of source array's null bitmap, or NULL if none
3820 * srcoffset: 0-based linear element number of first source element
3821 * nitems: number of bits to copy (>= 0)
3823 * If srcbitmap is NULL then we assume the source is all-non-NULL and
3824 * fill 1's into the destination bitmap. Note that only the specified
3825 * bits in the destination map are changed, not any before or after.
3827 * Note: this could certainly be optimized using standard bitblt methods.
3828 * However, it's not clear that the typical Postgres array has enough elements
3829 * to make it worth worrying too much. For the moment, KISS.
3831 void
3832 array_bitmap_copy(bits8 *destbitmap, int destoffset,
3833 const bits8 *srcbitmap, int srcoffset,
3834 int nitems)
3836 int destbitmask,
3837 destbitval,
3838 srcbitmask,
3839 srcbitval;
3841 Assert(destbitmap);
3842 if (nitems <= 0)
3843 return; /* don't risk fetch off end of memory */
3844 destbitmap += destoffset / 8;
3845 destbitmask = 1 << (destoffset % 8);
3846 destbitval = *destbitmap;
3847 if (srcbitmap)
3849 srcbitmap += srcoffset / 8;
3850 srcbitmask = 1 << (srcoffset % 8);
3851 srcbitval = *srcbitmap;
3852 while (nitems-- > 0)
3854 if (srcbitval & srcbitmask)
3855 destbitval |= destbitmask;
3856 else
3857 destbitval &= ~destbitmask;
3858 destbitmask <<= 1;
3859 if (destbitmask == 0x100)
3861 *destbitmap++ = destbitval;
3862 destbitmask = 1;
3863 if (nitems > 0)
3864 destbitval = *destbitmap;
3866 srcbitmask <<= 1;
3867 if (srcbitmask == 0x100)
3869 srcbitmap++;
3870 srcbitmask = 1;
3871 if (nitems > 0)
3872 srcbitval = *srcbitmap;
3875 if (destbitmask != 1)
3876 *destbitmap = destbitval;
3878 else
3880 while (nitems-- > 0)
3882 destbitval |= destbitmask;
3883 destbitmask <<= 1;
3884 if (destbitmask == 0x100)
3886 *destbitmap++ = destbitval;
3887 destbitmask = 1;
3888 if (nitems > 0)
3889 destbitval = *destbitmap;
3892 if (destbitmask != 1)
3893 *destbitmap = destbitval;
3898 * Compute space needed for a slice of an array
3900 * We assume the caller has verified that the slice coordinates are valid.
3902 static int
3903 array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
3904 int ndim, int *dim, int *lb,
3905 int *st, int *endp,
3906 int typlen, bool typbyval, char typalign)
3908 int src_offset,
3909 span[MAXDIM],
3910 prod[MAXDIM],
3911 dist[MAXDIM],
3912 indx[MAXDIM];
3913 char *ptr;
3914 int i,
3916 inc;
3917 int count = 0;
3919 mda_get_range(ndim, span, st, endp);
3921 /* Pretty easy for fixed element length without nulls ... */
3922 if (typlen > 0 && !arraynullsptr)
3923 return ArrayGetNItems(ndim, span) * att_align_nominal(typlen, typalign);
3925 /* Else gotta do it the hard way */
3926 src_offset = ArrayGetOffset(ndim, dim, lb, st);
3927 ptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
3928 typlen, typbyval, typalign);
3929 mda_get_prod(ndim, dim, prod);
3930 mda_get_offset_values(ndim, dist, prod, span);
3931 for (i = 0; i < ndim; i++)
3932 indx[i] = 0;
3933 j = ndim - 1;
3936 if (dist[j])
3938 ptr = array_seek(ptr, src_offset, arraynullsptr, dist[j],
3939 typlen, typbyval, typalign);
3940 src_offset += dist[j];
3942 if (!array_get_isnull(arraynullsptr, src_offset))
3944 inc = att_addlength_pointer(0, typlen, ptr);
3945 inc = att_align_nominal(inc, typalign);
3946 ptr += inc;
3947 count += inc;
3949 src_offset++;
3950 } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
3951 return count;
3955 * Extract a slice of an array into consecutive elements in the destination
3956 * array.
3958 * We assume the caller has verified that the slice coordinates are valid,
3959 * allocated enough storage for the result, and initialized the header
3960 * of the new array.
3962 static void
3963 array_extract_slice(ArrayType *newarray,
3964 int ndim,
3965 int *dim,
3966 int *lb,
3967 char *arraydataptr,
3968 bits8 *arraynullsptr,
3969 int *st,
3970 int *endp,
3971 int typlen,
3972 bool typbyval,
3973 char typalign)
3975 char *destdataptr = ARR_DATA_PTR(newarray);
3976 bits8 *destnullsptr = ARR_NULLBITMAP(newarray);
3977 char *srcdataptr;
3978 int src_offset,
3979 dest_offset,
3980 prod[MAXDIM],
3981 span[MAXDIM],
3982 dist[MAXDIM],
3983 indx[MAXDIM];
3984 int i,
3986 inc;
3988 src_offset = ArrayGetOffset(ndim, dim, lb, st);
3989 srcdataptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
3990 typlen, typbyval, typalign);
3991 mda_get_prod(ndim, dim, prod);
3992 mda_get_range(ndim, span, st, endp);
3993 mda_get_offset_values(ndim, dist, prod, span);
3994 for (i = 0; i < ndim; i++)
3995 indx[i] = 0;
3996 dest_offset = 0;
3997 j = ndim - 1;
4000 if (dist[j])
4002 /* skip unwanted elements */
4003 srcdataptr = array_seek(srcdataptr, src_offset, arraynullsptr,
4004 dist[j],
4005 typlen, typbyval, typalign);
4006 src_offset += dist[j];
4008 inc = array_copy(destdataptr, 1,
4009 srcdataptr, src_offset, arraynullsptr,
4010 typlen, typbyval, typalign);
4011 if (destnullsptr)
4012 array_bitmap_copy(destnullsptr, dest_offset,
4013 arraynullsptr, src_offset,
4015 destdataptr += inc;
4016 srcdataptr += inc;
4017 src_offset++;
4018 dest_offset++;
4019 } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
4023 * Insert a slice into an array.
4025 * ndim/dim[]/lb[] are dimensions of the original array. A new array with
4026 * those same dimensions is to be constructed. destArray must already
4027 * have been allocated and its header initialized.
4029 * st[]/endp[] identify the slice to be replaced. Elements within the slice
4030 * volume are taken from consecutive elements of the srcArray; elements
4031 * outside it are copied from origArray.
4033 * We assume the caller has verified that the slice coordinates are valid.
4035 static void
4036 array_insert_slice(ArrayType *destArray,
4037 ArrayType *origArray,
4038 ArrayType *srcArray,
4039 int ndim,
4040 int *dim,
4041 int *lb,
4042 int *st,
4043 int *endp,
4044 int typlen,
4045 bool typbyval,
4046 char typalign)
4048 char *destPtr = ARR_DATA_PTR(destArray);
4049 char *origPtr = ARR_DATA_PTR(origArray);
4050 char *srcPtr = ARR_DATA_PTR(srcArray);
4051 bits8 *destBitmap = ARR_NULLBITMAP(destArray);
4052 bits8 *origBitmap = ARR_NULLBITMAP(origArray);
4053 bits8 *srcBitmap = ARR_NULLBITMAP(srcArray);
4054 int orignitems = ArrayGetNItems(ARR_NDIM(origArray),
4055 ARR_DIMS(origArray));
4056 int dest_offset,
4057 orig_offset,
4058 src_offset,
4059 prod[MAXDIM],
4060 span[MAXDIM],
4061 dist[MAXDIM],
4062 indx[MAXDIM];
4063 int i,
4065 inc;
4067 dest_offset = ArrayGetOffset(ndim, dim, lb, st);
4068 /* copy items before the slice start */
4069 inc = array_copy(destPtr, dest_offset,
4070 origPtr, 0, origBitmap,
4071 typlen, typbyval, typalign);
4072 destPtr += inc;
4073 origPtr += inc;
4074 if (destBitmap)
4075 array_bitmap_copy(destBitmap, 0, origBitmap, 0, dest_offset);
4076 orig_offset = dest_offset;
4077 mda_get_prod(ndim, dim, prod);
4078 mda_get_range(ndim, span, st, endp);
4079 mda_get_offset_values(ndim, dist, prod, span);
4080 for (i = 0; i < ndim; i++)
4081 indx[i] = 0;
4082 src_offset = 0;
4083 j = ndim - 1;
4086 /* Copy/advance over elements between here and next part of slice */
4087 if (dist[j])
4089 inc = array_copy(destPtr, dist[j],
4090 origPtr, orig_offset, origBitmap,
4091 typlen, typbyval, typalign);
4092 destPtr += inc;
4093 origPtr += inc;
4094 if (destBitmap)
4095 array_bitmap_copy(destBitmap, dest_offset,
4096 origBitmap, orig_offset,
4097 dist[j]);
4098 dest_offset += dist[j];
4099 orig_offset += dist[j];
4101 /* Copy new element at this slice position */
4102 inc = array_copy(destPtr, 1,
4103 srcPtr, src_offset, srcBitmap,
4104 typlen, typbyval, typalign);
4105 if (destBitmap)
4106 array_bitmap_copy(destBitmap, dest_offset,
4107 srcBitmap, src_offset,
4109 destPtr += inc;
4110 srcPtr += inc;
4111 dest_offset++;
4112 src_offset++;
4113 /* Advance over old element at this slice position */
4114 origPtr = array_seek(origPtr, orig_offset, origBitmap, 1,
4115 typlen, typbyval, typalign);
4116 orig_offset++;
4117 } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
4119 /* don't miss any data at the end */
4120 array_copy(destPtr, orignitems - orig_offset,
4121 origPtr, orig_offset, origBitmap,
4122 typlen, typbyval, typalign);
4123 if (destBitmap)
4124 array_bitmap_copy(destBitmap, dest_offset,
4125 origBitmap, orig_offset,
4126 orignitems - orig_offset);
4130 * accumArrayResult - accumulate one (more) Datum for an array result
4132 * astate is working state (NULL on first call)
4133 * rcontext is where to keep working state
4135 ArrayBuildState *
4136 accumArrayResult(ArrayBuildState *astate,
4137 Datum dvalue, bool disnull,
4138 Oid element_type,
4139 MemoryContext rcontext)
4141 MemoryContext arr_context,
4142 oldcontext;
4144 if (astate == NULL)
4146 /* First time through --- initialize */
4148 /* Make a temporary context to hold all the junk */
4149 arr_context = AllocSetContextCreate(rcontext,
4150 "accumArrayResult",
4151 ALLOCSET_DEFAULT_MINSIZE,
4152 ALLOCSET_DEFAULT_INITSIZE,
4153 ALLOCSET_DEFAULT_MAXSIZE);
4154 oldcontext = MemoryContextSwitchTo(arr_context);
4155 astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
4156 astate->mcontext = arr_context;
4157 astate->alen = 64; /* arbitrary starting array size */
4158 astate->dvalues = (Datum *) palloc(astate->alen * sizeof(Datum));
4159 astate->dnulls = (bool *) palloc(astate->alen * sizeof(bool));
4160 astate->nelems = 0;
4161 astate->element_type = element_type;
4162 get_typlenbyvalalign(element_type,
4163 &astate->typlen,
4164 &astate->typbyval,
4165 &astate->typalign);
4167 else
4169 oldcontext = MemoryContextSwitchTo(astate->mcontext);
4170 Assert(astate->element_type == element_type);
4171 /* enlarge dvalues[]/dnulls[] if needed */
4172 if (astate->nelems >= astate->alen)
4174 astate->alen *= 2;
4175 astate->dvalues = (Datum *)
4176 repalloc(astate->dvalues, astate->alen * sizeof(Datum));
4177 astate->dnulls = (bool *)
4178 repalloc(astate->dnulls, astate->alen * sizeof(bool));
4183 * Ensure pass-by-ref stuff is copied into mcontext; and detoast it too
4184 * if it's varlena. (You might think that detoasting is not needed here
4185 * because construct_md_array can detoast the array elements later.
4186 * However, we must not let construct_md_array modify the ArrayBuildState
4187 * because that would mean array_agg_finalfn damages its input, which
4188 * is verboten. Also, this way frequently saves one copying step.)
4190 if (!disnull && !astate->typbyval)
4192 if (astate->typlen == -1)
4193 dvalue = PointerGetDatum(PG_DETOAST_DATUM_COPY(dvalue));
4194 else
4195 dvalue = datumCopy(dvalue, astate->typbyval, astate->typlen);
4198 astate->dvalues[astate->nelems] = dvalue;
4199 astate->dnulls[astate->nelems] = disnull;
4200 astate->nelems++;
4202 MemoryContextSwitchTo(oldcontext);
4204 return astate;
4208 * makeArrayResult - produce 1-D final result of accumArrayResult
4210 * astate is working state (not NULL)
4211 * rcontext is where to construct result
4213 Datum
4214 makeArrayResult(ArrayBuildState *astate,
4215 MemoryContext rcontext)
4217 int dims[1];
4218 int lbs[1];
4220 dims[0] = astate->nelems;
4221 lbs[0] = 1;
4223 return makeMdArrayResult(astate, 1, dims, lbs, rcontext, true);
4227 * makeMdArrayResult - produce multi-D final result of accumArrayResult
4229 * beware: no check that specified dimensions match the number of values
4230 * accumulated.
4232 * astate is working state (not NULL)
4233 * rcontext is where to construct result
4234 * release is true if okay to release working state
4236 Datum
4237 makeMdArrayResult(ArrayBuildState *astate,
4238 int ndims,
4239 int *dims,
4240 int *lbs,
4241 MemoryContext rcontext,
4242 bool release)
4244 ArrayType *result;
4245 MemoryContext oldcontext;
4247 /* Build the final array result in rcontext */
4248 oldcontext = MemoryContextSwitchTo(rcontext);
4250 result = construct_md_array(astate->dvalues,
4251 astate->dnulls,
4252 ndims,
4253 dims,
4254 lbs,
4255 astate->element_type,
4256 astate->typlen,
4257 astate->typbyval,
4258 astate->typalign);
4260 MemoryContextSwitchTo(oldcontext);
4262 /* Clean up all the junk */
4263 if (release)
4264 MemoryContextDelete(astate->mcontext);
4266 return PointerGetDatum(result);
4269 Datum
4270 array_larger(PG_FUNCTION_ARGS)
4272 ArrayType *v1,
4273 *v2,
4274 *result;
4276 v1 = PG_GETARG_ARRAYTYPE_P(0);
4277 v2 = PG_GETARG_ARRAYTYPE_P(1);
4279 result = ((array_cmp(fcinfo) > 0) ? v1 : v2);
4281 PG_RETURN_ARRAYTYPE_P(result);
4284 Datum
4285 array_smaller(PG_FUNCTION_ARGS)
4287 ArrayType *v1,
4288 *v2,
4289 *result;
4291 v1 = PG_GETARG_ARRAYTYPE_P(0);
4292 v2 = PG_GETARG_ARRAYTYPE_P(1);
4294 result = ((array_cmp(fcinfo) < 0) ? v1 : v2);
4296 PG_RETURN_ARRAYTYPE_P(result);
4300 typedef struct generate_subscripts_fctx
4302 int4 lower;
4303 int4 upper;
4304 bool reverse;
4305 } generate_subscripts_fctx;
4308 * generate_subscripts(array anyarray, dim int [, reverse bool])
4309 * Returns all subscripts of the array for any dimension
4311 Datum
4312 generate_subscripts(PG_FUNCTION_ARGS)
4314 FuncCallContext *funcctx;
4315 MemoryContext oldcontext;
4316 generate_subscripts_fctx *fctx;
4318 /* stuff done only on the first call of the function */
4319 if (SRF_IS_FIRSTCALL())
4321 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
4322 int reqdim = PG_GETARG_INT32(1);
4323 int *lb,
4324 *dimv;
4326 /* create a function context for cross-call persistence */
4327 funcctx = SRF_FIRSTCALL_INIT();
4329 /* Sanity check: does it look like an array at all? */
4330 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
4331 SRF_RETURN_DONE(funcctx);
4333 /* Sanity check: was the requested dim valid */
4334 if (reqdim <= 0 || reqdim > ARR_NDIM(v))
4335 SRF_RETURN_DONE(funcctx);
4338 * switch to memory context appropriate for multiple function calls
4340 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
4341 fctx = (generate_subscripts_fctx *) palloc(sizeof(generate_subscripts_fctx));
4343 lb = ARR_LBOUND(v);
4344 dimv = ARR_DIMS(v);
4346 fctx->lower = lb[reqdim - 1];
4347 fctx->upper = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
4348 fctx->reverse = (PG_NARGS() < 3) ? false : PG_GETARG_BOOL(2);
4350 funcctx->user_fctx = fctx;
4352 MemoryContextSwitchTo(oldcontext);
4355 funcctx = SRF_PERCALL_SETUP();
4357 fctx = funcctx->user_fctx;
4359 if (fctx->lower <= fctx->upper)
4361 if (!fctx->reverse)
4362 SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->lower++));
4363 else
4364 SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->upper--));
4366 else
4367 /* done when there are no more elements left */
4368 SRF_RETURN_DONE(funcctx);
4372 * generate_subscripts_nodir
4373 * Implements the 2-argument version of generate_subscripts
4375 Datum
4376 generate_subscripts_nodir(PG_FUNCTION_ARGS)
4378 /* just call the other one -- it can handle both cases */
4379 return generate_subscripts(fcinfo);
4383 * array_fill_with_lower_bounds
4384 * Create and fill array with defined lower bounds.
4386 Datum
4387 array_fill_with_lower_bounds(PG_FUNCTION_ARGS)
4389 ArrayType *dims;
4390 ArrayType *lbs;
4391 ArrayType *result;
4392 Oid elmtype;
4393 Datum value;
4394 bool isnull;
4396 if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
4397 ereport(ERROR,
4398 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
4399 errmsg("dimension array or low bound array must not be null")));
4401 dims = PG_GETARG_ARRAYTYPE_P(1);
4402 lbs = PG_GETARG_ARRAYTYPE_P(2);
4404 if (!PG_ARGISNULL(0))
4406 value = PG_GETARG_DATUM(0);
4407 isnull = false;
4409 else
4411 value = 0;
4412 isnull = true;
4415 elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
4416 if (!OidIsValid(elmtype))
4417 elog(ERROR, "could not determine data type of input");
4419 result = array_fill_internal(dims, lbs, value, isnull, elmtype, fcinfo);
4420 PG_RETURN_ARRAYTYPE_P(result);
4424 * array_fill
4425 * Create and fill array with default lower bounds.
4427 Datum
4428 array_fill(PG_FUNCTION_ARGS)
4430 ArrayType *dims;
4431 ArrayType *result;
4432 Oid elmtype;
4433 Datum value;
4434 bool isnull;
4436 if (PG_ARGISNULL(1))
4437 ereport(ERROR,
4438 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
4439 errmsg("dimension array or low bound array must not be null")));
4441 dims = PG_GETARG_ARRAYTYPE_P(1);
4443 if (!PG_ARGISNULL(0))
4445 value = PG_GETARG_DATUM(0);
4446 isnull = false;
4448 else
4450 value = 0;
4451 isnull = true;
4454 elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
4455 if (!OidIsValid(elmtype))
4456 elog(ERROR, "could not determine data type of input");
4458 result = array_fill_internal(dims, NULL, value, isnull, elmtype, fcinfo);
4459 PG_RETURN_ARRAYTYPE_P(result);
4462 static ArrayType *
4463 create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
4464 Oid elmtype, int dataoffset)
4466 ArrayType *result;
4468 result = (ArrayType *) palloc0(nbytes);
4469 SET_VARSIZE(result, nbytes);
4470 result->ndim = ndims;
4471 result->dataoffset = dataoffset;
4472 result->elemtype = elmtype;
4473 memcpy(ARR_DIMS(result), dimv, ndims * sizeof(int));
4474 memcpy(ARR_LBOUND(result), lbsv, ndims * sizeof(int));
4476 return result;
4479 static ArrayType *
4480 array_fill_internal(ArrayType *dims, ArrayType *lbs,
4481 Datum value, bool isnull, Oid elmtype,
4482 FunctionCallInfo fcinfo)
4484 ArrayType *result;
4485 int *dimv;
4486 int *lbsv;
4487 int ndims;
4488 int nitems;
4489 int deflbs[MAXDIM];
4490 int16 elmlen;
4491 bool elmbyval;
4492 char elmalign;
4493 ArrayMetaState *my_extra;
4496 * Params checks
4498 if (ARR_NDIM(dims) != 1)
4499 ereport(ERROR,
4500 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4501 errmsg("wrong number of array subscripts"),
4502 errdetail("Dimension array must be one dimensional.")));
4504 if (ARR_LBOUND(dims)[0] != 1)
4505 ereport(ERROR,
4506 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4507 errmsg("wrong range of array subscripts"),
4508 errdetail("Lower bound of dimension array must be one.")));
4510 if (ARR_HASNULL(dims))
4511 ereport(ERROR,
4512 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
4513 errmsg("dimension values cannot be null")));
4515 dimv = (int *) ARR_DATA_PTR(dims);
4516 ndims = ARR_DIMS(dims)[0];
4518 if (ndims < 0) /* we do allow zero-dimension arrays */
4519 ereport(ERROR,
4520 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4521 errmsg("invalid number of dimensions: %d", ndims)));
4522 if (ndims > MAXDIM)
4523 ereport(ERROR,
4524 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
4525 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
4526 ndims, MAXDIM)));
4528 if (lbs != NULL)
4530 if (ARR_NDIM(lbs) != 1)
4531 ereport(ERROR,
4532 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4533 errmsg("wrong number of array subscripts"),
4534 errdetail("Dimension array must be one dimensional.")));
4536 if (ARR_LBOUND(lbs)[0] != 1)
4537 ereport(ERROR,
4538 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4539 errmsg("wrong range of array subscripts"),
4540 errdetail("Lower bound of dimension array must be one.")));
4542 if (ARR_HASNULL(lbs))
4543 ereport(ERROR,
4544 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
4545 errmsg("dimension values cannot be null")));
4547 if (ARR_DIMS(lbs)[0] != ndims)
4548 ereport(ERROR,
4549 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4550 errmsg("wrong number of array subscripts"),
4551 errdetail("Low bound array has different size than dimensions array.")));
4553 lbsv = (int *) ARR_DATA_PTR(lbs);
4555 else
4557 int i;
4559 for (i = 0; i < MAXDIM; i++)
4560 deflbs[i] = 1;
4562 lbsv = deflbs;
4565 /* fast track for empty array */
4566 if (ndims == 0)
4567 return construct_empty_array(elmtype);
4569 nitems = ArrayGetNItems(ndims, dimv);
4572 * We arrange to look up info about element type only once per series of
4573 * calls, assuming the element type doesn't change underneath us.
4575 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
4576 if (my_extra == NULL)
4578 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
4579 sizeof(ArrayMetaState));
4580 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
4581 my_extra->element_type = InvalidOid;
4584 if (my_extra->element_type != elmtype)
4586 /* Get info about element type */
4587 get_typlenbyvalalign(elmtype,
4588 &my_extra->typlen,
4589 &my_extra->typbyval,
4590 &my_extra->typalign);
4591 my_extra->element_type = elmtype;
4594 elmlen = my_extra->typlen;
4595 elmbyval = my_extra->typbyval;
4596 elmalign = my_extra->typalign;
4598 /* compute required space */
4599 if (!isnull)
4601 int i;
4602 char *p;
4603 int nbytes;
4604 int totbytes;
4606 /* make sure data is not toasted */
4607 if (elmlen == -1)
4608 value = PointerGetDatum(PG_DETOAST_DATUM(value));
4610 nbytes = att_addlength_datum(0, elmlen, value);
4611 nbytes = att_align_nominal(nbytes, elmalign);
4612 Assert(nbytes > 0);
4614 totbytes = nbytes * nitems;
4616 /* check for overflow of multiplication or total request */
4617 if (totbytes / nbytes != nitems ||
4618 !AllocSizeIsValid(totbytes))
4619 ereport(ERROR,
4620 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
4621 errmsg("array size exceeds the maximum allowed (%d)",
4622 (int) MaxAllocSize)));
4625 * This addition can't overflow, but it might cause us to go past
4626 * MaxAllocSize. We leave it to palloc to complain in that case.
4628 totbytes += ARR_OVERHEAD_NONULLS(ndims);
4630 result = create_array_envelope(ndims, dimv, lbsv, totbytes,
4631 elmtype, 0);
4633 p = ARR_DATA_PTR(result);
4634 for (i = 0; i < nitems; i++)
4635 p += ArrayCastAndSet(value, elmlen, elmbyval, elmalign, p);
4637 else
4639 int nbytes;
4640 int dataoffset;
4642 dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
4643 nbytes = dataoffset;
4645 result = create_array_envelope(ndims, dimv, lbsv, nbytes,
4646 elmtype, dataoffset);
4648 /* create_array_envelope already zeroed the bitmap, so we're done */
4651 return result;
4656 * UNNEST
4658 Datum
4659 array_unnest(PG_FUNCTION_ARGS)
4661 typedef struct
4663 ArrayType *arr;
4664 int nextelem;
4665 int numelems;
4666 char *elemdataptr; /* this moves with nextelem */
4667 bits8 *arraynullsptr; /* this does not */
4668 int16 elmlen;
4669 bool elmbyval;
4670 char elmalign;
4671 } array_unnest_fctx;
4673 FuncCallContext *funcctx;
4674 array_unnest_fctx *fctx;
4675 MemoryContext oldcontext;
4677 /* stuff done only on the first call of the function */
4678 if (SRF_IS_FIRSTCALL())
4680 ArrayType *arr;
4682 /* create a function context for cross-call persistence */
4683 funcctx = SRF_FIRSTCALL_INIT();
4686 * switch to memory context appropriate for multiple function calls
4688 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
4691 * Get the array value and detoast if needed. We can't do this
4692 * earlier because if we have to detoast, we want the detoasted copy
4693 * to be in multi_call_memory_ctx, so it will go away when we're done
4694 * and not before. (If no detoast happens, we assume the originally
4695 * passed array will stick around till then.)
4697 arr = PG_GETARG_ARRAYTYPE_P(0);
4699 /* allocate memory for user context */
4700 fctx = (array_unnest_fctx *) palloc(sizeof(array_unnest_fctx));
4702 /* initialize state */
4703 fctx->arr = arr;
4704 fctx->nextelem = 0;
4705 fctx->numelems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
4707 fctx->elemdataptr = ARR_DATA_PTR(arr);
4708 fctx->arraynullsptr = ARR_NULLBITMAP(arr);
4710 get_typlenbyvalalign(ARR_ELEMTYPE(arr),
4711 &fctx->elmlen,
4712 &fctx->elmbyval,
4713 &fctx->elmalign);
4715 funcctx->user_fctx = fctx;
4716 MemoryContextSwitchTo(oldcontext);
4719 /* stuff done on every call of the function */
4720 funcctx = SRF_PERCALL_SETUP();
4721 fctx = funcctx->user_fctx;
4723 if (fctx->nextelem < fctx->numelems)
4725 int offset = fctx->nextelem++;
4726 Datum elem;
4729 * Check for NULL array element
4731 if (array_get_isnull(fctx->arraynullsptr, offset))
4733 fcinfo->isnull = true;
4734 elem = (Datum) 0;
4735 /* elemdataptr does not move */
4737 else
4740 * OK, get the element
4742 char *ptr = fctx->elemdataptr;
4744 fcinfo->isnull = false;
4745 elem = ArrayCast(ptr, fctx->elmbyval, fctx->elmlen);
4748 * Advance elemdataptr over it
4750 ptr = att_addlength_pointer(ptr, fctx->elmlen, ptr);
4751 ptr = (char *) att_align_nominal(ptr, fctx->elmalign);
4752 fctx->elemdataptr = ptr;
4755 SRF_RETURN_NEXT(funcctx, elem);
4757 else
4759 /* do when there is no more left */
4760 SRF_RETURN_DONE(funcctx);