1 /*-------------------------------------------------------------------------
2  *
3  * array_userfuncs.c
4  *	  Misc user-visible array support functions
5  *
6  * Copyright (c) 2003-2018, PostgreSQL Global Development Group
7  *
8  * IDENTIFICATION
9  *	  src/backend/utils/adt/array_userfuncs.c
10  *
11  *-------------------------------------------------------------------------
12  */
13 #include "postgres.h"
14 
15 #include "catalog/pg_type.h"
16 #include "common/int.h"
17 #include "utils/array.h"
18 #include "utils/builtins.h"
19 #include "utils/lsyscache.h"
20 #include "utils/typcache.h"
21 
22 
23 static Datum array_position_common(FunctionCallInfo fcinfo);
24 
25 
26 /*
27  * fetch_array_arg_replace_nulls
28  *
29  * Fetch an array-valued argument in expanded form; if it's null, construct an
30  * empty array value of the proper data type.  Also cache basic element type
31  * information in fn_extra.
32  *
33  * Caution: if the input is a read/write pointer, this returns the input
34  * argument; so callers must be sure that their changes are "safe", that is
35  * they cannot leave the array in a corrupt state.
36  *
37  * If we're being called as an aggregate function, make sure any newly-made
38  * expanded array is allocated in the aggregate state context, so as to save
39  * copying operations.
40  */
41 static ExpandedArrayHeader *
fetch_array_arg_replace_nulls(FunctionCallInfo fcinfo,int argno)42 fetch_array_arg_replace_nulls(FunctionCallInfo fcinfo, int argno)
43 {
44 	ExpandedArrayHeader *eah;
45 	Oid			element_type;
46 	ArrayMetaState *my_extra;
47 	MemoryContext resultcxt;
48 
49 	/* If first time through, create datatype cache struct */
50 	my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
51 	if (my_extra == NULL)
52 	{
53 		my_extra = (ArrayMetaState *)
54 			MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
55 							   sizeof(ArrayMetaState));
56 		my_extra->element_type = InvalidOid;
57 		fcinfo->flinfo->fn_extra = my_extra;
58 	}
59 
60 	/* Figure out which context we want the result in */
61 	if (!AggCheckCallContext(fcinfo, &resultcxt))
62 		resultcxt = CurrentMemoryContext;
63 
64 	/* Now collect the array value */
65 	if (!PG_ARGISNULL(argno))
66 	{
67 		MemoryContext oldcxt = MemoryContextSwitchTo(resultcxt);
68 
69 		eah = PG_GETARG_EXPANDED_ARRAYX(argno, my_extra);
70 		MemoryContextSwitchTo(oldcxt);
71 	}
72 	else
73 	{
74 		/* We have to look up the array type and element type */
75 		Oid			arr_typeid = get_fn_expr_argtype(fcinfo->flinfo, argno);
76 
77 		if (!OidIsValid(arr_typeid))
78 			ereport(ERROR,
79 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
80 					 errmsg("could not determine input data type")));
81 		element_type = get_element_type(arr_typeid);
82 		if (!OidIsValid(element_type))
83 			ereport(ERROR,
84 					(errcode(ERRCODE_DATATYPE_MISMATCH),
85 					 errmsg("input data type is not an array")));
86 
87 		eah = construct_empty_expanded_array(element_type,
88 											 resultcxt,
89 											 my_extra);
90 	}
91 
92 	return eah;
93 }
94 
95 /*-----------------------------------------------------------------------------
96  * array_append :
97  *		push an element onto the end of a one-dimensional array
98  *----------------------------------------------------------------------------
99  */
100 Datum
array_append(PG_FUNCTION_ARGS)101 array_append(PG_FUNCTION_ARGS)
102 {
103 	ExpandedArrayHeader *eah;
104 	Datum		newelem;
105 	bool		isNull;
106 	Datum		result;
107 	int		   *dimv,
108 			   *lb;
109 	int			indx;
110 	ArrayMetaState *my_extra;
111 
112 	eah = fetch_array_arg_replace_nulls(fcinfo, 0);
113 	isNull = PG_ARGISNULL(1);
114 	if (isNull)
115 		newelem = (Datum) 0;
116 	else
117 		newelem = PG_GETARG_DATUM(1);
118 
119 	if (eah->ndims == 1)
120 	{
121 		/* append newelem */
122 		lb = eah->lbound;
123 		dimv = eah->dims;
124 
125 		/* index of added elem is at lb[0] + (dimv[0] - 1) + 1 */
126 		if (pg_add_s32_overflow(lb[0], dimv[0], &indx))
127 			ereport(ERROR,
128 					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
129 					 errmsg("integer out of range")));
130 	}
131 	else if (eah->ndims == 0)
132 		indx = 1;
133 	else
134 		ereport(ERROR,
135 				(errcode(ERRCODE_DATA_EXCEPTION),
136 				 errmsg("argument must be empty or one-dimensional array")));
137 
138 	/* Perform element insertion */
139 	my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
140 
141 	result = array_set_element(EOHPGetRWDatum(&eah->hdr),
142 							   1, &indx, newelem, isNull,
143 							   -1, my_extra->typlen, my_extra->typbyval, my_extra->typalign);
144 
145 	PG_RETURN_DATUM(result);
146 }
147 
148 /*-----------------------------------------------------------------------------
149  * array_prepend :
150  *		push an element onto the front of a one-dimensional array
151  *----------------------------------------------------------------------------
152  */
153 Datum
array_prepend(PG_FUNCTION_ARGS)154 array_prepend(PG_FUNCTION_ARGS)
155 {
156 	ExpandedArrayHeader *eah;
157 	Datum		newelem;
158 	bool		isNull;
159 	Datum		result;
160 	int		   *lb;
161 	int			indx;
162 	int			lb0;
163 	ArrayMetaState *my_extra;
164 
165 	isNull = PG_ARGISNULL(0);
166 	if (isNull)
167 		newelem = (Datum) 0;
168 	else
169 		newelem = PG_GETARG_DATUM(0);
170 	eah = fetch_array_arg_replace_nulls(fcinfo, 1);
171 
172 	if (eah->ndims == 1)
173 	{
174 		/* prepend newelem */
175 		lb = eah->lbound;
176 		lb0 = lb[0];
177 
178 		if (pg_sub_s32_overflow(lb0, 1, &indx))
179 			ereport(ERROR,
180 					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
181 					 errmsg("integer out of range")));
182 	}
183 	else if (eah->ndims == 0)
184 	{
185 		indx = 1;
186 		lb0 = 1;
187 	}
188 	else
189 		ereport(ERROR,
190 				(errcode(ERRCODE_DATA_EXCEPTION),
191 				 errmsg("argument must be empty or one-dimensional array")));
192 
193 	/* Perform element insertion */
194 	my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
195 
196 	result = array_set_element(EOHPGetRWDatum(&eah->hdr),
197 							   1, &indx, newelem, isNull,
198 							   -1, my_extra->typlen, my_extra->typbyval, my_extra->typalign);
199 
200 	/* Readjust result's LB to match the input's, as expected for prepend */
201 	Assert(result == EOHPGetRWDatum(&eah->hdr));
202 	if (eah->ndims == 1)
203 	{
204 		/* This is ok whether we've deconstructed or not */
205 		eah->lbound[0] = lb0;
206 	}
207 
208 	PG_RETURN_DATUM(result);
209 }
210 
211 /*-----------------------------------------------------------------------------
212  * array_cat :
213  *		concatenate two nD arrays to form an nD array, or
214  *		push an (n-1)D array onto the end of an nD array
215  *----------------------------------------------------------------------------
216  */
217 Datum
array_cat(PG_FUNCTION_ARGS)218 array_cat(PG_FUNCTION_ARGS)
219 {
220 	ArrayType  *v1,
221 			   *v2;
222 	ArrayType  *result;
223 	int		   *dims,
224 			   *lbs,
225 				ndims,
226 				nitems,
227 				ndatabytes,
228 				nbytes;
229 	int		   *dims1,
230 			   *lbs1,
231 				ndims1,
232 				nitems1,
233 				ndatabytes1;
234 	int		   *dims2,
235 			   *lbs2,
236 				ndims2,
237 				nitems2,
238 				ndatabytes2;
239 	int			i;
240 	char	   *dat1,
241 			   *dat2;
242 	bits8	   *bitmap1,
243 			   *bitmap2;
244 	Oid			element_type;
245 	Oid			element_type1;
246 	Oid			element_type2;
247 	int32		dataoffset;
248 
249 	/* Concatenating a null array is a no-op, just return the other input */
250 	if (PG_ARGISNULL(0))
251 	{
252 		if (PG_ARGISNULL(1))
253 			PG_RETURN_NULL();
254 		result = PG_GETARG_ARRAYTYPE_P(1);
255 		PG_RETURN_ARRAYTYPE_P(result);
256 	}
257 	if (PG_ARGISNULL(1))
258 	{
259 		result = PG_GETARG_ARRAYTYPE_P(0);
260 		PG_RETURN_ARRAYTYPE_P(result);
261 	}
262 
263 	v1 = PG_GETARG_ARRAYTYPE_P(0);
264 	v2 = PG_GETARG_ARRAYTYPE_P(1);
265 
266 	element_type1 = ARR_ELEMTYPE(v1);
267 	element_type2 = ARR_ELEMTYPE(v2);
268 
269 	/* Check we have matching element types */
270 	if (element_type1 != element_type2)
271 		ereport(ERROR,
272 				(errcode(ERRCODE_DATATYPE_MISMATCH),
273 				 errmsg("cannot concatenate incompatible arrays"),
274 				 errdetail("Arrays with element types %s and %s are not "
275 						   "compatible for concatenation.",
276 						   format_type_be(element_type1),
277 						   format_type_be(element_type2))));
278 
279 	/* OK, use it */
280 	element_type = element_type1;
281 
282 	/*----------
283 	 * We must have one of the following combinations of inputs:
284 	 * 1) one empty array, and one non-empty array
285 	 * 2) both arrays empty
286 	 * 3) two arrays with ndims1 == ndims2
287 	 * 4) ndims1 == ndims2 - 1
288 	 * 5) ndims1 == ndims2 + 1
289 	 *----------
290 	 */
291 	ndims1 = ARR_NDIM(v1);
292 	ndims2 = ARR_NDIM(v2);
293 
294 	/*
295 	 * short circuit - if one input array is empty, and the other is not, we
296 	 * return the non-empty one as the result
297 	 *
298 	 * if both are empty, return the first one
299 	 */
300 	if (ndims1 == 0 && ndims2 > 0)
301 		PG_RETURN_ARRAYTYPE_P(v2);
302 
303 	if (ndims2 == 0)
304 		PG_RETURN_ARRAYTYPE_P(v1);
305 
306 	/* the rest fall under rule 3, 4, or 5 */
307 	if (ndims1 != ndims2 &&
308 		ndims1 != ndims2 - 1 &&
309 		ndims1 != ndims2 + 1)
310 		ereport(ERROR,
311 				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
312 				 errmsg("cannot concatenate incompatible arrays"),
313 				 errdetail("Arrays of %d and %d dimensions are not "
314 						   "compatible for concatenation.",
315 						   ndims1, ndims2)));
316 
317 	/* get argument array details */
318 	lbs1 = ARR_LBOUND(v1);
319 	lbs2 = ARR_LBOUND(v2);
320 	dims1 = ARR_DIMS(v1);
321 	dims2 = ARR_DIMS(v2);
322 	dat1 = ARR_DATA_PTR(v1);
323 	dat2 = ARR_DATA_PTR(v2);
324 	bitmap1 = ARR_NULLBITMAP(v1);
325 	bitmap2 = ARR_NULLBITMAP(v2);
326 	nitems1 = ArrayGetNItems(ndims1, dims1);
327 	nitems2 = ArrayGetNItems(ndims2, dims2);
328 	ndatabytes1 = ARR_SIZE(v1) - ARR_DATA_OFFSET(v1);
329 	ndatabytes2 = ARR_SIZE(v2) - ARR_DATA_OFFSET(v2);
330 
331 	if (ndims1 == ndims2)
332 	{
333 		/*
334 		 * resulting array is made up of the elements (possibly arrays
335 		 * themselves) of the input argument arrays
336 		 */
337 		ndims = ndims1;
338 		dims = (int *) palloc(ndims * sizeof(int));
339 		lbs = (int *) palloc(ndims * sizeof(int));
340 
341 		dims[0] = dims1[0] + dims2[0];
342 		lbs[0] = lbs1[0];
343 
344 		for (i = 1; i < ndims; i++)
345 		{
346 			if (dims1[i] != dims2[i] || lbs1[i] != lbs2[i])
347 				ereport(ERROR,
348 						(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
349 						 errmsg("cannot concatenate incompatible arrays"),
350 						 errdetail("Arrays with differing element dimensions are "
351 								   "not compatible for concatenation.")));
352 
353 			dims[i] = dims1[i];
354 			lbs[i] = lbs1[i];
355 		}
356 	}
357 	else if (ndims1 == ndims2 - 1)
358 	{
359 		/*
360 		 * resulting array has the second argument as the outer array, with
361 		 * the first argument inserted at the front of the outer dimension
362 		 */
363 		ndims = ndims2;
364 		dims = (int *) palloc(ndims * sizeof(int));
365 		lbs = (int *) palloc(ndims * sizeof(int));
366 		memcpy(dims, dims2, ndims * sizeof(int));
367 		memcpy(lbs, lbs2, ndims * sizeof(int));
368 
369 		/* increment number of elements in outer array */
370 		dims[0] += 1;
371 
372 		/* make sure the added element matches our existing elements */
373 		for (i = 0; i < ndims1; i++)
374 		{
375 			if (dims1[i] != dims[i + 1] || lbs1[i] != lbs[i + 1])
376 				ereport(ERROR,
377 						(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
378 						 errmsg("cannot concatenate incompatible arrays"),
379 						 errdetail("Arrays with differing dimensions are not "
380 								   "compatible for concatenation.")));
381 		}
382 	}
383 	else
384 	{
385 		/*
386 		 * (ndims1 == ndims2 + 1)
387 		 *
388 		 * resulting array has the first argument as the outer array, with the
389 		 * second argument appended to the end of the outer dimension
390 		 */
391 		ndims = ndims1;
392 		dims = (int *) palloc(ndims * sizeof(int));
393 		lbs = (int *) palloc(ndims * sizeof(int));
394 		memcpy(dims, dims1, ndims * sizeof(int));
395 		memcpy(lbs, lbs1, ndims * sizeof(int));
396 
397 		/* increment number of elements in outer array */
398 		dims[0] += 1;
399 
400 		/* make sure the added element matches our existing elements */
401 		for (i = 0; i < ndims2; i++)
402 		{
403 			if (dims2[i] != dims[i + 1] || lbs2[i] != lbs[i + 1])
404 				ereport(ERROR,
405 						(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
406 						 errmsg("cannot concatenate incompatible arrays"),
407 						 errdetail("Arrays with differing dimensions are not "
408 								   "compatible for concatenation.")));
409 		}
410 	}
411 
412 	/* Do this mainly for overflow checking */
413 	nitems = ArrayGetNItems(ndims, dims);
414 	ArrayCheckBounds(ndims, dims, lbs);
415 
416 	/* build the result array */
417 	ndatabytes = ndatabytes1 + ndatabytes2;
418 	if (ARR_HASNULL(v1) || ARR_HASNULL(v2))
419 	{
420 		dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
421 		nbytes = ndatabytes + dataoffset;
422 	}
423 	else
424 	{
425 		dataoffset = 0;			/* marker for no null bitmap */
426 		nbytes = ndatabytes + ARR_OVERHEAD_NONULLS(ndims);
427 	}
428 	result = (ArrayType *) palloc0(nbytes);
429 	SET_VARSIZE(result, nbytes);
430 	result->ndim = ndims;
431 	result->dataoffset = dataoffset;
432 	result->elemtype = element_type;
433 	memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
434 	memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
435 	/* data area is arg1 then arg2 */
436 	memcpy(ARR_DATA_PTR(result), dat1, ndatabytes1);
437 	memcpy(ARR_DATA_PTR(result) + ndatabytes1, dat2, ndatabytes2);
438 	/* handle the null bitmap if needed */
439 	if (ARR_HASNULL(result))
440 	{
441 		array_bitmap_copy(ARR_NULLBITMAP(result), 0,
442 						  bitmap1, 0,
443 						  nitems1);
444 		array_bitmap_copy(ARR_NULLBITMAP(result), nitems1,
445 						  bitmap2, 0,
446 						  nitems2);
447 	}
448 
449 	PG_RETURN_ARRAYTYPE_P(result);
450 }
451 
452 
453 /*
454  * ARRAY_AGG(anynonarray) aggregate function
455  */
456 Datum
array_agg_transfn(PG_FUNCTION_ARGS)457 array_agg_transfn(PG_FUNCTION_ARGS)
458 {
459 	Oid			arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
460 	MemoryContext aggcontext;
461 	ArrayBuildState *state;
462 	Datum		elem;
463 
464 	if (arg1_typeid == InvalidOid)
465 		ereport(ERROR,
466 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
467 				 errmsg("could not determine input data type")));
468 
469 	/*
470 	 * Note: we do not need a run-time check about whether arg1_typeid is a
471 	 * valid array element type, because the parser would have verified that
472 	 * while resolving the input/result types of this polymorphic aggregate.
473 	 */
474 
475 	if (!AggCheckCallContext(fcinfo, &aggcontext))
476 	{
477 		/* cannot be called directly because of internal-type argument */
478 		elog(ERROR, "array_agg_transfn called in non-aggregate context");
479 	}
480 
481 	if (PG_ARGISNULL(0))
482 		state = initArrayResult(arg1_typeid, aggcontext, false);
483 	else
484 		state = (ArrayBuildState *) PG_GETARG_POINTER(0);
485 
486 	elem = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1);
487 
488 	state = accumArrayResult(state,
489 							 elem,
490 							 PG_ARGISNULL(1),
491 							 arg1_typeid,
492 							 aggcontext);
493 
494 	/*
495 	 * The transition type for array_agg() is declared to be "internal", which
496 	 * is a pass-by-value type the same size as a pointer.  So we can safely
497 	 * pass the ArrayBuildState pointer through nodeAgg.c's machinations.
498 	 */
499 	PG_RETURN_POINTER(state);
500 }
501 
502 Datum
array_agg_finalfn(PG_FUNCTION_ARGS)503 array_agg_finalfn(PG_FUNCTION_ARGS)
504 {
505 	Datum		result;
506 	ArrayBuildState *state;
507 	int			dims[1];
508 	int			lbs[1];
509 
510 	/* cannot be called directly because of internal-type argument */
511 	Assert(AggCheckCallContext(fcinfo, NULL));
512 
513 	state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
514 
515 	if (state == NULL)
516 		PG_RETURN_NULL();		/* returns null iff no input values */
517 
518 	dims[0] = state->nelems;
519 	lbs[0] = 1;
520 
521 	/*
522 	 * Make the result.  We cannot release the ArrayBuildState because
523 	 * sometimes aggregate final functions are re-executed.  Rather, it is
524 	 * nodeAgg.c's responsibility to reset the aggcontext when it's safe to do
525 	 * so.
526 	 */
527 	result = makeMdArrayResult(state, 1, dims, lbs,
528 							   CurrentMemoryContext,
529 							   false);
530 
531 	PG_RETURN_DATUM(result);
532 }
533 
534 /*
535  * ARRAY_AGG(anyarray) aggregate function
536  */
537 Datum
array_agg_array_transfn(PG_FUNCTION_ARGS)538 array_agg_array_transfn(PG_FUNCTION_ARGS)
539 {
540 	Oid			arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
541 	MemoryContext aggcontext;
542 	ArrayBuildStateArr *state;
543 
544 	if (arg1_typeid == InvalidOid)
545 		ereport(ERROR,
546 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
547 				 errmsg("could not determine input data type")));
548 
549 	/*
550 	 * Note: we do not need a run-time check about whether arg1_typeid is a
551 	 * valid array type, because the parser would have verified that while
552 	 * resolving the input/result types of this polymorphic aggregate.
553 	 */
554 
555 	if (!AggCheckCallContext(fcinfo, &aggcontext))
556 	{
557 		/* cannot be called directly because of internal-type argument */
558 		elog(ERROR, "array_agg_array_transfn called in non-aggregate context");
559 	}
560 
561 
562 	if (PG_ARGISNULL(0))
563 		state = initArrayResultArr(arg1_typeid, InvalidOid, aggcontext, false);
564 	else
565 		state = (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
566 
567 	state = accumArrayResultArr(state,
568 								PG_GETARG_DATUM(1),
569 								PG_ARGISNULL(1),
570 								arg1_typeid,
571 								aggcontext);
572 
573 	/*
574 	 * The transition type for array_agg() is declared to be "internal", which
575 	 * is a pass-by-value type the same size as a pointer.  So we can safely
576 	 * pass the ArrayBuildStateArr pointer through nodeAgg.c's machinations.
577 	 */
578 	PG_RETURN_POINTER(state);
579 }
580 
581 Datum
array_agg_array_finalfn(PG_FUNCTION_ARGS)582 array_agg_array_finalfn(PG_FUNCTION_ARGS)
583 {
584 	Datum		result;
585 	ArrayBuildStateArr *state;
586 
587 	/* cannot be called directly because of internal-type argument */
588 	Assert(AggCheckCallContext(fcinfo, NULL));
589 
590 	state = PG_ARGISNULL(0) ? NULL : (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
591 
592 	if (state == NULL)
593 		PG_RETURN_NULL();		/* returns null iff no input values */
594 
595 	/*
596 	 * Make the result.  We cannot release the ArrayBuildStateArr because
597 	 * sometimes aggregate final functions are re-executed.  Rather, it is
598 	 * nodeAgg.c's responsibility to reset the aggcontext when it's safe to do
599 	 * so.
600 	 */
601 	result = makeArrayResultArr(state, CurrentMemoryContext, false);
602 
603 	PG_RETURN_DATUM(result);
604 }
605 
606 /*-----------------------------------------------------------------------------
607  * array_position, array_position_start :
608  *			return the offset of a value in an array.
609  *
610  * IS NOT DISTINCT FROM semantics are used for comparisons.  Return NULL when
611  * the value is not found.
612  *-----------------------------------------------------------------------------
613  */
614 Datum
array_position(PG_FUNCTION_ARGS)615 array_position(PG_FUNCTION_ARGS)
616 {
617 	return array_position_common(fcinfo);
618 }
619 
620 Datum
array_position_start(PG_FUNCTION_ARGS)621 array_position_start(PG_FUNCTION_ARGS)
622 {
623 	return array_position_common(fcinfo);
624 }
625 
626 /*
627  * array_position_common
628  *		Common code for array_position and array_position_start
629  *
630  * These are separate wrappers for the sake of opr_sanity regression test.
631  * They are not strict so we have to test for null inputs explicitly.
632  */
633 static Datum
array_position_common(FunctionCallInfo fcinfo)634 array_position_common(FunctionCallInfo fcinfo)
635 {
636 	ArrayType  *array;
637 	Oid			collation = PG_GET_COLLATION();
638 	Oid			element_type;
639 	Datum		searched_element,
640 				value;
641 	bool		isnull;
642 	int			position,
643 				position_min;
644 	bool		found = false;
645 	TypeCacheEntry *typentry;
646 	ArrayMetaState *my_extra;
647 	bool		null_search;
648 	ArrayIterator array_iterator;
649 
650 	if (PG_ARGISNULL(0))
651 		PG_RETURN_NULL();
652 
653 	array = PG_GETARG_ARRAYTYPE_P(0);
654 	element_type = ARR_ELEMTYPE(array);
655 
656 	/*
657 	 * We refuse to search for elements in multi-dimensional arrays, since we
658 	 * have no good way to report the element's location in the array.
659 	 */
660 	if (ARR_NDIM(array) > 1)
661 		ereport(ERROR,
662 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
663 				 errmsg("searching for elements in multidimensional arrays is not supported")));
664 
665 	if (PG_ARGISNULL(1))
666 	{
667 		/* fast return when the array doesn't have nulls */
668 		if (!array_contains_nulls(array))
669 			PG_RETURN_NULL();
670 		searched_element = (Datum) 0;
671 		null_search = true;
672 	}
673 	else
674 	{
675 		searched_element = PG_GETARG_DATUM(1);
676 		null_search = false;
677 	}
678 
679 	position = (ARR_LBOUND(array))[0] - 1;
680 
681 	/* figure out where to start */
682 	if (PG_NARGS() == 3)
683 	{
684 		if (PG_ARGISNULL(2))
685 			ereport(ERROR,
686 					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
687 					 errmsg("initial position must not be null")));
688 
689 		position_min = PG_GETARG_INT32(2);
690 	}
691 	else
692 		position_min = (ARR_LBOUND(array))[0];
693 
694 	/*
695 	 * We arrange to look up type info for array_create_iterator only once per
696 	 * series of calls, assuming the element type doesn't change underneath
697 	 * us.
698 	 */
699 	my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
700 	if (my_extra == NULL)
701 	{
702 		fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
703 													  sizeof(ArrayMetaState));
704 		my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
705 		my_extra->element_type = ~element_type;
706 	}
707 
708 	if (my_extra->element_type != element_type)
709 	{
710 		get_typlenbyvalalign(element_type,
711 							 &my_extra->typlen,
712 							 &my_extra->typbyval,
713 							 &my_extra->typalign);
714 
715 		typentry = lookup_type_cache(element_type, TYPECACHE_EQ_OPR_FINFO);
716 
717 		if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
718 			ereport(ERROR,
719 					(errcode(ERRCODE_UNDEFINED_FUNCTION),
720 					 errmsg("could not identify an equality operator for type %s",
721 							format_type_be(element_type))));
722 
723 		my_extra->element_type = element_type;
724 		fmgr_info_cxt(typentry->eq_opr_finfo.fn_oid, &my_extra->proc,
725 					  fcinfo->flinfo->fn_mcxt);
726 	}
727 
728 	/* Examine each array element until we find a match. */
729 	array_iterator = array_create_iterator(array, 0, my_extra);
730 	while (array_iterate(array_iterator, &value, &isnull))
731 	{
732 		position++;
733 
734 		/* skip initial elements if caller requested so */
735 		if (position < position_min)
736 			continue;
737 
738 		/*
739 		 * Can't look at the array element's value if it's null; but if we
740 		 * search for null, we have a hit and are done.
741 		 */
742 		if (isnull || null_search)
743 		{
744 			if (isnull && null_search)
745 			{
746 				found = true;
747 				break;
748 			}
749 			else
750 				continue;
751 		}
752 
753 		/* not nulls, so run the operator */
754 		if (DatumGetBool(FunctionCall2Coll(&my_extra->proc, collation,
755 										   searched_element, value)))
756 		{
757 			found = true;
758 			break;
759 		}
760 	}
761 
762 	array_free_iterator(array_iterator);
763 
764 	/* Avoid leaking memory when handed toasted input */
765 	PG_FREE_IF_COPY(array, 0);
766 
767 	if (!found)
768 		PG_RETURN_NULL();
769 
770 	PG_RETURN_INT32(position);
771 }
772 
773 /*-----------------------------------------------------------------------------
774  * array_positions :
775  *			return an array of positions of a value in an array.
776  *
777  * IS NOT DISTINCT FROM semantics are used for comparisons.  Returns NULL when
778  * the input array is NULL.  When the value is not found in the array, returns
779  * an empty array.
780  *
781  * This is not strict so we have to test for null inputs explicitly.
782  *-----------------------------------------------------------------------------
783  */
784 Datum
array_positions(PG_FUNCTION_ARGS)785 array_positions(PG_FUNCTION_ARGS)
786 {
787 	ArrayType  *array;
788 	Oid			collation = PG_GET_COLLATION();
789 	Oid			element_type;
790 	Datum		searched_element,
791 				value;
792 	bool		isnull;
793 	int			position;
794 	TypeCacheEntry *typentry;
795 	ArrayMetaState *my_extra;
796 	bool		null_search;
797 	ArrayIterator array_iterator;
798 	ArrayBuildState *astate = NULL;
799 
800 	if (PG_ARGISNULL(0))
801 		PG_RETURN_NULL();
802 
803 	array = PG_GETARG_ARRAYTYPE_P(0);
804 	element_type = ARR_ELEMTYPE(array);
805 
806 	position = (ARR_LBOUND(array))[0] - 1;
807 
808 	/*
809 	 * We refuse to search for elements in multi-dimensional arrays, since we
810 	 * have no good way to report the element's location in the array.
811 	 */
812 	if (ARR_NDIM(array) > 1)
813 		ereport(ERROR,
814 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
815 				 errmsg("searching for elements in multidimensional arrays is not supported")));
816 
817 	astate = initArrayResult(INT4OID, CurrentMemoryContext, false);
818 
819 	if (PG_ARGISNULL(1))
820 	{
821 		/* fast return when the array doesn't have nulls */
822 		if (!array_contains_nulls(array))
823 			PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
824 		searched_element = (Datum) 0;
825 		null_search = true;
826 	}
827 	else
828 	{
829 		searched_element = PG_GETARG_DATUM(1);
830 		null_search = false;
831 	}
832 
833 	/*
834 	 * We arrange to look up type info for array_create_iterator only once per
835 	 * series of calls, assuming the element type doesn't change underneath
836 	 * us.
837 	 */
838 	my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
839 	if (my_extra == NULL)
840 	{
841 		fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
842 													  sizeof(ArrayMetaState));
843 		my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
844 		my_extra->element_type = ~element_type;
845 	}
846 
847 	if (my_extra->element_type != element_type)
848 	{
849 		get_typlenbyvalalign(element_type,
850 							 &my_extra->typlen,
851 							 &my_extra->typbyval,
852 							 &my_extra->typalign);
853 
854 		typentry = lookup_type_cache(element_type, TYPECACHE_EQ_OPR_FINFO);
855 
856 		if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
857 			ereport(ERROR,
858 					(errcode(ERRCODE_UNDEFINED_FUNCTION),
859 					 errmsg("could not identify an equality operator for type %s",
860 							format_type_be(element_type))));
861 
862 		my_extra->element_type = element_type;
863 		fmgr_info_cxt(typentry->eq_opr_finfo.fn_oid, &my_extra->proc,
864 					  fcinfo->flinfo->fn_mcxt);
865 	}
866 
867 	/*
868 	 * Accumulate each array position iff the element matches the given
869 	 * element.
870 	 */
871 	array_iterator = array_create_iterator(array, 0, my_extra);
872 	while (array_iterate(array_iterator, &value, &isnull))
873 	{
874 		position += 1;
875 
876 		/*
877 		 * Can't look at the array element's value if it's null; but if we
878 		 * search for null, we have a hit.
879 		 */
880 		if (isnull || null_search)
881 		{
882 			if (isnull && null_search)
883 				astate =
884 					accumArrayResult(astate, Int32GetDatum(position), false,
885 									 INT4OID, CurrentMemoryContext);
886 
887 			continue;
888 		}
889 
890 		/* not nulls, so run the operator */
891 		if (DatumGetBool(FunctionCall2Coll(&my_extra->proc, collation,
892 										   searched_element, value)))
893 			astate =
894 				accumArrayResult(astate, Int32GetDatum(position), false,
895 								 INT4OID, CurrentMemoryContext);
896 	}
897 
898 	array_free_iterator(array_iterator);
899 
900 	/* Avoid leaking memory when handed toasted input */
901 	PG_FREE_IF_COPY(array, 0);
902 
903 	PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
904 }
905