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