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