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