1/*-------------------------------------------------------------------------
2 *
3 * array_userfuncs.c
4 * Misc user-visible array support functions
5 *
6 * Copyright (c) 2003-2019, 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
23static 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 */
41static ExpandedArrayHeader *
42fetch_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 */
100Datum
101array_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 */
153Datum
154array_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 */
217Datum
218array_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
415 /* build the result array */
416 ndatabytes = ndatabytes1 + ndatabytes2;
417 if (ARR_HASNULL(v1) || ARR_HASNULL(v2))
418 {
419 dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
420 nbytes = ndatabytes + dataoffset;
421 }
422 else
423 {
424 dataoffset = 0; /* marker for no null bitmap */
425 nbytes = ndatabytes + ARR_OVERHEAD_NONULLS(ndims);
426 }
427 result = (ArrayType *) palloc0(nbytes);
428 SET_VARSIZE(result, nbytes);
429 result->ndim = ndims;
430 result->dataoffset = dataoffset;
431 result->elemtype = element_type;
432 memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
433 memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
434 /* data area is arg1 then arg2 */
435 memcpy(ARR_DATA_PTR(result), dat1, ndatabytes1);
436 memcpy(ARR_DATA_PTR(result) + ndatabytes1, dat2, ndatabytes2);
437 /* handle the null bitmap if needed */
438 if (ARR_HASNULL(result))
439 {
440 array_bitmap_copy(ARR_NULLBITMAP(result), 0,
441 bitmap1, 0,
442 nitems1);
443 array_bitmap_copy(ARR_NULLBITMAP(result), nitems1,
444 bitmap2, 0,
445 nitems2);
446 }
447
448 PG_RETURN_ARRAYTYPE_P(result);
449}
450
451
452/*
453 * ARRAY_AGG(anynonarray) aggregate function
454 */
455Datum
456array_agg_transfn(PG_FUNCTION_ARGS)
457{
458 Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
459 MemoryContext aggcontext;
460 ArrayBuildState *state;
461 Datum elem;
462
463 if (arg1_typeid == InvalidOid)
464 ereport(ERROR,
465 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
466 errmsg("could not determine input data type")));
467
468 /*
469 * Note: we do not need a run-time check about whether arg1_typeid is a
470 * valid array element type, because the parser would have verified that
471 * while resolving the input/result types of this polymorphic aggregate.
472 */
473
474 if (!AggCheckCallContext(fcinfo, &aggcontext))
475 {
476 /* cannot be called directly because of internal-type argument */
477 elog(ERROR, "array_agg_transfn called in non-aggregate context");
478 }
479
480 if (PG_ARGISNULL(0))
481 state = initArrayResult(arg1_typeid, aggcontext, false);
482 else
483 state = (ArrayBuildState *) PG_GETARG_POINTER(0);
484
485 elem = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1);
486
487 state = accumArrayResult(state,
488 elem,
489 PG_ARGISNULL(1),
490 arg1_typeid,
491 aggcontext);
492
493 /*
494 * The transition type for array_agg() is declared to be "internal", which
495 * is a pass-by-value type the same size as a pointer. So we can safely
496 * pass the ArrayBuildState pointer through nodeAgg.c's machinations.
497 */
498 PG_RETURN_POINTER(state);
499}
500
501Datum
502array_agg_finalfn(PG_FUNCTION_ARGS)
503{
504 Datum result;
505 ArrayBuildState *state;
506 int dims[1];
507 int lbs[1];
508
509 /* cannot be called directly because of internal-type argument */
510 Assert(AggCheckCallContext(fcinfo, NULL));
511
512 state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
513
514 if (state == NULL)
515 PG_RETURN_NULL(); /* returns null iff no input values */
516
517 dims[0] = state->nelems;
518 lbs[0] = 1;
519
520 /*
521 * Make the result. We cannot release the ArrayBuildState because
522 * sometimes aggregate final functions are re-executed. Rather, it is
523 * nodeAgg.c's responsibility to reset the aggcontext when it's safe to do
524 * so.
525 */
526 result = makeMdArrayResult(state, 1, dims, lbs,
527 CurrentMemoryContext,
528 false);
529
530 PG_RETURN_DATUM(result);
531}
532
533/*
534 * ARRAY_AGG(anyarray) aggregate function
535 */
536Datum
537array_agg_array_transfn(PG_FUNCTION_ARGS)
538{
539 Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
540 MemoryContext aggcontext;
541 ArrayBuildStateArr *state;
542
543 if (arg1_typeid == InvalidOid)
544 ereport(ERROR,
545 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
546 errmsg("could not determine input data type")));
547
548 /*
549 * Note: we do not need a run-time check about whether arg1_typeid is a
550 * valid array type, because the parser would have verified that while
551 * resolving the input/result types of this polymorphic aggregate.
552 */
553
554 if (!AggCheckCallContext(fcinfo, &aggcontext))
555 {
556 /* cannot be called directly because of internal-type argument */
557 elog(ERROR, "array_agg_array_transfn called in non-aggregate context");
558 }
559
560
561 if (PG_ARGISNULL(0))
562 state = initArrayResultArr(arg1_typeid, InvalidOid, aggcontext, false);
563 else
564 state = (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
565
566 state = accumArrayResultArr(state,
567 PG_GETARG_DATUM(1),
568 PG_ARGISNULL(1),
569 arg1_typeid,
570 aggcontext);
571
572 /*
573 * The transition type for array_agg() is declared to be "internal", which
574 * is a pass-by-value type the same size as a pointer. So we can safely
575 * pass the ArrayBuildStateArr pointer through nodeAgg.c's machinations.
576 */
577 PG_RETURN_POINTER(state);
578}
579
580Datum
581array_agg_array_finalfn(PG_FUNCTION_ARGS)
582{
583 Datum result;
584 ArrayBuildStateArr *state;
585
586 /* cannot be called directly because of internal-type argument */
587 Assert(AggCheckCallContext(fcinfo, NULL));
588
589 state = PG_ARGISNULL(0) ? NULL : (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
590
591 if (state == NULL)
592 PG_RETURN_NULL(); /* returns null iff no input values */
593
594 /*
595 * Make the result. We cannot release the ArrayBuildStateArr because
596 * sometimes aggregate final functions are re-executed. Rather, it is
597 * nodeAgg.c's responsibility to reset the aggcontext when it's safe to do
598 * so.
599 */
600 result = makeArrayResultArr(state, CurrentMemoryContext, false);
601
602 PG_RETURN_DATUM(result);
603}
604
605/*-----------------------------------------------------------------------------
606 * array_position, array_position_start :
607 * return the offset of a value in an array.
608 *
609 * IS NOT DISTINCT FROM semantics are used for comparisons. Return NULL when
610 * the value is not found.
611 *-----------------------------------------------------------------------------
612 */
613Datum
614array_position(PG_FUNCTION_ARGS)
615{
616 return array_position_common(fcinfo);
617}
618
619Datum
620array_position_start(PG_FUNCTION_ARGS)
621{
622 return array_position_common(fcinfo);
623}
624
625/*
626 * array_position_common
627 * Common code for array_position and array_position_start
628 *
629 * These are separate wrappers for the sake of opr_sanity regression test.
630 * They are not strict so we have to test for null inputs explicitly.
631 */
632static Datum
633array_position_common(FunctionCallInfo fcinfo)
634{
635 ArrayType *array;
636 Oid collation = PG_GET_COLLATION();
637 Oid element_type;
638 Datum searched_element,
639 value;
640 bool isnull;
641 int position,
642 position_min;
643 bool found = false;
644 TypeCacheEntry *typentry;
645 ArrayMetaState *my_extra;
646 bool null_search;
647 ArrayIterator array_iterator;
648
649 if (PG_ARGISNULL(0))
650 PG_RETURN_NULL();
651
652 array = PG_GETARG_ARRAYTYPE_P(0);
653 element_type = ARR_ELEMTYPE(array);
654
655 /*
656 * We refuse to search for elements in multi-dimensional arrays, since we
657 * have no good way to report the element's location in the array.
658 */
659 if (ARR_NDIM(array) > 1)
660 ereport(ERROR,
661 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
662 errmsg("searching for elements in multidimensional arrays is not supported")));
663
664 if (PG_ARGISNULL(1))
665 {
666 /* fast return when the array doesn't have nulls */
667 if (!array_contains_nulls(array))
668 PG_RETURN_NULL();
669 searched_element = (Datum) 0;
670 null_search = true;
671 }
672 else
673 {
674 searched_element = PG_GETARG_DATUM(1);
675 null_search = false;
676 }
677
678 position = (ARR_LBOUND(array))[0] - 1;
679
680 /* figure out where to start */
681 if (PG_NARGS() == 3)
682 {
683 if (PG_ARGISNULL(2))
684 ereport(ERROR,
685 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
686 errmsg("initial position must not be null")));
687
688 position_min = PG_GETARG_INT32(2);
689 }
690 else
691 position_min = (ARR_LBOUND(array))[0];
692
693 /*
694 * We arrange to look up type info for array_create_iterator only once per
695 * series of calls, assuming the element type doesn't change underneath
696 * us.
697 */
698 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
699 if (my_extra == NULL)
700 {
701 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
702 sizeof(ArrayMetaState));
703 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
704 my_extra->element_type = ~element_type;
705 }
706
707 if (my_extra->element_type != element_type)
708 {
709 get_typlenbyvalalign(element_type,
710 &my_extra->typlen,
711 &my_extra->typbyval,
712 &my_extra->typalign);
713
714 typentry = lookup_type_cache(element_type, TYPECACHE_EQ_OPR_FINFO);
715
716 if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
717 ereport(ERROR,
718 (errcode(ERRCODE_UNDEFINED_FUNCTION),
719 errmsg("could not identify an equality operator for type %s",
720 format_type_be(element_type))));
721
722 my_extra->element_type = element_type;
723 fmgr_info_cxt(typentry->eq_opr_finfo.fn_oid, &my_extra->proc,
724 fcinfo->flinfo->fn_mcxt);
725 }
726
727 /* Examine each array element until we find a match. */
728 array_iterator = array_create_iterator(array, 0, my_extra);
729 while (array_iterate(array_iterator, &value, &isnull))
730 {
731 position++;
732
733 /* skip initial elements if caller requested so */
734 if (position < position_min)
735 continue;
736
737 /*
738 * Can't look at the array element's value if it's null; but if we
739 * search for null, we have a hit and are done.
740 */
741 if (isnull || null_search)
742 {
743 if (isnull && null_search)
744 {
745 found = true;
746 break;
747 }
748 else
749 continue;
750 }
751
752 /* not nulls, so run the operator */
753 if (DatumGetBool(FunctionCall2Coll(&my_extra->proc, collation,
754 searched_element, value)))
755 {
756 found = true;
757 break;
758 }
759 }
760
761 array_free_iterator(array_iterator);
762
763 /* Avoid leaking memory when handed toasted input */
764 PG_FREE_IF_COPY(array, 0);
765
766 if (!found)
767 PG_RETURN_NULL();
768
769 PG_RETURN_INT32(position);
770}
771
772/*-----------------------------------------------------------------------------
773 * array_positions :
774 * return an array of positions of a value in an array.
775 *
776 * IS NOT DISTINCT FROM semantics are used for comparisons. Returns NULL when
777 * the input array is NULL. When the value is not found in the array, returns
778 * an empty array.
779 *
780 * This is not strict so we have to test for null inputs explicitly.
781 *-----------------------------------------------------------------------------
782 */
783Datum
784array_positions(PG_FUNCTION_ARGS)
785{
786 ArrayType *array;
787 Oid collation = PG_GET_COLLATION();
788 Oid element_type;
789 Datum searched_element,
790 value;
791 bool isnull;
792 int position;
793 TypeCacheEntry *typentry;
794 ArrayMetaState *my_extra;
795 bool null_search;
796 ArrayIterator array_iterator;
797 ArrayBuildState *astate = NULL;
798
799 if (PG_ARGISNULL(0))
800 PG_RETURN_NULL();
801
802 array = PG_GETARG_ARRAYTYPE_P(0);
803 element_type = ARR_ELEMTYPE(array);
804
805 position = (ARR_LBOUND(array))[0] - 1;
806
807 /*
808 * We refuse to search for elements in multi-dimensional arrays, since we
809 * have no good way to report the element's location in the array.
810 */
811 if (ARR_NDIM(array) > 1)
812 ereport(ERROR,
813 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
814 errmsg("searching for elements in multidimensional arrays is not supported")));
815
816 astate = initArrayResult(INT4OID, CurrentMemoryContext, false);
817
818 if (PG_ARGISNULL(1))
819 {
820 /* fast return when the array doesn't have nulls */
821 if (!array_contains_nulls(array))
822 PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
823 searched_element = (Datum) 0;
824 null_search = true;
825 }
826 else
827 {
828 searched_element = PG_GETARG_DATUM(1);
829 null_search = false;
830 }
831
832 /*
833 * We arrange to look up type info for array_create_iterator only once per
834 * series of calls, assuming the element type doesn't change underneath
835 * us.
836 */
837 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
838 if (my_extra == NULL)
839 {
840 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
841 sizeof(ArrayMetaState));
842 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
843 my_extra->element_type = ~element_type;
844 }
845
846 if (my_extra->element_type != element_type)
847 {
848 get_typlenbyvalalign(element_type,
849 &my_extra->typlen,
850 &my_extra->typbyval,
851 &my_extra->typalign);
852
853 typentry = lookup_type_cache(element_type, TYPECACHE_EQ_OPR_FINFO);
854
855 if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
856 ereport(ERROR,
857 (errcode(ERRCODE_UNDEFINED_FUNCTION),
858 errmsg("could not identify an equality operator for type %s",
859 format_type_be(element_type))));
860
861 my_extra->element_type = element_type;
862 fmgr_info_cxt(typentry->eq_opr_finfo.fn_oid, &my_extra->proc,
863 fcinfo->flinfo->fn_mcxt);
864 }
865
866 /*
867 * Accumulate each array position iff the element matches the given
868 * element.
869 */
870 array_iterator = array_create_iterator(array, 0, my_extra);
871 while (array_iterate(array_iterator, &value, &isnull))
872 {
873 position += 1;
874
875 /*
876 * Can't look at the array element's value if it's null; but if we
877 * search for null, we have a hit.
878 */
879 if (isnull || null_search)
880 {
881 if (isnull && null_search)
882 astate =
883 accumArrayResult(astate, Int32GetDatum(position), false,
884 INT4OID, CurrentMemoryContext);
885
886 continue;
887 }
888
889 /* not nulls, so run the operator */
890 if (DatumGetBool(FunctionCall2Coll(&my_extra->proc, collation,
891 searched_element, value)))
892 astate =
893 accumArrayResult(astate, Int32GetDatum(position), false,
894 INT4OID, CurrentMemoryContext);
895 }
896
897 array_free_iterator(array_iterator);
898
899 /* Avoid leaking memory when handed toasted input */
900 PG_FREE_IF_COPY(array, 0);
901
902 PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
903}
904