Properly access a buffer's LSN using existing access macros instead of abusing
[PostgreSQL.git] / src / backend / utils / adt / arrayfuncs.c
blob98f0f0c22215042582ad1a6fe6e912d543d11baa
1 /*-------------------------------------------------------------------------
3 * arrayfuncs.c
4 * Support functions for arrays.
6 * Portions Copyright (c) 1996-2008, 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_dims :
1535 * returns the dimensions of the array pointed to by "v", as a "text"
1537 Datum
1538 array_dims(PG_FUNCTION_ARGS)
1540 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1541 char *p;
1542 int i;
1543 int *dimv,
1544 *lb;
1547 * 33 since we assume 15 digits per number + ':' +'[]'
1549 * +1 for trailing null
1551 char buf[MAXDIM * 33 + 1];
1553 /* Sanity check: does it look like an array at all? */
1554 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1555 PG_RETURN_NULL();
1557 dimv = ARR_DIMS(v);
1558 lb = ARR_LBOUND(v);
1560 p = buf;
1561 for (i = 0; i < ARR_NDIM(v); i++)
1563 sprintf(p, "[%d:%d]", lb[i], dimv[i] + lb[i] - 1);
1564 p += strlen(p);
1567 PG_RETURN_TEXT_P(cstring_to_text(buf));
1571 * array_lower :
1572 * returns the lower dimension, of the DIM requested, for
1573 * the array pointed to by "v", as an int4
1575 Datum
1576 array_lower(PG_FUNCTION_ARGS)
1578 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1579 int reqdim = PG_GETARG_INT32(1);
1580 int *lb;
1581 int result;
1583 /* Sanity check: does it look like an array at all? */
1584 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1585 PG_RETURN_NULL();
1587 /* Sanity check: was the requested dim valid */
1588 if (reqdim <= 0 || reqdim > ARR_NDIM(v))
1589 PG_RETURN_NULL();
1591 lb = ARR_LBOUND(v);
1592 result = lb[reqdim - 1];
1594 PG_RETURN_INT32(result);
1598 * array_upper :
1599 * returns the upper dimension, of the DIM requested, for
1600 * the array pointed to by "v", as an int4
1602 Datum
1603 array_upper(PG_FUNCTION_ARGS)
1605 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
1606 int reqdim = PG_GETARG_INT32(1);
1607 int *dimv,
1608 *lb;
1609 int result;
1611 /* Sanity check: does it look like an array at all? */
1612 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
1613 PG_RETURN_NULL();
1615 /* Sanity check: was the requested dim valid */
1616 if (reqdim <= 0 || reqdim > ARR_NDIM(v))
1617 PG_RETURN_NULL();
1619 lb = ARR_LBOUND(v);
1620 dimv = ARR_DIMS(v);
1622 result = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
1624 PG_RETURN_INT32(result);
1628 * array_ref :
1629 * This routine takes an array pointer and a subscript array and returns
1630 * the referenced item as a Datum. Note that for a pass-by-reference
1631 * datatype, the returned Datum is a pointer into the array object.
1633 * This handles both ordinary varlena arrays and fixed-length arrays.
1635 * Inputs:
1636 * array: the array object (mustn't be NULL)
1637 * nSubscripts: number of subscripts supplied
1638 * indx[]: the subscript values
1639 * arraytyplen: pg_type.typlen for the array type
1640 * elmlen: pg_type.typlen for the array's element type
1641 * elmbyval: pg_type.typbyval for the array's element type
1642 * elmalign: pg_type.typalign for the array's element type
1644 * Outputs:
1645 * The return value is the element Datum.
1646 * *isNull is set to indicate whether the element is NULL.
1648 Datum
1649 array_ref(ArrayType *array,
1650 int nSubscripts,
1651 int *indx,
1652 int arraytyplen,
1653 int elmlen,
1654 bool elmbyval,
1655 char elmalign,
1656 bool *isNull)
1658 int i,
1659 ndim,
1660 *dim,
1661 *lb,
1662 offset,
1663 fixedDim[1],
1664 fixedLb[1];
1665 char *arraydataptr,
1666 *retptr;
1667 bits8 *arraynullsptr;
1669 if (arraytyplen > 0)
1672 * fixed-length arrays -- these are assumed to be 1-d, 0-based
1674 ndim = 1;
1675 fixedDim[0] = arraytyplen / elmlen;
1676 fixedLb[0] = 0;
1677 dim = fixedDim;
1678 lb = fixedLb;
1679 arraydataptr = (char *) array;
1680 arraynullsptr = NULL;
1682 else
1684 /* detoast input array if necessary */
1685 array = DatumGetArrayTypeP(PointerGetDatum(array));
1687 ndim = ARR_NDIM(array);
1688 dim = ARR_DIMS(array);
1689 lb = ARR_LBOUND(array);
1690 arraydataptr = ARR_DATA_PTR(array);
1691 arraynullsptr = ARR_NULLBITMAP(array);
1695 * Return NULL for invalid subscript
1697 if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
1699 *isNull = true;
1700 return (Datum) 0;
1702 for (i = 0; i < ndim; i++)
1704 if (indx[i] < lb[i] || indx[i] >= (dim[i] + lb[i]))
1706 *isNull = true;
1707 return (Datum) 0;
1712 * Calculate the element number
1714 offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
1717 * Check for NULL array element
1719 if (array_get_isnull(arraynullsptr, offset))
1721 *isNull = true;
1722 return (Datum) 0;
1726 * OK, get the element
1728 *isNull = false;
1729 retptr = array_seek(arraydataptr, 0, arraynullsptr, offset,
1730 elmlen, elmbyval, elmalign);
1731 return ArrayCast(retptr, elmbyval, elmlen);
1735 * array_get_slice :
1736 * This routine takes an array and a range of indices (upperIndex and
1737 * lowerIndx), creates a new array structure for the referred elements
1738 * and returns a pointer to it.
1740 * This handles both ordinary varlena arrays and fixed-length arrays.
1742 * Inputs:
1743 * array: the array object (mustn't be NULL)
1744 * nSubscripts: number of subscripts supplied (must be same for upper/lower)
1745 * upperIndx[]: the upper subscript values
1746 * lowerIndx[]: the lower subscript values
1747 * arraytyplen: pg_type.typlen for the array type
1748 * elmlen: pg_type.typlen for the array's element type
1749 * elmbyval: pg_type.typbyval for the array's element type
1750 * elmalign: pg_type.typalign for the array's element type
1752 * Outputs:
1753 * The return value is the new array Datum (it's never NULL)
1755 * NOTE: we assume it is OK to scribble on the provided subscript arrays
1756 * lowerIndx[] and upperIndx[]. These are generally just temporaries.
1758 ArrayType *
1759 array_get_slice(ArrayType *array,
1760 int nSubscripts,
1761 int *upperIndx,
1762 int *lowerIndx,
1763 int arraytyplen,
1764 int elmlen,
1765 bool elmbyval,
1766 char elmalign)
1768 ArrayType *newarray;
1769 int i,
1770 ndim,
1771 *dim,
1772 *lb,
1773 *newlb;
1774 int fixedDim[1],
1775 fixedLb[1];
1776 Oid elemtype;
1777 char *arraydataptr;
1778 bits8 *arraynullsptr;
1779 int32 dataoffset;
1780 int bytes,
1781 span[MAXDIM];
1783 if (arraytyplen > 0)
1786 * fixed-length arrays -- currently, cannot slice these because parser
1787 * labels output as being of the fixed-length array type! Code below
1788 * shows how we could support it if the parser were changed to label
1789 * output as a suitable varlena array type.
1791 ereport(ERROR,
1792 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1793 errmsg("slices of fixed-length arrays not implemented")));
1796 * fixed-length arrays -- these are assumed to be 1-d, 0-based
1798 * XXX where would we get the correct ELEMTYPE from?
1800 ndim = 1;
1801 fixedDim[0] = arraytyplen / elmlen;
1802 fixedLb[0] = 0;
1803 dim = fixedDim;
1804 lb = fixedLb;
1805 elemtype = InvalidOid; /* XXX */
1806 arraydataptr = (char *) array;
1807 arraynullsptr = NULL;
1809 else
1811 /* detoast input array if necessary */
1812 array = DatumGetArrayTypeP(PointerGetDatum(array));
1814 ndim = ARR_NDIM(array);
1815 dim = ARR_DIMS(array);
1816 lb = ARR_LBOUND(array);
1817 elemtype = ARR_ELEMTYPE(array);
1818 arraydataptr = ARR_DATA_PTR(array);
1819 arraynullsptr = ARR_NULLBITMAP(array);
1823 * Check provided subscripts. A slice exceeding the current array limits
1824 * is silently truncated to the array limits. If we end up with an empty
1825 * slice, return an empty array.
1827 if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
1828 return construct_empty_array(elemtype);
1830 for (i = 0; i < nSubscripts; i++)
1832 if (lowerIndx[i] < lb[i])
1833 lowerIndx[i] = lb[i];
1834 if (upperIndx[i] >= (dim[i] + lb[i]))
1835 upperIndx[i] = dim[i] + lb[i] - 1;
1836 if (lowerIndx[i] > upperIndx[i])
1837 return construct_empty_array(elemtype);
1839 /* fill any missing subscript positions with full array range */
1840 for (; i < ndim; i++)
1842 lowerIndx[i] = lb[i];
1843 upperIndx[i] = dim[i] + lb[i] - 1;
1844 if (lowerIndx[i] > upperIndx[i])
1845 return construct_empty_array(elemtype);
1848 mda_get_range(ndim, span, lowerIndx, upperIndx);
1850 bytes = array_slice_size(arraydataptr, arraynullsptr,
1851 ndim, dim, lb,
1852 lowerIndx, upperIndx,
1853 elmlen, elmbyval, elmalign);
1856 * Currently, we put a null bitmap in the result if the source has one;
1857 * could be smarter ...
1859 if (arraynullsptr)
1861 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, ArrayGetNItems(ndim, span));
1862 bytes += dataoffset;
1864 else
1866 dataoffset = 0; /* marker for no null bitmap */
1867 bytes += ARR_OVERHEAD_NONULLS(ndim);
1870 newarray = (ArrayType *) palloc(bytes);
1871 SET_VARSIZE(newarray, bytes);
1872 newarray->ndim = ndim;
1873 newarray->dataoffset = dataoffset;
1874 newarray->elemtype = elemtype;
1875 memcpy(ARR_DIMS(newarray), span, ndim * sizeof(int));
1878 * Lower bounds of the new array are set to 1. Formerly (before 7.3) we
1879 * copied the given lowerIndx values ... but that seems confusing.
1881 newlb = ARR_LBOUND(newarray);
1882 for (i = 0; i < ndim; i++)
1883 newlb[i] = 1;
1885 array_extract_slice(newarray,
1886 ndim, dim, lb,
1887 arraydataptr, arraynullsptr,
1888 lowerIndx, upperIndx,
1889 elmlen, elmbyval, elmalign);
1891 return newarray;
1895 * array_set :
1896 * This routine sets the value of an array element (specified by
1897 * a subscript array) to a new value specified by "dataValue".
1899 * This handles both ordinary varlena arrays and fixed-length arrays.
1901 * Inputs:
1902 * array: the initial array object (mustn't be NULL)
1903 * nSubscripts: number of subscripts supplied
1904 * indx[]: the subscript values
1905 * dataValue: the datum to be inserted at the given position
1906 * isNull: whether dataValue is NULL
1907 * arraytyplen: pg_type.typlen for the array type
1908 * elmlen: pg_type.typlen for the array's element type
1909 * elmbyval: pg_type.typbyval for the array's element type
1910 * elmalign: pg_type.typalign for the array's element type
1912 * Result:
1913 * A new array is returned, just like the old except for the one
1914 * modified entry. The original array object is not changed.
1916 * For one-dimensional arrays only, we allow the array to be extended
1917 * by assigning to a position outside the existing subscript range; any
1918 * positions between the existing elements and the new one are set to NULLs.
1919 * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
1921 * NOTE: For assignments, we throw an error for invalid subscripts etc,
1922 * rather than returning a NULL as the fetch operations do.
1924 ArrayType *
1925 array_set(ArrayType *array,
1926 int nSubscripts,
1927 int *indx,
1928 Datum dataValue,
1929 bool isNull,
1930 int arraytyplen,
1931 int elmlen,
1932 bool elmbyval,
1933 char elmalign)
1935 ArrayType *newarray;
1936 int i,
1937 ndim,
1938 dim[MAXDIM],
1939 lb[MAXDIM],
1940 offset;
1941 char *elt_ptr;
1942 bool newhasnulls;
1943 bits8 *oldnullbitmap;
1944 int oldnitems,
1945 newnitems,
1946 olddatasize,
1947 newsize,
1948 olditemlen,
1949 newitemlen,
1950 overheadlen,
1951 oldoverheadlen,
1952 addedbefore,
1953 addedafter,
1954 lenbefore,
1955 lenafter;
1957 if (arraytyplen > 0)
1960 * fixed-length arrays -- these are assumed to be 1-d, 0-based. We
1961 * cannot extend them, either.
1963 if (nSubscripts != 1)
1964 ereport(ERROR,
1965 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1966 errmsg("wrong number of array subscripts")));
1968 if (indx[0] < 0 || indx[0] * elmlen >= arraytyplen)
1969 ereport(ERROR,
1970 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1971 errmsg("array subscript out of range")));
1973 if (isNull)
1974 ereport(ERROR,
1975 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
1976 errmsg("cannot assign null value to an element of a fixed-length array")));
1978 newarray = (ArrayType *) palloc(arraytyplen);
1979 memcpy(newarray, array, arraytyplen);
1980 elt_ptr = (char *) newarray + indx[0] * elmlen;
1981 ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign, elt_ptr);
1982 return newarray;
1985 if (nSubscripts <= 0 || nSubscripts > MAXDIM)
1986 ereport(ERROR,
1987 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1988 errmsg("wrong number of array subscripts")));
1990 /* make sure item to be inserted is not toasted */
1991 if (elmlen == -1 && !isNull)
1992 dataValue = PointerGetDatum(PG_DETOAST_DATUM(dataValue));
1994 /* detoast input array if necessary */
1995 array = DatumGetArrayTypeP(PointerGetDatum(array));
1997 ndim = ARR_NDIM(array);
2000 * if number of dims is zero, i.e. an empty array, create an array with
2001 * nSubscripts dimensions, and set the lower bounds to the supplied
2002 * subscripts
2004 if (ndim == 0)
2006 Oid elmtype = ARR_ELEMTYPE(array);
2008 for (i = 0; i < nSubscripts; i++)
2010 dim[i] = 1;
2011 lb[i] = indx[i];
2014 return construct_md_array(&dataValue, &isNull, nSubscripts,
2015 dim, lb, elmtype,
2016 elmlen, elmbyval, elmalign);
2019 if (ndim != nSubscripts)
2020 ereport(ERROR,
2021 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2022 errmsg("wrong number of array subscripts")));
2024 /* copy dim/lb since we may modify them */
2025 memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
2026 memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
2028 newhasnulls = (ARR_HASNULL(array) || isNull);
2029 addedbefore = addedafter = 0;
2032 * Check subscripts
2034 if (ndim == 1)
2036 if (indx[0] < lb[0])
2038 addedbefore = lb[0] - indx[0];
2039 dim[0] += addedbefore;
2040 lb[0] = indx[0];
2041 if (addedbefore > 1)
2042 newhasnulls = true; /* will insert nulls */
2044 if (indx[0] >= (dim[0] + lb[0]))
2046 addedafter = indx[0] - (dim[0] + lb[0]) + 1;
2047 dim[0] += addedafter;
2048 if (addedafter > 1)
2049 newhasnulls = true; /* will insert nulls */
2052 else
2055 * XXX currently we do not support extending multi-dimensional arrays
2056 * during assignment
2058 for (i = 0; i < ndim; i++)
2060 if (indx[i] < lb[i] ||
2061 indx[i] >= (dim[i] + lb[i]))
2062 ereport(ERROR,
2063 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2064 errmsg("array subscript out of range")));
2069 * Compute sizes of items and areas to copy
2071 newnitems = ArrayGetNItems(ndim, dim);
2072 if (newhasnulls)
2073 overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, newnitems);
2074 else
2075 overheadlen = ARR_OVERHEAD_NONULLS(ndim);
2076 oldnitems = ArrayGetNItems(ndim, ARR_DIMS(array));
2077 oldnullbitmap = ARR_NULLBITMAP(array);
2078 oldoverheadlen = ARR_DATA_OFFSET(array);
2079 olddatasize = ARR_SIZE(array) - oldoverheadlen;
2080 if (addedbefore)
2082 offset = 0;
2083 lenbefore = 0;
2084 olditemlen = 0;
2085 lenafter = olddatasize;
2087 else if (addedafter)
2089 offset = oldnitems;
2090 lenbefore = olddatasize;
2091 olditemlen = 0;
2092 lenafter = 0;
2094 else
2096 offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
2097 elt_ptr = array_seek(ARR_DATA_PTR(array), 0, oldnullbitmap, offset,
2098 elmlen, elmbyval, elmalign);
2099 lenbefore = (int) (elt_ptr - ARR_DATA_PTR(array));
2100 if (array_get_isnull(oldnullbitmap, offset))
2101 olditemlen = 0;
2102 else
2104 olditemlen = att_addlength_pointer(0, elmlen, elt_ptr);
2105 olditemlen = att_align_nominal(olditemlen, elmalign);
2107 lenafter = (int) (olddatasize - lenbefore - olditemlen);
2110 if (isNull)
2111 newitemlen = 0;
2112 else
2114 newitemlen = att_addlength_datum(0, elmlen, dataValue);
2115 newitemlen = att_align_nominal(newitemlen, elmalign);
2118 newsize = overheadlen + lenbefore + newitemlen + lenafter;
2121 * OK, create the new array and fill in header/dimensions
2123 newarray = (ArrayType *) palloc(newsize);
2124 SET_VARSIZE(newarray, newsize);
2125 newarray->ndim = ndim;
2126 newarray->dataoffset = newhasnulls ? overheadlen : 0;
2127 newarray->elemtype = ARR_ELEMTYPE(array);
2128 memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
2129 memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
2132 * Fill in data
2134 memcpy((char *) newarray + overheadlen,
2135 (char *) array + oldoverheadlen,
2136 lenbefore);
2137 if (!isNull)
2138 ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign,
2139 (char *) newarray + overheadlen + lenbefore);
2140 memcpy((char *) newarray + overheadlen + lenbefore + newitemlen,
2141 (char *) array + oldoverheadlen + lenbefore + olditemlen,
2142 lenafter);
2145 * Fill in nulls bitmap if needed
2147 * Note: it's possible we just replaced the last NULL with a non-NULL, and
2148 * could get rid of the bitmap. Seems not worth testing for though.
2150 if (newhasnulls)
2152 bits8 *newnullbitmap = ARR_NULLBITMAP(newarray);
2154 /* Zero the bitmap to take care of marking inserted positions null */
2155 MemSet(newnullbitmap, 0, (newnitems + 7) / 8);
2156 /* Fix the inserted value */
2157 if (addedafter)
2158 array_set_isnull(newnullbitmap, newnitems - 1, isNull);
2159 else
2160 array_set_isnull(newnullbitmap, offset, isNull);
2161 /* Fix the copied range(s) */
2162 if (addedbefore)
2163 array_bitmap_copy(newnullbitmap, addedbefore,
2164 oldnullbitmap, 0,
2165 oldnitems);
2166 else
2168 array_bitmap_copy(newnullbitmap, 0,
2169 oldnullbitmap, 0,
2170 offset);
2171 if (addedafter == 0)
2172 array_bitmap_copy(newnullbitmap, offset + 1,
2173 oldnullbitmap, offset + 1,
2174 oldnitems - offset - 1);
2178 return newarray;
2182 * array_set_slice :
2183 * This routine sets the value of a range of array locations (specified
2184 * by upper and lower subscript values) to new values passed as
2185 * another array.
2187 * This handles both ordinary varlena arrays and fixed-length arrays.
2189 * Inputs:
2190 * array: the initial array object (mustn't be NULL)
2191 * nSubscripts: number of subscripts supplied (must be same for upper/lower)
2192 * upperIndx[]: the upper subscript values
2193 * lowerIndx[]: the lower subscript values
2194 * srcArray: the source for the inserted values
2195 * isNull: indicates whether srcArray is NULL
2196 * arraytyplen: pg_type.typlen for the array type
2197 * elmlen: pg_type.typlen for the array's element type
2198 * elmbyval: pg_type.typbyval for the array's element type
2199 * elmalign: pg_type.typalign for the array's element type
2201 * Result:
2202 * A new array is returned, just like the old except for the
2203 * modified range. The original array object is not changed.
2205 * For one-dimensional arrays only, we allow the array to be extended
2206 * by assigning to positions outside the existing subscript range; any
2207 * positions between the existing elements and the new ones are set to NULLs.
2208 * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
2210 * NOTE: we assume it is OK to scribble on the provided index arrays
2211 * lowerIndx[] and upperIndx[]. These are generally just temporaries.
2213 * NOTE: For assignments, we throw an error for silly subscripts etc,
2214 * rather than returning a NULL or empty array as the fetch operations do.
2216 ArrayType *
2217 array_set_slice(ArrayType *array,
2218 int nSubscripts,
2219 int *upperIndx,
2220 int *lowerIndx,
2221 ArrayType *srcArray,
2222 bool isNull,
2223 int arraytyplen,
2224 int elmlen,
2225 bool elmbyval,
2226 char elmalign)
2228 ArrayType *newarray;
2229 int i,
2230 ndim,
2231 dim[MAXDIM],
2232 lb[MAXDIM],
2233 span[MAXDIM];
2234 bool newhasnulls;
2235 int nitems,
2236 nsrcitems,
2237 olddatasize,
2238 newsize,
2239 olditemsize,
2240 newitemsize,
2241 overheadlen,
2242 oldoverheadlen,
2243 addedbefore,
2244 addedafter,
2245 lenbefore,
2246 lenafter,
2247 itemsbefore,
2248 itemsafter,
2249 nolditems;
2251 /* Currently, assignment from a NULL source array is a no-op */
2252 if (isNull)
2253 return array;
2255 if (arraytyplen > 0)
2258 * fixed-length arrays -- not got round to doing this...
2260 ereport(ERROR,
2261 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2262 errmsg("updates on slices of fixed-length arrays not implemented")));
2265 /* detoast arrays if necessary */
2266 array = DatumGetArrayTypeP(PointerGetDatum(array));
2267 srcArray = DatumGetArrayTypeP(PointerGetDatum(srcArray));
2269 /* note: we assume srcArray contains no toasted elements */
2271 ndim = ARR_NDIM(array);
2274 * if number of dims is zero, i.e. an empty array, create an array with
2275 * nSubscripts dimensions, and set the upper and lower bounds to the
2276 * supplied subscripts
2278 if (ndim == 0)
2280 Datum *dvalues;
2281 bool *dnulls;
2282 int nelems;
2283 Oid elmtype = ARR_ELEMTYPE(array);
2285 deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
2286 &dvalues, &dnulls, &nelems);
2288 for (i = 0; i < nSubscripts; i++)
2290 dim[i] = 1 + upperIndx[i] - lowerIndx[i];
2291 lb[i] = lowerIndx[i];
2294 /* complain if too few source items; we ignore extras, however */
2295 if (nelems < ArrayGetNItems(nSubscripts, dim))
2296 ereport(ERROR,
2297 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2298 errmsg("source array too small")));
2300 return construct_md_array(dvalues, dnulls, nSubscripts,
2301 dim, lb, elmtype,
2302 elmlen, elmbyval, elmalign);
2305 if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
2306 ereport(ERROR,
2307 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2308 errmsg("wrong number of array subscripts")));
2310 /* copy dim/lb since we may modify them */
2311 memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
2312 memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
2314 newhasnulls = (ARR_HASNULL(array) || ARR_HASNULL(srcArray));
2315 addedbefore = addedafter = 0;
2318 * Check subscripts
2320 if (ndim == 1)
2322 Assert(nSubscripts == 1);
2323 if (lowerIndx[0] > upperIndx[0])
2324 ereport(ERROR,
2325 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2326 errmsg("upper bound cannot be less than lower bound")));
2327 if (lowerIndx[0] < lb[0])
2329 if (upperIndx[0] < lb[0] - 1)
2330 newhasnulls = true; /* will insert nulls */
2331 addedbefore = lb[0] - lowerIndx[0];
2332 dim[0] += addedbefore;
2333 lb[0] = lowerIndx[0];
2335 if (upperIndx[0] >= (dim[0] + lb[0]))
2337 if (lowerIndx[0] > (dim[0] + lb[0]))
2338 newhasnulls = true; /* will insert nulls */
2339 addedafter = upperIndx[0] - (dim[0] + lb[0]) + 1;
2340 dim[0] += addedafter;
2343 else
2346 * XXX currently we do not support extending multi-dimensional arrays
2347 * during assignment
2349 for (i = 0; i < nSubscripts; i++)
2351 if (lowerIndx[i] > upperIndx[i])
2352 ereport(ERROR,
2353 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2354 errmsg("upper bound cannot be less than lower bound")));
2355 if (lowerIndx[i] < lb[i] ||
2356 upperIndx[i] >= (dim[i] + lb[i]))
2357 ereport(ERROR,
2358 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2359 errmsg("array subscript out of range")));
2361 /* fill any missing subscript positions with full array range */
2362 for (; i < ndim; i++)
2364 lowerIndx[i] = lb[i];
2365 upperIndx[i] = dim[i] + lb[i] - 1;
2366 if (lowerIndx[i] > upperIndx[i])
2367 ereport(ERROR,
2368 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2369 errmsg("upper bound cannot be less than lower bound")));
2373 /* Do this mainly to check for overflow */
2374 nitems = ArrayGetNItems(ndim, dim);
2377 * Make sure source array has enough entries. Note we ignore the shape of
2378 * the source array and just read entries serially.
2380 mda_get_range(ndim, span, lowerIndx, upperIndx);
2381 nsrcitems = ArrayGetNItems(ndim, span);
2382 if (nsrcitems > ArrayGetNItems(ARR_NDIM(srcArray), ARR_DIMS(srcArray)))
2383 ereport(ERROR,
2384 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2385 errmsg("source array too small")));
2388 * Compute space occupied by new entries, space occupied by replaced
2389 * entries, and required space for new array.
2391 if (newhasnulls)
2392 overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
2393 else
2394 overheadlen = ARR_OVERHEAD_NONULLS(ndim);
2395 newitemsize = array_nelems_size(ARR_DATA_PTR(srcArray), 0,
2396 ARR_NULLBITMAP(srcArray), nsrcitems,
2397 elmlen, elmbyval, elmalign);
2398 oldoverheadlen = ARR_DATA_OFFSET(array);
2399 olddatasize = ARR_SIZE(array) - oldoverheadlen;
2400 if (ndim > 1)
2403 * here we do not need to cope with extension of the array; it would
2404 * be a lot more complicated if we had to do so...
2406 olditemsize = array_slice_size(ARR_DATA_PTR(array),
2407 ARR_NULLBITMAP(array),
2408 ndim, dim, lb,
2409 lowerIndx, upperIndx,
2410 elmlen, elmbyval, elmalign);
2411 lenbefore = lenafter = 0; /* keep compiler quiet */
2412 itemsbefore = itemsafter = nolditems = 0;
2414 else
2417 * here we must allow for possibility of slice larger than orig array
2419 int oldlb = ARR_LBOUND(array)[0];
2420 int oldub = oldlb + ARR_DIMS(array)[0] - 1;
2421 int slicelb = Max(oldlb, lowerIndx[0]);
2422 int sliceub = Min(oldub, upperIndx[0]);
2423 char *oldarraydata = ARR_DATA_PTR(array);
2424 bits8 *oldarraybitmap = ARR_NULLBITMAP(array);
2426 itemsbefore = Min(slicelb, oldub + 1) - oldlb;
2427 lenbefore = array_nelems_size(oldarraydata, 0, oldarraybitmap,
2428 itemsbefore,
2429 elmlen, elmbyval, elmalign);
2430 if (slicelb > sliceub)
2432 nolditems = 0;
2433 olditemsize = 0;
2435 else
2437 nolditems = sliceub - slicelb + 1;
2438 olditemsize = array_nelems_size(oldarraydata + lenbefore,
2439 itemsbefore, oldarraybitmap,
2440 nolditems,
2441 elmlen, elmbyval, elmalign);
2443 itemsafter = oldub - sliceub;
2444 lenafter = olddatasize - lenbefore - olditemsize;
2447 newsize = overheadlen + olddatasize - olditemsize + newitemsize;
2449 newarray = (ArrayType *) palloc(newsize);
2450 SET_VARSIZE(newarray, newsize);
2451 newarray->ndim = ndim;
2452 newarray->dataoffset = newhasnulls ? overheadlen : 0;
2453 newarray->elemtype = ARR_ELEMTYPE(array);
2454 memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
2455 memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
2457 if (ndim > 1)
2460 * here we do not need to cope with extension of the array; it would
2461 * be a lot more complicated if we had to do so...
2463 array_insert_slice(newarray, array, srcArray,
2464 ndim, dim, lb,
2465 lowerIndx, upperIndx,
2466 elmlen, elmbyval, elmalign);
2468 else
2470 /* fill in data */
2471 memcpy((char *) newarray + overheadlen,
2472 (char *) array + oldoverheadlen,
2473 lenbefore);
2474 memcpy((char *) newarray + overheadlen + lenbefore,
2475 ARR_DATA_PTR(srcArray),
2476 newitemsize);
2477 memcpy((char *) newarray + overheadlen + lenbefore + newitemsize,
2478 (char *) array + oldoverheadlen + lenbefore + olditemsize,
2479 lenafter);
2480 /* fill in nulls bitmap if needed */
2481 if (newhasnulls)
2483 bits8 *newnullbitmap = ARR_NULLBITMAP(newarray);
2484 bits8 *oldnullbitmap = ARR_NULLBITMAP(array);
2486 /* Zero the bitmap to handle marking inserted positions null */
2487 MemSet(newnullbitmap, 0, (nitems + 7) / 8);
2488 array_bitmap_copy(newnullbitmap, addedbefore,
2489 oldnullbitmap, 0,
2490 itemsbefore);
2491 array_bitmap_copy(newnullbitmap, lowerIndx[0] - lb[0],
2492 ARR_NULLBITMAP(srcArray), 0,
2493 nsrcitems);
2494 array_bitmap_copy(newnullbitmap, addedbefore + itemsbefore + nolditems,
2495 oldnullbitmap, itemsbefore + nolditems,
2496 itemsafter);
2500 return newarray;
2504 * array_map()
2506 * Map an array through an arbitrary function. Return a new array with
2507 * same dimensions and each source element transformed by fn(). Each
2508 * source element is passed as the first argument to fn(); additional
2509 * arguments to be passed to fn() can be specified by the caller.
2510 * The output array can have a different element type than the input.
2512 * Parameters are:
2513 * * fcinfo: a function-call data structure pre-constructed by the caller
2514 * to be ready to call the desired function, with everything except the
2515 * first argument position filled in. In particular, flinfo identifies
2516 * the function fn(), and if nargs > 1 then argument positions after the
2517 * first must be preset to the additional values to be passed. The
2518 * first argument position initially holds the input array value.
2519 * * inpType: OID of element type of input array. This must be the same as,
2520 * or binary-compatible with, the first argument type of fn().
2521 * * retType: OID of element type of output array. This must be the same as,
2522 * or binary-compatible with, the result type of fn().
2523 * * amstate: workspace for array_map. Must be zeroed by caller before
2524 * first call, and not touched after that.
2526 * It is legitimate to pass a freshly-zeroed ArrayMapState on each call,
2527 * but better performance can be had if the state can be preserved across
2528 * a series of calls.
2530 * NB: caller must assure that input array is not NULL. NULL elements in
2531 * the array are OK however.
2533 Datum
2534 array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType,
2535 ArrayMapState *amstate)
2537 ArrayType *v;
2538 ArrayType *result;
2539 Datum *values;
2540 bool *nulls;
2541 Datum elt;
2542 int *dim;
2543 int ndim;
2544 int nitems;
2545 int i;
2546 int32 nbytes = 0;
2547 int32 dataoffset;
2548 bool hasnulls;
2549 int inp_typlen;
2550 bool inp_typbyval;
2551 char inp_typalign;
2552 int typlen;
2553 bool typbyval;
2554 char typalign;
2555 char *s;
2556 bits8 *bitmap;
2557 int bitmask;
2558 ArrayMetaState *inp_extra;
2559 ArrayMetaState *ret_extra;
2561 /* Get input array */
2562 if (fcinfo->nargs < 1)
2563 elog(ERROR, "invalid nargs: %d", fcinfo->nargs);
2564 if (PG_ARGISNULL(0))
2565 elog(ERROR, "null input array");
2566 v = PG_GETARG_ARRAYTYPE_P(0);
2568 Assert(ARR_ELEMTYPE(v) == inpType);
2570 ndim = ARR_NDIM(v);
2571 dim = ARR_DIMS(v);
2572 nitems = ArrayGetNItems(ndim, dim);
2574 /* Check for empty array */
2575 if (nitems <= 0)
2577 /* Return empty array */
2578 PG_RETURN_ARRAYTYPE_P(construct_empty_array(retType));
2582 * We arrange to look up info about input and return element types only
2583 * once per series of calls, assuming the element type doesn't change
2584 * underneath us.
2586 inp_extra = &amstate->inp_extra;
2587 ret_extra = &amstate->ret_extra;
2589 if (inp_extra->element_type != inpType)
2591 get_typlenbyvalalign(inpType,
2592 &inp_extra->typlen,
2593 &inp_extra->typbyval,
2594 &inp_extra->typalign);
2595 inp_extra->element_type = inpType;
2597 inp_typlen = inp_extra->typlen;
2598 inp_typbyval = inp_extra->typbyval;
2599 inp_typalign = inp_extra->typalign;
2601 if (ret_extra->element_type != retType)
2603 get_typlenbyvalalign(retType,
2604 &ret_extra->typlen,
2605 &ret_extra->typbyval,
2606 &ret_extra->typalign);
2607 ret_extra->element_type = retType;
2609 typlen = ret_extra->typlen;
2610 typbyval = ret_extra->typbyval;
2611 typalign = ret_extra->typalign;
2613 /* Allocate temporary arrays for new values */
2614 values = (Datum *) palloc(nitems * sizeof(Datum));
2615 nulls = (bool *) palloc(nitems * sizeof(bool));
2617 /* Loop over source data */
2618 s = ARR_DATA_PTR(v);
2619 bitmap = ARR_NULLBITMAP(v);
2620 bitmask = 1;
2621 hasnulls = false;
2623 for (i = 0; i < nitems; i++)
2625 bool callit = true;
2627 /* Get source element, checking for NULL */
2628 if (bitmap && (*bitmap & bitmask) == 0)
2630 fcinfo->argnull[0] = true;
2632 else
2634 elt = fetch_att(s, inp_typbyval, inp_typlen);
2635 s = att_addlength_datum(s, inp_typlen, elt);
2636 s = (char *) att_align_nominal(s, inp_typalign);
2637 fcinfo->arg[0] = elt;
2638 fcinfo->argnull[0] = false;
2642 * Apply the given function to source elt and extra args.
2644 if (fcinfo->flinfo->fn_strict)
2646 int j;
2648 for (j = 0; j < fcinfo->nargs; j++)
2650 if (fcinfo->argnull[j])
2652 callit = false;
2653 break;
2658 if (callit)
2660 fcinfo->isnull = false;
2661 values[i] = FunctionCallInvoke(fcinfo);
2663 else
2664 fcinfo->isnull = true;
2666 nulls[i] = fcinfo->isnull;
2667 if (fcinfo->isnull)
2668 hasnulls = true;
2669 else
2671 /* Ensure data is not toasted */
2672 if (typlen == -1)
2673 values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
2674 /* Update total result size */
2675 nbytes = att_addlength_datum(nbytes, typlen, values[i]);
2676 nbytes = att_align_nominal(nbytes, typalign);
2677 /* check for overflow of total request */
2678 if (!AllocSizeIsValid(nbytes))
2679 ereport(ERROR,
2680 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2681 errmsg("array size exceeds the maximum allowed (%d)",
2682 (int) MaxAllocSize)));
2685 /* advance bitmap pointer if any */
2686 if (bitmap)
2688 bitmask <<= 1;
2689 if (bitmask == 0x100)
2691 bitmap++;
2692 bitmask = 1;
2697 /* Allocate and initialize the result array */
2698 if (hasnulls)
2700 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
2701 nbytes += dataoffset;
2703 else
2705 dataoffset = 0; /* marker for no null bitmap */
2706 nbytes += ARR_OVERHEAD_NONULLS(ndim);
2708 result = (ArrayType *) palloc(nbytes);
2709 SET_VARSIZE(result, nbytes);
2710 result->ndim = ndim;
2711 result->dataoffset = dataoffset;
2712 result->elemtype = retType;
2713 memcpy(ARR_DIMS(result), ARR_DIMS(v), 2 * ndim * sizeof(int));
2716 * Note: do not risk trying to pfree the results of the called function
2718 CopyArrayEls(result,
2719 values, nulls, nitems,
2720 typlen, typbyval, typalign,
2721 false);
2723 pfree(values);
2724 pfree(nulls);
2726 PG_RETURN_ARRAYTYPE_P(result);
2730 * construct_array --- simple method for constructing an array object
2732 * elems: array of Datum items to become the array contents
2733 * (NULL element values are not supported).
2734 * nelems: number of items
2735 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
2737 * A palloc'd 1-D array object is constructed and returned. Note that
2738 * elem values will be copied into the object even if pass-by-ref type.
2740 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
2741 * from the system catalogs, given the elmtype. However, the caller is
2742 * in a better position to cache this info across multiple uses, or even
2743 * to hard-wire values if the element type is hard-wired.
2745 ArrayType *
2746 construct_array(Datum *elems, int nelems,
2747 Oid elmtype,
2748 int elmlen, bool elmbyval, char elmalign)
2750 int dims[1];
2751 int lbs[1];
2753 dims[0] = nelems;
2754 lbs[0] = 1;
2756 return construct_md_array(elems, NULL, 1, dims, lbs,
2757 elmtype, elmlen, elmbyval, elmalign);
2761 * construct_md_array --- simple method for constructing an array object
2762 * with arbitrary dimensions and possible NULLs
2764 * elems: array of Datum items to become the array contents
2765 * nulls: array of is-null flags (can be NULL if no nulls)
2766 * ndims: number of dimensions
2767 * dims: integer array with size of each dimension
2768 * lbs: integer array with lower bound of each dimension
2769 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
2771 * A palloc'd ndims-D array object is constructed and returned. Note that
2772 * elem values will be copied into the object even if pass-by-ref type.
2774 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
2775 * from the system catalogs, given the elmtype. However, the caller is
2776 * in a better position to cache this info across multiple uses, or even
2777 * to hard-wire values if the element type is hard-wired.
2779 ArrayType *
2780 construct_md_array(Datum *elems,
2781 bool *nulls,
2782 int ndims,
2783 int *dims,
2784 int *lbs,
2785 Oid elmtype, int elmlen, bool elmbyval, char elmalign)
2787 ArrayType *result;
2788 bool hasnulls;
2789 int32 nbytes;
2790 int32 dataoffset;
2791 int i;
2792 int nelems;
2794 if (ndims < 0) /* we do allow zero-dimension arrays */
2795 ereport(ERROR,
2796 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2797 errmsg("invalid number of dimensions: %d", ndims)));
2798 if (ndims > MAXDIM)
2799 ereport(ERROR,
2800 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2801 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
2802 ndims, MAXDIM)));
2804 /* fast track for empty array */
2805 if (ndims == 0)
2806 return construct_empty_array(elmtype);
2808 nelems = ArrayGetNItems(ndims, dims);
2810 /* compute required space */
2811 nbytes = 0;
2812 hasnulls = false;
2813 for (i = 0; i < nelems; i++)
2815 if (nulls && nulls[i])
2817 hasnulls = true;
2818 continue;
2820 /* make sure data is not toasted */
2821 if (elmlen == -1)
2822 elems[i] = PointerGetDatum(PG_DETOAST_DATUM(elems[i]));
2823 nbytes = att_addlength_datum(nbytes, elmlen, elems[i]);
2824 nbytes = att_align_nominal(nbytes, elmalign);
2825 /* check for overflow of total request */
2826 if (!AllocSizeIsValid(nbytes))
2827 ereport(ERROR,
2828 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2829 errmsg("array size exceeds the maximum allowed (%d)",
2830 (int) MaxAllocSize)));
2833 /* Allocate and initialize result array */
2834 if (hasnulls)
2836 dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nelems);
2837 nbytes += dataoffset;
2839 else
2841 dataoffset = 0; /* marker for no null bitmap */
2842 nbytes += ARR_OVERHEAD_NONULLS(ndims);
2844 result = (ArrayType *) palloc(nbytes);
2845 SET_VARSIZE(result, nbytes);
2846 result->ndim = ndims;
2847 result->dataoffset = dataoffset;
2848 result->elemtype = elmtype;
2849 memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
2850 memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
2852 CopyArrayEls(result,
2853 elems, nulls, nelems,
2854 elmlen, elmbyval, elmalign,
2855 false);
2857 return result;
2861 * construct_empty_array --- make a zero-dimensional array of given type
2863 ArrayType *
2864 construct_empty_array(Oid elmtype)
2866 ArrayType *result;
2868 result = (ArrayType *) palloc(sizeof(ArrayType));
2869 SET_VARSIZE(result, sizeof(ArrayType));
2870 result->ndim = 0;
2871 result->dataoffset = 0;
2872 result->elemtype = elmtype;
2873 return result;
2877 * deconstruct_array --- simple method for extracting data from an array
2879 * array: array object to examine (must not be NULL)
2880 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
2881 * elemsp: return value, set to point to palloc'd array of Datum values
2882 * nullsp: return value, set to point to palloc'd array of isnull markers
2883 * nelemsp: return value, set to number of extracted values
2885 * The caller may pass nullsp == NULL if it does not support NULLs in the
2886 * array. Note that this produces a very uninformative error message,
2887 * so do it only in cases where a NULL is really not expected.
2889 * If array elements are pass-by-ref data type, the returned Datums will
2890 * be pointers into the array object.
2892 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
2893 * from the system catalogs, given the elmtype. However, in most current
2894 * uses the type is hard-wired into the caller and so we can save a lookup
2895 * cycle by hard-wiring the type info as well.
2897 void
2898 deconstruct_array(ArrayType *array,
2899 Oid elmtype,
2900 int elmlen, bool elmbyval, char elmalign,
2901 Datum **elemsp, bool **nullsp, int *nelemsp)
2903 Datum *elems;
2904 bool *nulls;
2905 int nelems;
2906 char *p;
2907 bits8 *bitmap;
2908 int bitmask;
2909 int i;
2911 Assert(ARR_ELEMTYPE(array) == elmtype);
2913 nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
2914 *elemsp = elems = (Datum *) palloc(nelems * sizeof(Datum));
2915 if (nullsp)
2916 *nullsp = nulls = (bool *) palloc(nelems * sizeof(bool));
2917 else
2918 nulls = NULL;
2919 *nelemsp = nelems;
2921 p = ARR_DATA_PTR(array);
2922 bitmap = ARR_NULLBITMAP(array);
2923 bitmask = 1;
2925 for (i = 0; i < nelems; i++)
2927 /* Get source element, checking for NULL */
2928 if (bitmap && (*bitmap & bitmask) == 0)
2930 elems[i] = (Datum) 0;
2931 if (nulls)
2932 nulls[i] = true;
2933 else
2934 ereport(ERROR,
2935 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
2936 errmsg("null array element not allowed in this context")));
2938 else
2940 elems[i] = fetch_att(p, elmbyval, elmlen);
2941 if (nulls)
2942 nulls[i] = false;
2943 p = att_addlength_pointer(p, elmlen, p);
2944 p = (char *) att_align_nominal(p, elmalign);
2947 /* advance bitmap pointer if any */
2948 if (bitmap)
2950 bitmask <<= 1;
2951 if (bitmask == 0x100)
2953 bitmap++;
2954 bitmask = 1;
2962 * array_eq :
2963 * compares two arrays for equality
2964 * result :
2965 * returns true if the arrays are equal, false otherwise.
2967 * Note: we do not use array_cmp here, since equality may be meaningful in
2968 * datatypes that don't have a total ordering (and hence no btree support).
2970 Datum
2971 array_eq(PG_FUNCTION_ARGS)
2973 ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
2974 ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
2975 int ndims1 = ARR_NDIM(array1);
2976 int ndims2 = ARR_NDIM(array2);
2977 int *dims1 = ARR_DIMS(array1);
2978 int *dims2 = ARR_DIMS(array2);
2979 Oid element_type = ARR_ELEMTYPE(array1);
2980 bool result = true;
2981 int nitems;
2982 TypeCacheEntry *typentry;
2983 int typlen;
2984 bool typbyval;
2985 char typalign;
2986 char *ptr1;
2987 char *ptr2;
2988 bits8 *bitmap1;
2989 bits8 *bitmap2;
2990 int bitmask;
2991 int i;
2992 FunctionCallInfoData locfcinfo;
2994 if (element_type != ARR_ELEMTYPE(array2))
2995 ereport(ERROR,
2996 (errcode(ERRCODE_DATATYPE_MISMATCH),
2997 errmsg("cannot compare arrays of different element types")));
2999 /* fast path if the arrays do not have the same dimensionality */
3000 if (ndims1 != ndims2 ||
3001 memcmp(dims1, dims2, 2 * ndims1 * sizeof(int)) != 0)
3002 result = false;
3003 else
3006 * We arrange to look up the equality function only once per series of
3007 * calls, assuming the element type doesn't change underneath us. The
3008 * typcache is used so that we have no memory leakage when being used
3009 * as an index support function.
3011 typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3012 if (typentry == NULL ||
3013 typentry->type_id != element_type)
3015 typentry = lookup_type_cache(element_type,
3016 TYPECACHE_EQ_OPR_FINFO);
3017 if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
3018 ereport(ERROR,
3019 (errcode(ERRCODE_UNDEFINED_FUNCTION),
3020 errmsg("could not identify an equality operator for type %s",
3021 format_type_be(element_type))));
3022 fcinfo->flinfo->fn_extra = (void *) typentry;
3024 typlen = typentry->typlen;
3025 typbyval = typentry->typbyval;
3026 typalign = typentry->typalign;
3029 * apply the operator to each pair of array elements.
3031 InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
3032 NULL, NULL);
3034 /* Loop over source data */
3035 nitems = ArrayGetNItems(ndims1, dims1);
3036 ptr1 = ARR_DATA_PTR(array1);
3037 ptr2 = ARR_DATA_PTR(array2);
3038 bitmap1 = ARR_NULLBITMAP(array1);
3039 bitmap2 = ARR_NULLBITMAP(array2);
3040 bitmask = 1; /* use same bitmask for both arrays */
3042 for (i = 0; i < nitems; i++)
3044 Datum elt1;
3045 Datum elt2;
3046 bool isnull1;
3047 bool isnull2;
3048 bool oprresult;
3050 /* Get elements, checking for NULL */
3051 if (bitmap1 && (*bitmap1 & bitmask) == 0)
3053 isnull1 = true;
3054 elt1 = (Datum) 0;
3056 else
3058 isnull1 = false;
3059 elt1 = fetch_att(ptr1, typbyval, typlen);
3060 ptr1 = att_addlength_pointer(ptr1, typlen, ptr1);
3061 ptr1 = (char *) att_align_nominal(ptr1, typalign);
3064 if (bitmap2 && (*bitmap2 & bitmask) == 0)
3066 isnull2 = true;
3067 elt2 = (Datum) 0;
3069 else
3071 isnull2 = false;
3072 elt2 = fetch_att(ptr2, typbyval, typlen);
3073 ptr2 = att_addlength_pointer(ptr2, typlen, ptr2);
3074 ptr2 = (char *) att_align_nominal(ptr2, typalign);
3077 /* advance bitmap pointers if any */
3078 bitmask <<= 1;
3079 if (bitmask == 0x100)
3081 if (bitmap1)
3082 bitmap1++;
3083 if (bitmap2)
3084 bitmap2++;
3085 bitmask = 1;
3089 * We consider two NULLs equal; NULL and not-NULL are unequal.
3091 if (isnull1 && isnull2)
3092 continue;
3093 if (isnull1 || isnull2)
3095 result = false;
3096 break;
3100 * Apply the operator to the element pair
3102 locfcinfo.arg[0] = elt1;
3103 locfcinfo.arg[1] = elt2;
3104 locfcinfo.argnull[0] = false;
3105 locfcinfo.argnull[1] = false;
3106 locfcinfo.isnull = false;
3107 oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
3108 if (!oprresult)
3110 result = false;
3111 break;
3116 /* Avoid leaking memory when handed toasted input. */
3117 PG_FREE_IF_COPY(array1, 0);
3118 PG_FREE_IF_COPY(array2, 1);
3120 PG_RETURN_BOOL(result);
3124 /*-----------------------------------------------------------------------------
3125 * array-array bool operators:
3126 * Given two arrays, iterate comparison operators
3127 * over the array. Uses logic similar to text comparison
3128 * functions, except element-by-element instead of
3129 * character-by-character.
3130 *----------------------------------------------------------------------------
3133 Datum
3134 array_ne(PG_FUNCTION_ARGS)
3136 PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo)));
3139 Datum
3140 array_lt(PG_FUNCTION_ARGS)
3142 PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
3145 Datum
3146 array_gt(PG_FUNCTION_ARGS)
3148 PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
3151 Datum
3152 array_le(PG_FUNCTION_ARGS)
3154 PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
3157 Datum
3158 array_ge(PG_FUNCTION_ARGS)
3160 PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
3163 Datum
3164 btarraycmp(PG_FUNCTION_ARGS)
3166 PG_RETURN_INT32(array_cmp(fcinfo));
3170 * array_cmp()
3171 * Internal comparison function for arrays.
3173 * Returns -1, 0 or 1
3175 static int
3176 array_cmp(FunctionCallInfo fcinfo)
3178 ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
3179 ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
3180 int ndims1 = ARR_NDIM(array1);
3181 int ndims2 = ARR_NDIM(array2);
3182 int *dims1 = ARR_DIMS(array1);
3183 int *dims2 = ARR_DIMS(array2);
3184 int nitems1 = ArrayGetNItems(ndims1, dims1);
3185 int nitems2 = ArrayGetNItems(ndims2, dims2);
3186 Oid element_type = ARR_ELEMTYPE(array1);
3187 int result = 0;
3188 TypeCacheEntry *typentry;
3189 int typlen;
3190 bool typbyval;
3191 char typalign;
3192 int min_nitems;
3193 char *ptr1;
3194 char *ptr2;
3195 bits8 *bitmap1;
3196 bits8 *bitmap2;
3197 int bitmask;
3198 int i;
3199 FunctionCallInfoData locfcinfo;
3201 if (element_type != ARR_ELEMTYPE(array2))
3202 ereport(ERROR,
3203 (errcode(ERRCODE_DATATYPE_MISMATCH),
3204 errmsg("cannot compare arrays of different element types")));
3207 * We arrange to look up the comparison function only once per series of
3208 * calls, assuming the element type doesn't change underneath us. The
3209 * typcache is used so that we have no memory leakage when being used as
3210 * an index support function.
3212 typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3213 if (typentry == NULL ||
3214 typentry->type_id != element_type)
3216 typentry = lookup_type_cache(element_type,
3217 TYPECACHE_CMP_PROC_FINFO);
3218 if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
3219 ereport(ERROR,
3220 (errcode(ERRCODE_UNDEFINED_FUNCTION),
3221 errmsg("could not identify a comparison function for type %s",
3222 format_type_be(element_type))));
3223 fcinfo->flinfo->fn_extra = (void *) typentry;
3225 typlen = typentry->typlen;
3226 typbyval = typentry->typbyval;
3227 typalign = typentry->typalign;
3230 * apply the operator to each pair of array elements.
3232 InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2,
3233 NULL, NULL);
3235 /* Loop over source data */
3236 min_nitems = Min(nitems1, nitems2);
3237 ptr1 = ARR_DATA_PTR(array1);
3238 ptr2 = ARR_DATA_PTR(array2);
3239 bitmap1 = ARR_NULLBITMAP(array1);
3240 bitmap2 = ARR_NULLBITMAP(array2);
3241 bitmask = 1; /* use same bitmask for both arrays */
3243 for (i = 0; i < min_nitems; i++)
3245 Datum elt1;
3246 Datum elt2;
3247 bool isnull1;
3248 bool isnull2;
3249 int32 cmpresult;
3251 /* Get elements, checking for NULL */
3252 if (bitmap1 && (*bitmap1 & bitmask) == 0)
3254 isnull1 = true;
3255 elt1 = (Datum) 0;
3257 else
3259 isnull1 = false;
3260 elt1 = fetch_att(ptr1, typbyval, typlen);
3261 ptr1 = att_addlength_pointer(ptr1, typlen, ptr1);
3262 ptr1 = (char *) att_align_nominal(ptr1, typalign);
3265 if (bitmap2 && (*bitmap2 & bitmask) == 0)
3267 isnull2 = true;
3268 elt2 = (Datum) 0;
3270 else
3272 isnull2 = false;
3273 elt2 = fetch_att(ptr2, typbyval, typlen);
3274 ptr2 = att_addlength_pointer(ptr2, typlen, ptr2);
3275 ptr2 = (char *) att_align_nominal(ptr2, typalign);
3278 /* advance bitmap pointers if any */
3279 bitmask <<= 1;
3280 if (bitmask == 0x100)
3282 if (bitmap1)
3283 bitmap1++;
3284 if (bitmap2)
3285 bitmap2++;
3286 bitmask = 1;
3290 * We consider two NULLs equal; NULL > not-NULL.
3292 if (isnull1 && isnull2)
3293 continue;
3294 if (isnull1)
3296 /* arg1 is greater than arg2 */
3297 result = 1;
3298 break;
3300 if (isnull2)
3302 /* arg1 is less than arg2 */
3303 result = -1;
3304 break;
3307 /* Compare the pair of elements */
3308 locfcinfo.arg[0] = elt1;
3309 locfcinfo.arg[1] = elt2;
3310 locfcinfo.argnull[0] = false;
3311 locfcinfo.argnull[1] = false;
3312 locfcinfo.isnull = false;
3313 cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
3315 if (cmpresult == 0)
3316 continue; /* equal */
3318 if (cmpresult < 0)
3320 /* arg1 is less than arg2 */
3321 result = -1;
3322 break;
3324 else
3326 /* arg1 is greater than arg2 */
3327 result = 1;
3328 break;
3333 * If arrays contain same data (up to end of shorter one), apply
3334 * additional rules to sort by dimensionality. The relative significance
3335 * of the different bits of information is historical; mainly we just care
3336 * that we don't say "equal" for arrays of different dimensionality.
3338 if (result == 0)
3340 if (nitems1 != nitems2)
3341 result = (nitems1 < nitems2) ? -1 : 1;
3342 else if (ndims1 != ndims2)
3343 result = (ndims1 < ndims2) ? -1 : 1;
3344 else
3346 /* this relies on LB array immediately following DIMS array */
3347 for (i = 0; i < ndims1 * 2; i++)
3349 if (dims1[i] != dims2[i])
3351 result = (dims1[i] < dims2[i]) ? -1 : 1;
3352 break;
3358 /* Avoid leaking memory when handed toasted input. */
3359 PG_FREE_IF_COPY(array1, 0);
3360 PG_FREE_IF_COPY(array2, 1);
3362 return result;
3366 /*-----------------------------------------------------------------------------
3367 * array overlap/containment comparisons
3368 * These use the same methods of comparing array elements as array_eq.
3369 * We consider only the elements of the arrays, ignoring dimensionality.
3370 *----------------------------------------------------------------------------
3374 * array_contain_compare :
3375 * compares two arrays for overlap/containment
3377 * When matchall is true, return true if all members of array1 are in array2.
3378 * When matchall is false, return true if any members of array1 are in array2.
3380 static bool
3381 array_contain_compare(ArrayType *array1, ArrayType *array2, bool matchall,
3382 void **fn_extra)
3384 bool result = matchall;
3385 Oid element_type = ARR_ELEMTYPE(array1);
3386 TypeCacheEntry *typentry;
3387 int nelems1;
3388 Datum *values2;
3389 bool *nulls2;
3390 int nelems2;
3391 int typlen;
3392 bool typbyval;
3393 char typalign;
3394 char *ptr1;
3395 bits8 *bitmap1;
3396 int bitmask;
3397 int i;
3398 int j;
3399 FunctionCallInfoData locfcinfo;
3401 if (element_type != ARR_ELEMTYPE(array2))
3402 ereport(ERROR,
3403 (errcode(ERRCODE_DATATYPE_MISMATCH),
3404 errmsg("cannot compare arrays of different element types")));
3407 * We arrange to look up the equality function only once per series of
3408 * calls, assuming the element type doesn't change underneath us. The
3409 * typcache is used so that we have no memory leakage when being used as
3410 * an index support function.
3412 typentry = (TypeCacheEntry *) *fn_extra;
3413 if (typentry == NULL ||
3414 typentry->type_id != element_type)
3416 typentry = lookup_type_cache(element_type,
3417 TYPECACHE_EQ_OPR_FINFO);
3418 if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
3419 ereport(ERROR,
3420 (errcode(ERRCODE_UNDEFINED_FUNCTION),
3421 errmsg("could not identify an equality operator for type %s",
3422 format_type_be(element_type))));
3423 *fn_extra = (void *) typentry;
3425 typlen = typentry->typlen;
3426 typbyval = typentry->typbyval;
3427 typalign = typentry->typalign;
3430 * Since we probably will need to scan array2 multiple times, it's
3431 * worthwhile to use deconstruct_array on it. We scan array1 the hard way
3432 * however, since we very likely won't need to look at all of it.
3434 deconstruct_array(array2, element_type, typlen, typbyval, typalign,
3435 &values2, &nulls2, &nelems2);
3438 * Apply the comparison operator to each pair of array elements.
3440 InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
3441 NULL, NULL);
3443 /* Loop over source data */
3444 nelems1 = ArrayGetNItems(ARR_NDIM(array1), ARR_DIMS(array1));
3445 ptr1 = ARR_DATA_PTR(array1);
3446 bitmap1 = ARR_NULLBITMAP(array1);
3447 bitmask = 1;
3449 for (i = 0; i < nelems1; i++)
3451 Datum elt1;
3452 bool isnull1;
3454 /* Get element, checking for NULL */
3455 if (bitmap1 && (*bitmap1 & bitmask) == 0)
3457 isnull1 = true;
3458 elt1 = (Datum) 0;
3460 else
3462 isnull1 = false;
3463 elt1 = fetch_att(ptr1, typbyval, typlen);
3464 ptr1 = att_addlength_pointer(ptr1, typlen, ptr1);
3465 ptr1 = (char *) att_align_nominal(ptr1, typalign);
3468 /* advance bitmap pointer if any */
3469 bitmask <<= 1;
3470 if (bitmask == 0x100)
3472 if (bitmap1)
3473 bitmap1++;
3474 bitmask = 1;
3478 * We assume that the comparison operator is strict, so a NULL can't
3479 * match anything. XXX this diverges from the "NULL=NULL" behavior of
3480 * array_eq, should we act like that?
3482 if (isnull1)
3484 if (matchall)
3486 result = false;
3487 break;
3489 continue;
3492 for (j = 0; j < nelems2; j++)
3494 Datum elt2 = values2[j];
3495 bool isnull2 = nulls2[j];
3496 bool oprresult;
3498 if (isnull2)
3499 continue; /* can't match */
3502 * Apply the operator to the element pair
3504 locfcinfo.arg[0] = elt1;
3505 locfcinfo.arg[1] = elt2;
3506 locfcinfo.argnull[0] = false;
3507 locfcinfo.argnull[1] = false;
3508 locfcinfo.isnull = false;
3509 oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
3510 if (oprresult)
3511 break;
3514 if (j < nelems2)
3516 /* found a match for elt1 */
3517 if (!matchall)
3519 result = true;
3520 break;
3523 else
3525 /* no match for elt1 */
3526 if (matchall)
3528 result = false;
3529 break;
3534 pfree(values2);
3535 pfree(nulls2);
3537 return result;
3540 Datum
3541 arrayoverlap(PG_FUNCTION_ARGS)
3543 ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
3544 ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
3545 bool result;
3547 result = array_contain_compare(array1, array2, false,
3548 &fcinfo->flinfo->fn_extra);
3550 /* Avoid leaking memory when handed toasted input. */
3551 PG_FREE_IF_COPY(array1, 0);
3552 PG_FREE_IF_COPY(array2, 1);
3554 PG_RETURN_BOOL(result);
3557 Datum
3558 arraycontains(PG_FUNCTION_ARGS)
3560 ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
3561 ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
3562 bool result;
3564 result = array_contain_compare(array2, array1, true,
3565 &fcinfo->flinfo->fn_extra);
3567 /* Avoid leaking memory when handed toasted input. */
3568 PG_FREE_IF_COPY(array1, 0);
3569 PG_FREE_IF_COPY(array2, 1);
3571 PG_RETURN_BOOL(result);
3574 Datum
3575 arraycontained(PG_FUNCTION_ARGS)
3577 ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
3578 ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
3579 bool result;
3581 result = array_contain_compare(array1, array2, true,
3582 &fcinfo->flinfo->fn_extra);
3584 /* Avoid leaking memory when handed toasted input. */
3585 PG_FREE_IF_COPY(array1, 0);
3586 PG_FREE_IF_COPY(array2, 1);
3588 PG_RETURN_BOOL(result);
3592 /***************************************************************************/
3593 /******************| Support Routines |*****************/
3594 /***************************************************************************/
3597 * Check whether a specific array element is NULL
3599 * nullbitmap: pointer to array's null bitmap (NULL if none)
3600 * offset: 0-based linear element number of array element
3602 static bool
3603 array_get_isnull(const bits8 *nullbitmap, int offset)
3605 if (nullbitmap == NULL)
3606 return false; /* assume not null */
3607 if (nullbitmap[offset / 8] & (1 << (offset % 8)))
3608 return false; /* not null */
3609 return true;
3613 * Set a specific array element's null-bitmap entry
3615 * nullbitmap: pointer to array's null bitmap (mustn't be NULL)
3616 * offset: 0-based linear element number of array element
3617 * isNull: null status to set
3619 static void
3620 array_set_isnull(bits8 *nullbitmap, int offset, bool isNull)
3622 int bitmask;
3624 nullbitmap += offset / 8;
3625 bitmask = 1 << (offset % 8);
3626 if (isNull)
3627 *nullbitmap &= ~bitmask;
3628 else
3629 *nullbitmap |= bitmask;
3633 * Fetch array element at pointer, converted correctly to a Datum
3635 * Caller must have handled case of NULL element
3637 static Datum
3638 ArrayCast(char *value, bool byval, int len)
3640 return fetch_att(value, byval, len);
3644 * Copy datum to *dest and return total space used (including align padding)
3646 * Caller must have handled case of NULL element
3648 static int
3649 ArrayCastAndSet(Datum src,
3650 int typlen,
3651 bool typbyval,
3652 char typalign,
3653 char *dest)
3655 int inc;
3657 if (typlen > 0)
3659 if (typbyval)
3660 store_att_byval(dest, src, typlen);
3661 else
3662 memmove(dest, DatumGetPointer(src), typlen);
3663 inc = att_align_nominal(typlen, typalign);
3665 else
3667 Assert(!typbyval);
3668 inc = att_addlength_datum(0, typlen, src);
3669 memmove(dest, DatumGetPointer(src), inc);
3670 inc = att_align_nominal(inc, typalign);
3673 return inc;
3677 * Advance ptr over nitems array elements
3679 * ptr: starting location in array
3680 * offset: 0-based linear element number of first element (the one at *ptr)
3681 * nullbitmap: start of array's null bitmap, or NULL if none
3682 * nitems: number of array elements to advance over (>= 0)
3683 * typlen, typbyval, typalign: storage parameters of array element datatype
3685 * It is caller's responsibility to ensure that nitems is within range
3687 static char *
3688 array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
3689 int typlen, bool typbyval, char typalign)
3691 int bitmask;
3692 int i;
3694 /* easy if fixed-size elements and no NULLs */
3695 if (typlen > 0 && !nullbitmap)
3696 return ptr + nitems * ((Size) att_align_nominal(typlen, typalign));
3698 /* seems worth having separate loops for NULL and no-NULLs cases */
3699 if (nullbitmap)
3701 nullbitmap += offset / 8;
3702 bitmask = 1 << (offset % 8);
3704 for (i = 0; i < nitems; i++)
3706 if (*nullbitmap & bitmask)
3708 ptr = att_addlength_pointer(ptr, typlen, ptr);
3709 ptr = (char *) att_align_nominal(ptr, typalign);
3711 bitmask <<= 1;
3712 if (bitmask == 0x100)
3714 nullbitmap++;
3715 bitmask = 1;
3719 else
3721 for (i = 0; i < nitems; i++)
3723 ptr = att_addlength_pointer(ptr, typlen, ptr);
3724 ptr = (char *) att_align_nominal(ptr, typalign);
3727 return ptr;
3731 * Compute total size of the nitems array elements starting at *ptr
3733 * Parameters same as for array_seek
3735 static int
3736 array_nelems_size(char *ptr, int offset, bits8 *nullbitmap, int nitems,
3737 int typlen, bool typbyval, char typalign)
3739 return array_seek(ptr, offset, nullbitmap, nitems,
3740 typlen, typbyval, typalign) - ptr;
3744 * Copy nitems array elements from srcptr to destptr
3746 * destptr: starting destination location (must be enough room!)
3747 * nitems: number of array elements to copy (>= 0)
3748 * srcptr: starting location in source array
3749 * offset: 0-based linear element number of first element (the one at *srcptr)
3750 * nullbitmap: start of source array's null bitmap, or NULL if none
3751 * typlen, typbyval, typalign: storage parameters of array element datatype
3753 * Returns number of bytes copied
3755 * NB: this does not take care of setting up the destination's null bitmap!
3757 static int
3758 array_copy(char *destptr, int nitems,
3759 char *srcptr, int offset, bits8 *nullbitmap,
3760 int typlen, bool typbyval, char typalign)
3762 int numbytes;
3764 numbytes = array_nelems_size(srcptr, offset, nullbitmap, nitems,
3765 typlen, typbyval, typalign);
3766 memcpy(destptr, srcptr, numbytes);
3767 return numbytes;
3771 * Copy nitems null-bitmap bits from source to destination
3773 * destbitmap: start of destination array's null bitmap (mustn't be NULL)
3774 * destoffset: 0-based linear element number of first dest element
3775 * srcbitmap: start of source array's null bitmap, or NULL if none
3776 * srcoffset: 0-based linear element number of first source element
3777 * nitems: number of bits to copy (>= 0)
3779 * If srcbitmap is NULL then we assume the source is all-non-NULL and
3780 * fill 1's into the destination bitmap. Note that only the specified
3781 * bits in the destination map are changed, not any before or after.
3783 * Note: this could certainly be optimized using standard bitblt methods.
3784 * However, it's not clear that the typical Postgres array has enough elements
3785 * to make it worth worrying too much. For the moment, KISS.
3787 void
3788 array_bitmap_copy(bits8 *destbitmap, int destoffset,
3789 const bits8 *srcbitmap, int srcoffset,
3790 int nitems)
3792 int destbitmask,
3793 destbitval,
3794 srcbitmask,
3795 srcbitval;
3797 Assert(destbitmap);
3798 if (nitems <= 0)
3799 return; /* don't risk fetch off end of memory */
3800 destbitmap += destoffset / 8;
3801 destbitmask = 1 << (destoffset % 8);
3802 destbitval = *destbitmap;
3803 if (srcbitmap)
3805 srcbitmap += srcoffset / 8;
3806 srcbitmask = 1 << (srcoffset % 8);
3807 srcbitval = *srcbitmap;
3808 while (nitems-- > 0)
3810 if (srcbitval & srcbitmask)
3811 destbitval |= destbitmask;
3812 else
3813 destbitval &= ~destbitmask;
3814 destbitmask <<= 1;
3815 if (destbitmask == 0x100)
3817 *destbitmap++ = destbitval;
3818 destbitmask = 1;
3819 if (nitems > 0)
3820 destbitval = *destbitmap;
3822 srcbitmask <<= 1;
3823 if (srcbitmask == 0x100)
3825 srcbitmap++;
3826 srcbitmask = 1;
3827 if (nitems > 0)
3828 srcbitval = *srcbitmap;
3831 if (destbitmask != 1)
3832 *destbitmap = destbitval;
3834 else
3836 while (nitems-- > 0)
3838 destbitval |= destbitmask;
3839 destbitmask <<= 1;
3840 if (destbitmask == 0x100)
3842 *destbitmap++ = destbitval;
3843 destbitmask = 1;
3844 if (nitems > 0)
3845 destbitval = *destbitmap;
3848 if (destbitmask != 1)
3849 *destbitmap = destbitval;
3854 * Compute space needed for a slice of an array
3856 * We assume the caller has verified that the slice coordinates are valid.
3858 static int
3859 array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
3860 int ndim, int *dim, int *lb,
3861 int *st, int *endp,
3862 int typlen, bool typbyval, char typalign)
3864 int src_offset,
3865 span[MAXDIM],
3866 prod[MAXDIM],
3867 dist[MAXDIM],
3868 indx[MAXDIM];
3869 char *ptr;
3870 int i,
3872 inc;
3873 int count = 0;
3875 mda_get_range(ndim, span, st, endp);
3877 /* Pretty easy for fixed element length without nulls ... */
3878 if (typlen > 0 && !arraynullsptr)
3879 return ArrayGetNItems(ndim, span) * att_align_nominal(typlen, typalign);
3881 /* Else gotta do it the hard way */
3882 src_offset = ArrayGetOffset(ndim, dim, lb, st);
3883 ptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
3884 typlen, typbyval, typalign);
3885 mda_get_prod(ndim, dim, prod);
3886 mda_get_offset_values(ndim, dist, prod, span);
3887 for (i = 0; i < ndim; i++)
3888 indx[i] = 0;
3889 j = ndim - 1;
3892 if (dist[j])
3894 ptr = array_seek(ptr, src_offset, arraynullsptr, dist[j],
3895 typlen, typbyval, typalign);
3896 src_offset += dist[j];
3898 if (!array_get_isnull(arraynullsptr, src_offset))
3900 inc = att_addlength_pointer(0, typlen, ptr);
3901 inc = att_align_nominal(inc, typalign);
3902 ptr += inc;
3903 count += inc;
3905 src_offset++;
3906 } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
3907 return count;
3911 * Extract a slice of an array into consecutive elements in the destination
3912 * array.
3914 * We assume the caller has verified that the slice coordinates are valid,
3915 * allocated enough storage for the result, and initialized the header
3916 * of the new array.
3918 static void
3919 array_extract_slice(ArrayType *newarray,
3920 int ndim,
3921 int *dim,
3922 int *lb,
3923 char *arraydataptr,
3924 bits8 *arraynullsptr,
3925 int *st,
3926 int *endp,
3927 int typlen,
3928 bool typbyval,
3929 char typalign)
3931 char *destdataptr = ARR_DATA_PTR(newarray);
3932 bits8 *destnullsptr = ARR_NULLBITMAP(newarray);
3933 char *srcdataptr;
3934 int src_offset,
3935 dest_offset,
3936 prod[MAXDIM],
3937 span[MAXDIM],
3938 dist[MAXDIM],
3939 indx[MAXDIM];
3940 int i,
3942 inc;
3944 src_offset = ArrayGetOffset(ndim, dim, lb, st);
3945 srcdataptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
3946 typlen, typbyval, typalign);
3947 mda_get_prod(ndim, dim, prod);
3948 mda_get_range(ndim, span, st, endp);
3949 mda_get_offset_values(ndim, dist, prod, span);
3950 for (i = 0; i < ndim; i++)
3951 indx[i] = 0;
3952 dest_offset = 0;
3953 j = ndim - 1;
3956 if (dist[j])
3958 /* skip unwanted elements */
3959 srcdataptr = array_seek(srcdataptr, src_offset, arraynullsptr,
3960 dist[j],
3961 typlen, typbyval, typalign);
3962 src_offset += dist[j];
3964 inc = array_copy(destdataptr, 1,
3965 srcdataptr, src_offset, arraynullsptr,
3966 typlen, typbyval, typalign);
3967 if (destnullsptr)
3968 array_bitmap_copy(destnullsptr, dest_offset,
3969 arraynullsptr, src_offset,
3971 destdataptr += inc;
3972 srcdataptr += inc;
3973 src_offset++;
3974 dest_offset++;
3975 } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
3979 * Insert a slice into an array.
3981 * ndim/dim[]/lb[] are dimensions of the original array. A new array with
3982 * those same dimensions is to be constructed. destArray must already
3983 * have been allocated and its header initialized.
3985 * st[]/endp[] identify the slice to be replaced. Elements within the slice
3986 * volume are taken from consecutive elements of the srcArray; elements
3987 * outside it are copied from origArray.
3989 * We assume the caller has verified that the slice coordinates are valid.
3991 static void
3992 array_insert_slice(ArrayType *destArray,
3993 ArrayType *origArray,
3994 ArrayType *srcArray,
3995 int ndim,
3996 int *dim,
3997 int *lb,
3998 int *st,
3999 int *endp,
4000 int typlen,
4001 bool typbyval,
4002 char typalign)
4004 char *destPtr = ARR_DATA_PTR(destArray);
4005 char *origPtr = ARR_DATA_PTR(origArray);
4006 char *srcPtr = ARR_DATA_PTR(srcArray);
4007 bits8 *destBitmap = ARR_NULLBITMAP(destArray);
4008 bits8 *origBitmap = ARR_NULLBITMAP(origArray);
4009 bits8 *srcBitmap = ARR_NULLBITMAP(srcArray);
4010 int orignitems = ArrayGetNItems(ARR_NDIM(origArray),
4011 ARR_DIMS(origArray));
4012 int dest_offset,
4013 orig_offset,
4014 src_offset,
4015 prod[MAXDIM],
4016 span[MAXDIM],
4017 dist[MAXDIM],
4018 indx[MAXDIM];
4019 int i,
4021 inc;
4023 dest_offset = ArrayGetOffset(ndim, dim, lb, st);
4024 /* copy items before the slice start */
4025 inc = array_copy(destPtr, dest_offset,
4026 origPtr, 0, origBitmap,
4027 typlen, typbyval, typalign);
4028 destPtr += inc;
4029 origPtr += inc;
4030 if (destBitmap)
4031 array_bitmap_copy(destBitmap, 0, origBitmap, 0, dest_offset);
4032 orig_offset = dest_offset;
4033 mda_get_prod(ndim, dim, prod);
4034 mda_get_range(ndim, span, st, endp);
4035 mda_get_offset_values(ndim, dist, prod, span);
4036 for (i = 0; i < ndim; i++)
4037 indx[i] = 0;
4038 src_offset = 0;
4039 j = ndim - 1;
4042 /* Copy/advance over elements between here and next part of slice */
4043 if (dist[j])
4045 inc = array_copy(destPtr, dist[j],
4046 origPtr, orig_offset, origBitmap,
4047 typlen, typbyval, typalign);
4048 destPtr += inc;
4049 origPtr += inc;
4050 if (destBitmap)
4051 array_bitmap_copy(destBitmap, dest_offset,
4052 origBitmap, orig_offset,
4053 dist[j]);
4054 dest_offset += dist[j];
4055 orig_offset += dist[j];
4057 /* Copy new element at this slice position */
4058 inc = array_copy(destPtr, 1,
4059 srcPtr, src_offset, srcBitmap,
4060 typlen, typbyval, typalign);
4061 if (destBitmap)
4062 array_bitmap_copy(destBitmap, dest_offset,
4063 srcBitmap, src_offset,
4065 destPtr += inc;
4066 srcPtr += inc;
4067 dest_offset++;
4068 src_offset++;
4069 /* Advance over old element at this slice position */
4070 origPtr = array_seek(origPtr, orig_offset, origBitmap, 1,
4071 typlen, typbyval, typalign);
4072 orig_offset++;
4073 } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
4075 /* don't miss any data at the end */
4076 array_copy(destPtr, orignitems - orig_offset,
4077 origPtr, orig_offset, origBitmap,
4078 typlen, typbyval, typalign);
4079 if (destBitmap)
4080 array_bitmap_copy(destBitmap, dest_offset,
4081 origBitmap, orig_offset,
4082 orignitems - orig_offset);
4086 * accumArrayResult - accumulate one (more) Datum for an array result
4088 * astate is working state (NULL on first call)
4089 * rcontext is where to keep working state
4091 ArrayBuildState *
4092 accumArrayResult(ArrayBuildState *astate,
4093 Datum dvalue, bool disnull,
4094 Oid element_type,
4095 MemoryContext rcontext)
4097 MemoryContext arr_context,
4098 oldcontext;
4100 if (astate == NULL)
4102 /* First time through --- initialize */
4104 /* Make a temporary context to hold all the junk */
4105 arr_context = AllocSetContextCreate(rcontext,
4106 "accumArrayResult",
4107 ALLOCSET_DEFAULT_MINSIZE,
4108 ALLOCSET_DEFAULT_INITSIZE,
4109 ALLOCSET_DEFAULT_MAXSIZE);
4110 oldcontext = MemoryContextSwitchTo(arr_context);
4111 astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
4112 astate->mcontext = arr_context;
4113 astate->alen = 64; /* arbitrary starting array size */
4114 astate->dvalues = (Datum *) palloc(astate->alen * sizeof(Datum));
4115 astate->dnulls = (bool *) palloc(astate->alen * sizeof(bool));
4116 astate->nelems = 0;
4117 astate->element_type = element_type;
4118 get_typlenbyvalalign(element_type,
4119 &astate->typlen,
4120 &astate->typbyval,
4121 &astate->typalign);
4123 else
4125 oldcontext = MemoryContextSwitchTo(astate->mcontext);
4126 Assert(astate->element_type == element_type);
4127 /* enlarge dvalues[]/dnulls[] if needed */
4128 if (astate->nelems >= astate->alen)
4130 astate->alen *= 2;
4131 astate->dvalues = (Datum *)
4132 repalloc(astate->dvalues, astate->alen * sizeof(Datum));
4133 astate->dnulls = (bool *)
4134 repalloc(astate->dnulls, astate->alen * sizeof(bool));
4138 /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
4139 if (!disnull && !astate->typbyval)
4140 dvalue = datumCopy(dvalue, astate->typbyval, astate->typlen);
4142 astate->dvalues[astate->nelems] = dvalue;
4143 astate->dnulls[astate->nelems] = disnull;
4144 astate->nelems++;
4146 MemoryContextSwitchTo(oldcontext);
4148 return astate;
4152 * makeArrayResult - produce 1-D final result of accumArrayResult
4154 * astate is working state (not NULL)
4155 * rcontext is where to construct result
4157 Datum
4158 makeArrayResult(ArrayBuildState *astate,
4159 MemoryContext rcontext)
4161 int dims[1];
4162 int lbs[1];
4164 dims[0] = astate->nelems;
4165 lbs[0] = 1;
4167 return makeMdArrayResult(astate, 1, dims, lbs, rcontext);
4171 * makeMdArrayResult - produce multi-D final result of accumArrayResult
4173 * beware: no check that specified dimensions match the number of values
4174 * accumulated.
4176 * astate is working state (not NULL)
4177 * rcontext is where to construct result
4179 Datum
4180 makeMdArrayResult(ArrayBuildState *astate,
4181 int ndims,
4182 int *dims,
4183 int *lbs,
4184 MemoryContext rcontext)
4186 ArrayType *result;
4187 MemoryContext oldcontext;
4189 /* Build the final array result in rcontext */
4190 oldcontext = MemoryContextSwitchTo(rcontext);
4192 result = construct_md_array(astate->dvalues,
4193 astate->dnulls,
4194 ndims,
4195 dims,
4196 lbs,
4197 astate->element_type,
4198 astate->typlen,
4199 astate->typbyval,
4200 astate->typalign);
4202 MemoryContextSwitchTo(oldcontext);
4204 /* Clean up all the junk */
4205 MemoryContextDelete(astate->mcontext);
4207 return PointerGetDatum(result);
4210 Datum
4211 array_larger(PG_FUNCTION_ARGS)
4213 ArrayType *v1,
4214 *v2,
4215 *result;
4217 v1 = PG_GETARG_ARRAYTYPE_P(0);
4218 v2 = PG_GETARG_ARRAYTYPE_P(1);
4220 result = ((array_cmp(fcinfo) > 0) ? v1 : v2);
4222 PG_RETURN_ARRAYTYPE_P(result);
4225 Datum
4226 array_smaller(PG_FUNCTION_ARGS)
4228 ArrayType *v1,
4229 *v2,
4230 *result;
4232 v1 = PG_GETARG_ARRAYTYPE_P(0);
4233 v2 = PG_GETARG_ARRAYTYPE_P(1);
4235 result = ((array_cmp(fcinfo) < 0) ? v1 : v2);
4237 PG_RETURN_ARRAYTYPE_P(result);
4241 typedef struct generate_subscripts_fctx
4243 int4 lower;
4244 int4 upper;
4245 bool reverse;
4246 } generate_subscripts_fctx;
4249 * generate_subscripts(array anyarray, dim int [, reverse bool])
4250 * Returns all subscripts of the array for any dimension
4252 Datum
4253 generate_subscripts(PG_FUNCTION_ARGS)
4255 FuncCallContext *funcctx;
4256 MemoryContext oldcontext;
4257 generate_subscripts_fctx *fctx;
4259 /* stuff done only on the first call of the function */
4260 if (SRF_IS_FIRSTCALL())
4262 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
4263 int reqdim = PG_GETARG_INT32(1);
4264 int *lb,
4265 *dimv;
4267 /* create a function context for cross-call persistence */
4268 funcctx = SRF_FIRSTCALL_INIT();
4270 /* Sanity check: does it look like an array at all? */
4271 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
4272 SRF_RETURN_DONE(funcctx);
4274 /* Sanity check: was the requested dim valid */
4275 if (reqdim <= 0 || reqdim > ARR_NDIM(v))
4276 SRF_RETURN_DONE(funcctx);
4279 * switch to memory context appropriate for multiple function calls
4281 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
4282 fctx = (generate_subscripts_fctx *) palloc(sizeof(generate_subscripts_fctx));
4284 lb = ARR_LBOUND(v);
4285 dimv = ARR_DIMS(v);
4287 fctx->lower = lb[reqdim - 1];
4288 fctx->upper = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
4289 fctx->reverse = (PG_NARGS() < 3) ? false : PG_GETARG_BOOL(2);
4291 funcctx->user_fctx = fctx;
4293 MemoryContextSwitchTo(oldcontext);
4296 funcctx = SRF_PERCALL_SETUP();
4298 fctx = funcctx->user_fctx;
4300 if (fctx->lower <= fctx->upper)
4302 if (!fctx->reverse)
4303 SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->lower++));
4304 else
4305 SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->upper--));
4307 else
4308 /* done when there are no more elements left */
4309 SRF_RETURN_DONE(funcctx);
4313 * generate_subscripts_nodir
4314 * Implements the 2-argument version of generate_subscripts
4316 Datum
4317 generate_subscripts_nodir(PG_FUNCTION_ARGS)
4319 /* just call the other one -- it can handle both cases */
4320 return generate_subscripts(fcinfo);
4324 * array_fill_with_lower_bounds
4325 * Create and fill array with defined lower bounds.
4327 Datum
4328 array_fill_with_lower_bounds(PG_FUNCTION_ARGS)
4330 ArrayType *dims;
4331 ArrayType *lbs;
4332 ArrayType *result;
4333 Oid elmtype;
4334 Datum value;
4335 bool isnull;
4337 if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
4338 ereport(ERROR,
4339 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
4340 errmsg("dimension array or low bound array cannot be NULL")));
4342 dims = PG_GETARG_ARRAYTYPE_P(1);
4343 lbs = PG_GETARG_ARRAYTYPE_P(2);
4345 if (!PG_ARGISNULL(0))
4347 value = PG_GETARG_DATUM(0);
4348 isnull = false;
4350 else
4352 value = 0;
4353 isnull = true;
4356 elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
4357 if (!OidIsValid(elmtype))
4358 elog(ERROR, "could not determine data type of input");
4360 result = array_fill_internal(dims, lbs, value, isnull, elmtype, fcinfo);
4361 PG_RETURN_ARRAYTYPE_P(result);
4365 * array_fill
4366 * Create and fill array with default lower bounds.
4368 Datum
4369 array_fill(PG_FUNCTION_ARGS)
4371 ArrayType *dims;
4372 ArrayType *result;
4373 Oid elmtype;
4374 Datum value;
4375 bool isnull;
4377 if (PG_ARGISNULL(1))
4378 ereport(ERROR,
4379 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
4380 errmsg("dimension array or low bound array cannot be NULL")));
4382 dims = PG_GETARG_ARRAYTYPE_P(1);
4384 if (!PG_ARGISNULL(0))
4386 value = PG_GETARG_DATUM(0);
4387 isnull = false;
4389 else
4391 value = 0;
4392 isnull = true;
4395 elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
4396 if (!OidIsValid(elmtype))
4397 elog(ERROR, "could not determine data type of input");
4399 result = array_fill_internal(dims, NULL, value, isnull, elmtype, fcinfo);
4400 PG_RETURN_ARRAYTYPE_P(result);
4403 static ArrayType *
4404 create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
4405 Oid elmtype, int dataoffset)
4407 ArrayType *result;
4409 result = (ArrayType *) palloc0(nbytes);
4410 SET_VARSIZE(result, nbytes);
4411 result->ndim = ndims;
4412 result->dataoffset = dataoffset;
4413 result->elemtype = elmtype;
4414 memcpy(ARR_DIMS(result), dimv, ndims * sizeof(int));
4415 memcpy(ARR_LBOUND(result), lbsv, ndims * sizeof(int));
4417 return result;
4420 static ArrayType *
4421 array_fill_internal(ArrayType *dims, ArrayType *lbs,
4422 Datum value, bool isnull, Oid elmtype,
4423 FunctionCallInfo fcinfo)
4425 ArrayType *result;
4426 int *dimv;
4427 int *lbsv;
4428 int ndims;
4429 int nitems;
4430 int deflbs[MAXDIM];
4431 int16 elmlen;
4432 bool elmbyval;
4433 char elmalign;
4434 ArrayMetaState *my_extra;
4437 * Params checks
4439 if (ARR_NDIM(dims) != 1)
4440 ereport(ERROR,
4441 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4442 errmsg("wrong number of array subscripts"),
4443 errdetail("Dimension array must be one dimensional.")));
4445 if (ARR_LBOUND(dims)[0] != 1)
4446 ereport(ERROR,
4447 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4448 errmsg("wrong range of array_subscripts"),
4449 errdetail("Lower bound of dimension array must be one.")));
4451 if (ARR_HASNULL(dims))
4452 ereport(ERROR,
4453 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
4454 errmsg("dimension values cannot be null")));
4456 dimv = (int *) ARR_DATA_PTR(dims);
4457 ndims = ARR_DIMS(dims)[0];
4459 if (ndims < 0) /* we do allow zero-dimension arrays */
4460 ereport(ERROR,
4461 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4462 errmsg("invalid number of dimensions: %d", ndims)));
4463 if (ndims > MAXDIM)
4464 ereport(ERROR,
4465 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
4466 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
4467 ndims, MAXDIM)));
4469 if (lbs != NULL)
4471 if (ARR_NDIM(lbs) != 1)
4472 ereport(ERROR,
4473 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4474 errmsg("wrong number of array subscripts"),
4475 errdetail("Dimension array must be one dimensional.")));
4477 if (ARR_LBOUND(lbs)[0] != 1)
4478 ereport(ERROR,
4479 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4480 errmsg("wrong range of array_subscripts"),
4481 errdetail("Lower bound of dimension array must be one.")));
4483 if (ARR_HASNULL(lbs))
4484 ereport(ERROR,
4485 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
4486 errmsg("dimension values cannot be null")));
4488 if (ARR_DIMS(lbs)[0] != ndims)
4489 ereport(ERROR,
4490 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4491 errmsg("wrong number of array_subscripts"),
4492 errdetail("Low bound array has different size than dimensions array.")));
4494 lbsv = (int *) ARR_DATA_PTR(lbs);
4496 else
4498 int i;
4500 for (i = 0; i < MAXDIM; i++)
4501 deflbs[i] = 1;
4503 lbsv = deflbs;
4506 /* fast track for empty array */
4507 if (ndims == 0)
4508 return construct_empty_array(elmtype);
4510 nitems = ArrayGetNItems(ndims, dimv);
4513 * We arrange to look up info about element type only once per series of
4514 * calls, assuming the element type doesn't change underneath us.
4516 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
4517 if (my_extra == NULL)
4519 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
4520 sizeof(ArrayMetaState));
4521 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
4522 my_extra->element_type = InvalidOid;
4525 if (my_extra->element_type != elmtype)
4527 /* Get info about element type */
4528 get_typlenbyvalalign(elmtype,
4529 &my_extra->typlen,
4530 &my_extra->typbyval,
4531 &my_extra->typalign);
4532 my_extra->element_type = elmtype;
4535 elmlen = my_extra->typlen;
4536 elmbyval = my_extra->typbyval;
4537 elmalign = my_extra->typalign;
4539 /* compute required space */
4540 if (!isnull)
4542 int i;
4543 char *p;
4544 int nbytes;
4545 int totbytes;
4547 /* make sure data is not toasted */
4548 if (elmlen == -1)
4549 value = PointerGetDatum(PG_DETOAST_DATUM(value));
4551 nbytes = att_addlength_datum(0, elmlen, value);
4552 nbytes = att_align_nominal(nbytes, elmalign);
4553 Assert(nbytes > 0);
4555 totbytes = nbytes * nitems;
4557 /* check for overflow of multiplication or total request */
4558 if (totbytes / nbytes != nitems ||
4559 !AllocSizeIsValid(totbytes))
4560 ereport(ERROR,
4561 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
4562 errmsg("array size exceeds the maximum allowed (%d)",
4563 (int) MaxAllocSize)));
4566 * This addition can't overflow, but it might cause us to go past
4567 * MaxAllocSize. We leave it to palloc to complain in that case.
4569 totbytes += ARR_OVERHEAD_NONULLS(ndims);
4571 result = create_array_envelope(ndims, dimv, lbsv, totbytes,
4572 elmtype, 0);
4574 p = ARR_DATA_PTR(result);
4575 for (i = 0; i < nitems; i++)
4576 p += ArrayCastAndSet(value, elmlen, elmbyval, elmalign, p);
4578 else
4580 int nbytes;
4581 int dataoffset;
4583 dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
4584 nbytes = dataoffset;
4586 result = create_array_envelope(ndims, dimv, lbsv, nbytes,
4587 elmtype, dataoffset);
4589 /* create_array_envelope already zeroed the bitmap, so we're done */
4592 return result;