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