Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * array_userfuncs.c
4 : * Misc user-visible array support functions
5 : *
6 : * Copyright (c) 2003-2024, 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 "common/pg_prng.h"
18 : #include "libpq/pqformat.h"
19 : #include "port/pg_bitutils.h"
20 : #include "utils/array.h"
21 : #include "utils/builtins.h"
22 : #include "utils/datum.h"
23 : #include "utils/lsyscache.h"
24 : #include "utils/typcache.h"
25 :
26 : /*
27 : * SerialIOData
28 : * Used for caching element-type data in array_agg_serialize
29 : */
30 : typedef struct SerialIOData
31 : {
32 : FmgrInfo typsend;
33 : } SerialIOData;
34 :
35 : /*
36 : * DeserialIOData
37 : * Used for caching element-type data in array_agg_deserialize
38 : */
39 : typedef struct DeserialIOData
40 : {
41 : FmgrInfo typreceive;
42 : Oid typioparam;
43 : } DeserialIOData;
44 :
45 : static Datum array_position_common(FunctionCallInfo fcinfo);
46 :
47 :
48 : /*
49 : * fetch_array_arg_replace_nulls
50 : *
51 : * Fetch an array-valued argument in expanded form; if it's null, construct an
52 : * empty array value of the proper data type. Also cache basic element type
53 : * information in fn_extra.
54 : *
55 : * Caution: if the input is a read/write pointer, this returns the input
56 : * argument; so callers must be sure that their changes are "safe", that is
57 : * they cannot leave the array in a corrupt state.
58 : *
59 : * If we're being called as an aggregate function, make sure any newly-made
60 : * expanded array is allocated in the aggregate state context, so as to save
61 : * copying operations.
62 : */
63 : static ExpandedArrayHeader *
64 1940 : fetch_array_arg_replace_nulls(FunctionCallInfo fcinfo, int argno)
65 : {
66 : ExpandedArrayHeader *eah;
67 : Oid element_type;
68 : ArrayMetaState *my_extra;
69 : MemoryContext resultcxt;
70 :
71 : /* If first time through, create datatype cache struct */
72 1940 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
73 1940 : if (my_extra == NULL)
74 : {
75 : my_extra = (ArrayMetaState *)
76 1206 : MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
77 : sizeof(ArrayMetaState));
78 1206 : my_extra->element_type = InvalidOid;
79 1206 : fcinfo->flinfo->fn_extra = my_extra;
80 : }
81 :
82 : /* Figure out which context we want the result in */
83 1940 : if (!AggCheckCallContext(fcinfo, &resultcxt))
84 1820 : resultcxt = CurrentMemoryContext;
85 :
86 : /* Now collect the array value */
87 1940 : if (!PG_ARGISNULL(argno))
88 : {
89 1916 : MemoryContext oldcxt = MemoryContextSwitchTo(resultcxt);
90 :
91 1916 : eah = PG_GETARG_EXPANDED_ARRAYX(argno, my_extra);
92 1916 : MemoryContextSwitchTo(oldcxt);
93 : }
94 : else
95 : {
96 : /* We have to look up the array type and element type */
97 24 : Oid arr_typeid = get_fn_expr_argtype(fcinfo->flinfo, argno);
98 :
99 24 : if (!OidIsValid(arr_typeid))
100 0 : ereport(ERROR,
101 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
102 : errmsg("could not determine input data type")));
103 24 : element_type = get_element_type(arr_typeid);
104 24 : if (!OidIsValid(element_type))
105 0 : ereport(ERROR,
106 : (errcode(ERRCODE_DATATYPE_MISMATCH),
107 : errmsg("input data type is not an array")));
108 :
109 24 : eah = construct_empty_expanded_array(element_type,
110 : resultcxt,
111 : my_extra);
112 : }
113 :
114 1940 : return eah;
115 : }
116 :
117 : /*-----------------------------------------------------------------------------
118 : * array_append :
119 : * push an element onto the end of a one-dimensional array
120 : *----------------------------------------------------------------------------
121 : */
122 : Datum
123 1862 : array_append(PG_FUNCTION_ARGS)
124 : {
125 : ExpandedArrayHeader *eah;
126 : Datum newelem;
127 : bool isNull;
128 : Datum result;
129 : int *dimv,
130 : *lb;
131 : int indx;
132 : ArrayMetaState *my_extra;
133 :
134 1862 : eah = fetch_array_arg_replace_nulls(fcinfo, 0);
135 1862 : isNull = PG_ARGISNULL(1);
136 1862 : if (isNull)
137 0 : newelem = (Datum) 0;
138 : else
139 1862 : newelem = PG_GETARG_DATUM(1);
140 :
141 1862 : if (eah->ndims == 1)
142 : {
143 : /* append newelem */
144 1446 : lb = eah->lbound;
145 1446 : dimv = eah->dims;
146 :
147 : /* index of added elem is at lb[0] + (dimv[0] - 1) + 1 */
148 1446 : if (pg_add_s32_overflow(lb[0], dimv[0], &indx))
149 0 : ereport(ERROR,
150 : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
151 : errmsg("integer out of range")));
152 : }
153 416 : else if (eah->ndims == 0)
154 416 : indx = 1;
155 : else
156 0 : ereport(ERROR,
157 : (errcode(ERRCODE_DATA_EXCEPTION),
158 : errmsg("argument must be empty or one-dimensional array")));
159 :
160 : /* Perform element insertion */
161 1862 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
162 :
163 1862 : result = array_set_element(EOHPGetRWDatum(&eah->hdr),
164 : 1, &indx, newelem, isNull,
165 1862 : -1, my_extra->typlen, my_extra->typbyval, my_extra->typalign);
166 :
167 1862 : PG_RETURN_DATUM(result);
168 : }
169 :
170 : /*-----------------------------------------------------------------------------
171 : * array_prepend :
172 : * push an element onto the front of a one-dimensional array
173 : *----------------------------------------------------------------------------
174 : */
175 : Datum
176 78 : array_prepend(PG_FUNCTION_ARGS)
177 : {
178 : ExpandedArrayHeader *eah;
179 : Datum newelem;
180 : bool isNull;
181 : Datum result;
182 : int *lb;
183 : int indx;
184 : int lb0;
185 : ArrayMetaState *my_extra;
186 :
187 78 : isNull = PG_ARGISNULL(0);
188 78 : if (isNull)
189 0 : newelem = (Datum) 0;
190 : else
191 78 : newelem = PG_GETARG_DATUM(0);
192 78 : eah = fetch_array_arg_replace_nulls(fcinfo, 1);
193 :
194 78 : if (eah->ndims == 1)
195 : {
196 : /* prepend newelem */
197 78 : lb = eah->lbound;
198 78 : lb0 = lb[0];
199 :
200 78 : if (pg_sub_s32_overflow(lb0, 1, &indx))
201 0 : ereport(ERROR,
202 : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
203 : errmsg("integer out of range")));
204 : }
205 0 : else if (eah->ndims == 0)
206 : {
207 0 : indx = 1;
208 0 : lb0 = 1;
209 : }
210 : else
211 0 : ereport(ERROR,
212 : (errcode(ERRCODE_DATA_EXCEPTION),
213 : errmsg("argument must be empty or one-dimensional array")));
214 :
215 : /* Perform element insertion */
216 78 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
217 :
218 78 : result = array_set_element(EOHPGetRWDatum(&eah->hdr),
219 : 1, &indx, newelem, isNull,
220 78 : -1, my_extra->typlen, my_extra->typbyval, my_extra->typalign);
221 :
222 : /* Readjust result's LB to match the input's, as expected for prepend */
223 : Assert(result == EOHPGetRWDatum(&eah->hdr));
224 78 : if (eah->ndims == 1)
225 : {
226 : /* This is ok whether we've deconstructed or not */
227 78 : eah->lbound[0] = lb0;
228 : }
229 :
230 78 : PG_RETURN_DATUM(result);
231 : }
232 :
233 : /*-----------------------------------------------------------------------------
234 : * array_cat :
235 : * concatenate two nD arrays to form an nD array, or
236 : * push an (n-1)D array onto the end of an nD array
237 : *----------------------------------------------------------------------------
238 : */
239 : Datum
240 29468 : array_cat(PG_FUNCTION_ARGS)
241 : {
242 : ArrayType *v1,
243 : *v2;
244 : ArrayType *result;
245 : int *dims,
246 : *lbs,
247 : ndims,
248 : nitems,
249 : ndatabytes,
250 : nbytes;
251 : int *dims1,
252 : *lbs1,
253 : ndims1,
254 : nitems1,
255 : ndatabytes1;
256 : int *dims2,
257 : *lbs2,
258 : ndims2,
259 : nitems2,
260 : ndatabytes2;
261 : int i;
262 : char *dat1,
263 : *dat2;
264 : bits8 *bitmap1,
265 : *bitmap2;
266 : Oid element_type;
267 : Oid element_type1;
268 : Oid element_type2;
269 : int32 dataoffset;
270 :
271 : /* Concatenating a null array is a no-op, just return the other input */
272 29468 : if (PG_ARGISNULL(0))
273 : {
274 1246 : if (PG_ARGISNULL(1))
275 0 : PG_RETURN_NULL();
276 1246 : result = PG_GETARG_ARRAYTYPE_P(1);
277 1246 : PG_RETURN_ARRAYTYPE_P(result);
278 : }
279 28222 : if (PG_ARGISNULL(1))
280 : {
281 0 : result = PG_GETARG_ARRAYTYPE_P(0);
282 0 : PG_RETURN_ARRAYTYPE_P(result);
283 : }
284 :
285 28222 : v1 = PG_GETARG_ARRAYTYPE_P(0);
286 28222 : v2 = PG_GETARG_ARRAYTYPE_P(1);
287 :
288 28222 : element_type1 = ARR_ELEMTYPE(v1);
289 28222 : element_type2 = ARR_ELEMTYPE(v2);
290 :
291 : /* Check we have matching element types */
292 28222 : if (element_type1 != element_type2)
293 0 : ereport(ERROR,
294 : (errcode(ERRCODE_DATATYPE_MISMATCH),
295 : errmsg("cannot concatenate incompatible arrays"),
296 : errdetail("Arrays with element types %s and %s are not "
297 : "compatible for concatenation.",
298 : format_type_be(element_type1),
299 : format_type_be(element_type2))));
300 :
301 : /* OK, use it */
302 28222 : element_type = element_type1;
303 :
304 : /*----------
305 : * We must have one of the following combinations of inputs:
306 : * 1) one empty array, and one non-empty array
307 : * 2) both arrays empty
308 : * 3) two arrays with ndims1 == ndims2
309 : * 4) ndims1 == ndims2 - 1
310 : * 5) ndims1 == ndims2 + 1
311 : *----------
312 : */
313 28222 : ndims1 = ARR_NDIM(v1);
314 28222 : ndims2 = ARR_NDIM(v2);
315 :
316 : /*
317 : * short circuit - if one input array is empty, and the other is not, we
318 : * return the non-empty one as the result
319 : *
320 : * if both are empty, return the first one
321 : */
322 28222 : if (ndims1 == 0 && ndims2 > 0)
323 0 : PG_RETURN_ARRAYTYPE_P(v2);
324 :
325 28222 : if (ndims2 == 0)
326 34 : PG_RETURN_ARRAYTYPE_P(v1);
327 :
328 : /* the rest fall under rule 3, 4, or 5 */
329 28188 : if (ndims1 != ndims2 &&
330 18 : ndims1 != ndims2 - 1 &&
331 12 : ndims1 != ndims2 + 1)
332 0 : ereport(ERROR,
333 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
334 : errmsg("cannot concatenate incompatible arrays"),
335 : errdetail("Arrays of %d and %d dimensions are not "
336 : "compatible for concatenation.",
337 : ndims1, ndims2)));
338 :
339 : /* get argument array details */
340 28188 : lbs1 = ARR_LBOUND(v1);
341 28188 : lbs2 = ARR_LBOUND(v2);
342 28188 : dims1 = ARR_DIMS(v1);
343 28188 : dims2 = ARR_DIMS(v2);
344 28188 : dat1 = ARR_DATA_PTR(v1);
345 28188 : dat2 = ARR_DATA_PTR(v2);
346 28188 : bitmap1 = ARR_NULLBITMAP(v1);
347 28188 : bitmap2 = ARR_NULLBITMAP(v2);
348 28188 : nitems1 = ArrayGetNItems(ndims1, dims1);
349 28188 : nitems2 = ArrayGetNItems(ndims2, dims2);
350 28188 : ndatabytes1 = ARR_SIZE(v1) - ARR_DATA_OFFSET(v1);
351 28188 : ndatabytes2 = ARR_SIZE(v2) - ARR_DATA_OFFSET(v2);
352 :
353 28188 : if (ndims1 == ndims2)
354 : {
355 : /*
356 : * resulting array is made up of the elements (possibly arrays
357 : * themselves) of the input argument arrays
358 : */
359 28170 : ndims = ndims1;
360 28170 : dims = (int *) palloc(ndims * sizeof(int));
361 28170 : lbs = (int *) palloc(ndims * sizeof(int));
362 :
363 28170 : dims[0] = dims1[0] + dims2[0];
364 28170 : lbs[0] = lbs1[0];
365 :
366 28182 : for (i = 1; i < ndims; i++)
367 : {
368 12 : if (dims1[i] != dims2[i] || lbs1[i] != lbs2[i])
369 0 : ereport(ERROR,
370 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
371 : errmsg("cannot concatenate incompatible arrays"),
372 : errdetail("Arrays with differing element dimensions are "
373 : "not compatible for concatenation.")));
374 :
375 12 : dims[i] = dims1[i];
376 12 : lbs[i] = lbs1[i];
377 : }
378 : }
379 18 : else if (ndims1 == ndims2 - 1)
380 : {
381 : /*
382 : * resulting array has the second argument as the outer array, with
383 : * the first argument inserted at the front of the outer dimension
384 : */
385 6 : ndims = ndims2;
386 6 : dims = (int *) palloc(ndims * sizeof(int));
387 6 : lbs = (int *) palloc(ndims * sizeof(int));
388 6 : memcpy(dims, dims2, ndims * sizeof(int));
389 6 : memcpy(lbs, lbs2, ndims * sizeof(int));
390 :
391 : /* increment number of elements in outer array */
392 6 : dims[0] += 1;
393 :
394 : /* make sure the added element matches our existing elements */
395 12 : for (i = 0; i < ndims1; i++)
396 : {
397 6 : if (dims1[i] != dims[i + 1] || lbs1[i] != lbs[i + 1])
398 0 : ereport(ERROR,
399 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
400 : errmsg("cannot concatenate incompatible arrays"),
401 : errdetail("Arrays with differing dimensions are not "
402 : "compatible for concatenation.")));
403 : }
404 : }
405 : else
406 : {
407 : /*
408 : * (ndims1 == ndims2 + 1)
409 : *
410 : * resulting array has the first argument as the outer array, with the
411 : * second argument appended to the end of the outer dimension
412 : */
413 12 : ndims = ndims1;
414 12 : dims = (int *) palloc(ndims * sizeof(int));
415 12 : lbs = (int *) palloc(ndims * sizeof(int));
416 12 : memcpy(dims, dims1, ndims * sizeof(int));
417 12 : memcpy(lbs, lbs1, ndims * sizeof(int));
418 :
419 : /* increment number of elements in outer array */
420 12 : dims[0] += 1;
421 :
422 : /* make sure the added element matches our existing elements */
423 24 : for (i = 0; i < ndims2; i++)
424 : {
425 12 : if (dims2[i] != dims[i + 1] || lbs2[i] != lbs[i + 1])
426 0 : ereport(ERROR,
427 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
428 : errmsg("cannot concatenate incompatible arrays"),
429 : errdetail("Arrays with differing dimensions are not "
430 : "compatible for concatenation.")));
431 : }
432 : }
433 :
434 : /* Do this mainly for overflow checking */
435 28188 : nitems = ArrayGetNItems(ndims, dims);
436 28188 : ArrayCheckBounds(ndims, dims, lbs);
437 :
438 : /* build the result array */
439 28188 : ndatabytes = ndatabytes1 + ndatabytes2;
440 28188 : if (ARR_HASNULL(v1) || ARR_HASNULL(v2))
441 : {
442 0 : dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
443 0 : nbytes = ndatabytes + dataoffset;
444 : }
445 : else
446 : {
447 28188 : dataoffset = 0; /* marker for no null bitmap */
448 28188 : nbytes = ndatabytes + ARR_OVERHEAD_NONULLS(ndims);
449 : }
450 28188 : result = (ArrayType *) palloc0(nbytes);
451 28188 : SET_VARSIZE(result, nbytes);
452 28188 : result->ndim = ndims;
453 28188 : result->dataoffset = dataoffset;
454 28188 : result->elemtype = element_type;
455 28188 : memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
456 28188 : memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
457 : /* data area is arg1 then arg2 */
458 28188 : memcpy(ARR_DATA_PTR(result), dat1, ndatabytes1);
459 28188 : memcpy(ARR_DATA_PTR(result) + ndatabytes1, dat2, ndatabytes2);
460 : /* handle the null bitmap if needed */
461 28188 : if (ARR_HASNULL(result))
462 : {
463 0 : array_bitmap_copy(ARR_NULLBITMAP(result), 0,
464 : bitmap1, 0,
465 : nitems1);
466 0 : array_bitmap_copy(ARR_NULLBITMAP(result), nitems1,
467 : bitmap2, 0,
468 : nitems2);
469 : }
470 :
471 28188 : PG_RETURN_ARRAYTYPE_P(result);
472 : }
473 :
474 :
475 : /*
476 : * ARRAY_AGG(anynonarray) aggregate function
477 : */
478 : Datum
479 1672440 : array_agg_transfn(PG_FUNCTION_ARGS)
480 : {
481 1672440 : Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
482 : MemoryContext aggcontext;
483 : ArrayBuildState *state;
484 : Datum elem;
485 :
486 1672440 : if (arg1_typeid == InvalidOid)
487 0 : ereport(ERROR,
488 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
489 : errmsg("could not determine input data type")));
490 :
491 : /*
492 : * Note: we do not need a run-time check about whether arg1_typeid is a
493 : * valid array element type, because the parser would have verified that
494 : * while resolving the input/result types of this polymorphic aggregate.
495 : */
496 :
497 1672440 : if (!AggCheckCallContext(fcinfo, &aggcontext))
498 : {
499 : /* cannot be called directly because of internal-type argument */
500 0 : elog(ERROR, "array_agg_transfn called in non-aggregate context");
501 : }
502 :
503 1672440 : if (PG_ARGISNULL(0))
504 75556 : state = initArrayResult(arg1_typeid, aggcontext, false);
505 : else
506 1596884 : state = (ArrayBuildState *) PG_GETARG_POINTER(0);
507 :
508 1672440 : elem = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1);
509 :
510 1672440 : state = accumArrayResult(state,
511 : elem,
512 1672440 : PG_ARGISNULL(1),
513 : arg1_typeid,
514 : aggcontext);
515 :
516 : /*
517 : * The transition type for array_agg() is declared to be "internal", which
518 : * is a pass-by-value type the same size as a pointer. So we can safely
519 : * pass the ArrayBuildState pointer through nodeAgg.c's machinations.
520 : */
521 1672440 : PG_RETURN_POINTER(state);
522 : }
523 :
524 : Datum
525 80 : array_agg_combine(PG_FUNCTION_ARGS)
526 : {
527 : ArrayBuildState *state1;
528 : ArrayBuildState *state2;
529 : MemoryContext agg_context;
530 : MemoryContext old_context;
531 :
532 80 : if (!AggCheckCallContext(fcinfo, &agg_context))
533 0 : elog(ERROR, "aggregate function called in non-aggregate context");
534 :
535 80 : state1 = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
536 80 : state2 = PG_ARGISNULL(1) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(1);
537 :
538 80 : if (state2 == NULL)
539 : {
540 : /*
541 : * NULL state2 is easy, just return state1, which we know is already
542 : * in the agg_context
543 : */
544 0 : if (state1 == NULL)
545 0 : PG_RETURN_NULL();
546 0 : PG_RETURN_POINTER(state1);
547 : }
548 :
549 80 : if (state1 == NULL)
550 : {
551 : /* We must copy state2's data into the agg_context */
552 60 : state1 = initArrayResultWithSize(state2->element_type, agg_context,
553 : false, state2->alen);
554 :
555 60 : old_context = MemoryContextSwitchTo(agg_context);
556 :
557 21868 : for (int i = 0; i < state2->nelems; i++)
558 : {
559 21808 : if (!state2->dnulls[i])
560 16354 : state1->dvalues[i] = datumCopy(state2->dvalues[i],
561 16354 : state1->typbyval,
562 16354 : state1->typlen);
563 : else
564 5454 : state1->dvalues[i] = (Datum) 0;
565 : }
566 :
567 60 : MemoryContextSwitchTo(old_context);
568 :
569 60 : memcpy(state1->dnulls, state2->dnulls, sizeof(bool) * state2->nelems);
570 :
571 60 : state1->nelems = state2->nelems;
572 :
573 60 : PG_RETURN_POINTER(state1);
574 : }
575 20 : else if (state2->nelems > 0)
576 : {
577 : /* We only need to combine the two states if state2 has any elements */
578 20 : int reqsize = state1->nelems + state2->nelems;
579 20 : MemoryContext oldContext = MemoryContextSwitchTo(state1->mcontext);
580 :
581 : Assert(state1->element_type == state2->element_type);
582 :
583 : /* Enlarge state1 arrays if needed */
584 20 : if (state1->alen < reqsize)
585 : {
586 : /* Use a power of 2 size rather than allocating just reqsize */
587 20 : state1->alen = pg_nextpower2_32(reqsize);
588 40 : state1->dvalues = (Datum *) repalloc(state1->dvalues,
589 20 : state1->alen * sizeof(Datum));
590 20 : state1->dnulls = (bool *) repalloc(state1->dnulls,
591 20 : state1->alen * sizeof(bool));
592 : }
593 :
594 : /* Copy in the state2 elements to the end of the state1 arrays */
595 8212 : for (int i = 0; i < state2->nelems; i++)
596 : {
597 8192 : if (!state2->dnulls[i])
598 6146 : state1->dvalues[i + state1->nelems] =
599 6146 : datumCopy(state2->dvalues[i],
600 6146 : state1->typbyval,
601 6146 : state1->typlen);
602 : else
603 2046 : state1->dvalues[i + state1->nelems] = (Datum) 0;
604 : }
605 :
606 20 : memcpy(&state1->dnulls[state1->nelems], state2->dnulls,
607 20 : sizeof(bool) * state2->nelems);
608 :
609 20 : state1->nelems = reqsize;
610 :
611 20 : MemoryContextSwitchTo(oldContext);
612 : }
613 :
614 20 : PG_RETURN_POINTER(state1);
615 : }
616 :
617 : /*
618 : * array_agg_serialize
619 : * Serialize ArrayBuildState into bytea.
620 : */
621 : Datum
622 80 : array_agg_serialize(PG_FUNCTION_ARGS)
623 : {
624 : ArrayBuildState *state;
625 : StringInfoData buf;
626 : bytea *result;
627 :
628 : /* cannot be called directly because of internal-type argument */
629 : Assert(AggCheckCallContext(fcinfo, NULL));
630 :
631 80 : state = (ArrayBuildState *) PG_GETARG_POINTER(0);
632 :
633 80 : pq_begintypsend(&buf);
634 :
635 : /*
636 : * element_type. Putting this first is more convenient in deserialization
637 : */
638 80 : pq_sendint32(&buf, state->element_type);
639 :
640 : /*
641 : * nelems -- send first so we know how large to make the dvalues and
642 : * dnulls array during deserialization.
643 : */
644 80 : pq_sendint64(&buf, state->nelems);
645 :
646 : /* alen can be decided during deserialization */
647 :
648 : /* typlen */
649 80 : pq_sendint16(&buf, state->typlen);
650 :
651 : /* typbyval */
652 80 : pq_sendbyte(&buf, state->typbyval);
653 :
654 : /* typalign */
655 80 : pq_sendbyte(&buf, state->typalign);
656 :
657 : /* dnulls */
658 80 : pq_sendbytes(&buf, state->dnulls, sizeof(bool) * state->nelems);
659 :
660 : /*
661 : * dvalues. By agreement with array_agg_deserialize, when the element
662 : * type is byval, we just transmit the Datum array as-is, including any
663 : * null elements. For by-ref types, we must invoke the element type's
664 : * send function, and we skip null elements (which is why the nulls flags
665 : * must be sent first).
666 : */
667 80 : if (state->typbyval)
668 80 : pq_sendbytes(&buf, state->dvalues, sizeof(Datum) * state->nelems);
669 : else
670 : {
671 : SerialIOData *iodata;
672 : int i;
673 :
674 : /* Avoid repeat catalog lookups for typsend function */
675 0 : iodata = (SerialIOData *) fcinfo->flinfo->fn_extra;
676 0 : if (iodata == NULL)
677 : {
678 : Oid typsend;
679 : bool typisvarlena;
680 :
681 : iodata = (SerialIOData *)
682 0 : MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
683 : sizeof(SerialIOData));
684 0 : getTypeBinaryOutputInfo(state->element_type, &typsend,
685 : &typisvarlena);
686 0 : fmgr_info_cxt(typsend, &iodata->typsend,
687 0 : fcinfo->flinfo->fn_mcxt);
688 0 : fcinfo->flinfo->fn_extra = (void *) iodata;
689 : }
690 :
691 0 : for (i = 0; i < state->nelems; i++)
692 : {
693 : bytea *outputbytes;
694 :
695 0 : if (state->dnulls[i])
696 0 : continue;
697 0 : outputbytes = SendFunctionCall(&iodata->typsend,
698 0 : state->dvalues[i]);
699 0 : pq_sendint32(&buf, VARSIZE(outputbytes) - VARHDRSZ);
700 0 : pq_sendbytes(&buf, VARDATA(outputbytes),
701 0 : VARSIZE(outputbytes) - VARHDRSZ);
702 : }
703 : }
704 :
705 80 : result = pq_endtypsend(&buf);
706 :
707 80 : PG_RETURN_BYTEA_P(result);
708 : }
709 :
710 : Datum
711 80 : array_agg_deserialize(PG_FUNCTION_ARGS)
712 : {
713 : bytea *sstate;
714 : ArrayBuildState *result;
715 : StringInfoData buf;
716 : Oid element_type;
717 : int64 nelems;
718 : const char *temp;
719 :
720 80 : if (!AggCheckCallContext(fcinfo, NULL))
721 0 : elog(ERROR, "aggregate function called in non-aggregate context");
722 :
723 80 : sstate = PG_GETARG_BYTEA_PP(0);
724 :
725 : /*
726 : * Initialize a StringInfo so that we can "receive" it using the standard
727 : * recv-function infrastructure.
728 : */
729 80 : initReadOnlyStringInfo(&buf, VARDATA_ANY(sstate),
730 80 : VARSIZE_ANY_EXHDR(sstate));
731 :
732 : /* element_type */
733 80 : element_type = pq_getmsgint(&buf, 4);
734 :
735 : /* nelems */
736 80 : nelems = pq_getmsgint64(&buf);
737 :
738 : /* Create output ArrayBuildState with the needed number of elements */
739 80 : result = initArrayResultWithSize(element_type, CurrentMemoryContext,
740 : false, nelems);
741 80 : result->nelems = nelems;
742 :
743 : /* typlen */
744 80 : result->typlen = pq_getmsgint(&buf, 2);
745 :
746 : /* typbyval */
747 80 : result->typbyval = pq_getmsgbyte(&buf);
748 :
749 : /* typalign */
750 80 : result->typalign = pq_getmsgbyte(&buf);
751 :
752 : /* dnulls */
753 80 : temp = pq_getmsgbytes(&buf, sizeof(bool) * nelems);
754 80 : memcpy(result->dnulls, temp, sizeof(bool) * nelems);
755 :
756 : /* dvalues --- see comment in array_agg_serialize */
757 80 : if (result->typbyval)
758 : {
759 80 : temp = pq_getmsgbytes(&buf, sizeof(Datum) * nelems);
760 80 : memcpy(result->dvalues, temp, sizeof(Datum) * nelems);
761 : }
762 : else
763 : {
764 : DeserialIOData *iodata;
765 :
766 : /* Avoid repeat catalog lookups for typreceive function */
767 0 : iodata = (DeserialIOData *) fcinfo->flinfo->fn_extra;
768 0 : if (iodata == NULL)
769 : {
770 : Oid typreceive;
771 :
772 : iodata = (DeserialIOData *)
773 0 : MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
774 : sizeof(DeserialIOData));
775 0 : getTypeBinaryInputInfo(element_type, &typreceive,
776 : &iodata->typioparam);
777 0 : fmgr_info_cxt(typreceive, &iodata->typreceive,
778 0 : fcinfo->flinfo->fn_mcxt);
779 0 : fcinfo->flinfo->fn_extra = (void *) iodata;
780 : }
781 :
782 0 : for (int i = 0; i < nelems; i++)
783 : {
784 : int itemlen;
785 : StringInfoData elem_buf;
786 :
787 0 : if (result->dnulls[i])
788 : {
789 0 : result->dvalues[i] = (Datum) 0;
790 0 : continue;
791 : }
792 :
793 0 : itemlen = pq_getmsgint(&buf, 4);
794 0 : if (itemlen < 0 || itemlen > (buf.len - buf.cursor))
795 0 : ereport(ERROR,
796 : (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
797 : errmsg("insufficient data left in message")));
798 :
799 : /*
800 : * Rather than copying data around, we just initialize a
801 : * StringInfo pointing to the correct portion of the message
802 : * buffer.
803 : */
804 0 : initReadOnlyStringInfo(&elem_buf, &buf.data[buf.cursor], itemlen);
805 :
806 0 : buf.cursor += itemlen;
807 :
808 : /* Now call the element's receiveproc */
809 0 : result->dvalues[i] = ReceiveFunctionCall(&iodata->typreceive,
810 : &elem_buf,
811 : iodata->typioparam,
812 : -1);
813 : }
814 : }
815 :
816 80 : pq_getmsgend(&buf);
817 :
818 80 : PG_RETURN_POINTER(result);
819 : }
820 :
821 : Datum
822 84804 : array_agg_finalfn(PG_FUNCTION_ARGS)
823 : {
824 : Datum result;
825 : ArrayBuildState *state;
826 : int dims[1];
827 : int lbs[1];
828 :
829 : /* cannot be called directly because of internal-type argument */
830 : Assert(AggCheckCallContext(fcinfo, NULL));
831 :
832 84804 : state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
833 :
834 84804 : if (state == NULL)
835 9246 : PG_RETURN_NULL(); /* returns null iff no input values */
836 :
837 75558 : dims[0] = state->nelems;
838 75558 : lbs[0] = 1;
839 :
840 : /*
841 : * Make the result. We cannot release the ArrayBuildState because
842 : * sometimes aggregate final functions are re-executed. Rather, it is
843 : * nodeAgg.c's responsibility to reset the aggcontext when it's safe to do
844 : * so.
845 : */
846 75558 : result = makeMdArrayResult(state, 1, dims, lbs,
847 : CurrentMemoryContext,
848 : false);
849 :
850 75558 : PG_RETURN_DATUM(result);
851 : }
852 :
853 : /*
854 : * ARRAY_AGG(anyarray) aggregate function
855 : */
856 : Datum
857 60222 : array_agg_array_transfn(PG_FUNCTION_ARGS)
858 : {
859 60222 : Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
860 : MemoryContext aggcontext;
861 : ArrayBuildStateArr *state;
862 :
863 60222 : if (arg1_typeid == InvalidOid)
864 0 : ereport(ERROR,
865 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
866 : errmsg("could not determine input data type")));
867 :
868 : /*
869 : * Note: we do not need a run-time check about whether arg1_typeid is a
870 : * valid array type, because the parser would have verified that while
871 : * resolving the input/result types of this polymorphic aggregate.
872 : */
873 :
874 60222 : if (!AggCheckCallContext(fcinfo, &aggcontext))
875 : {
876 : /* cannot be called directly because of internal-type argument */
877 0 : elog(ERROR, "array_agg_array_transfn called in non-aggregate context");
878 : }
879 :
880 :
881 60222 : if (PG_ARGISNULL(0))
882 224 : state = initArrayResultArr(arg1_typeid, InvalidOid, aggcontext, false);
883 : else
884 59998 : state = (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
885 :
886 60222 : state = accumArrayResultArr(state,
887 : PG_GETARG_DATUM(1),
888 60222 : PG_ARGISNULL(1),
889 : arg1_typeid,
890 : aggcontext);
891 :
892 : /*
893 : * The transition type for array_agg() is declared to be "internal", which
894 : * is a pass-by-value type the same size as a pointer. So we can safely
895 : * pass the ArrayBuildStateArr pointer through nodeAgg.c's machinations.
896 : */
897 60204 : PG_RETURN_POINTER(state);
898 : }
899 :
900 : Datum
901 80 : array_agg_array_combine(PG_FUNCTION_ARGS)
902 : {
903 : ArrayBuildStateArr *state1;
904 : ArrayBuildStateArr *state2;
905 : MemoryContext agg_context;
906 : MemoryContext old_context;
907 :
908 80 : if (!AggCheckCallContext(fcinfo, &agg_context))
909 0 : elog(ERROR, "aggregate function called in non-aggregate context");
910 :
911 80 : state1 = PG_ARGISNULL(0) ? NULL : (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
912 80 : state2 = PG_ARGISNULL(1) ? NULL : (ArrayBuildStateArr *) PG_GETARG_POINTER(1);
913 :
914 80 : if (state2 == NULL)
915 : {
916 : /*
917 : * NULL state2 is easy, just return state1, which we know is already
918 : * in the agg_context
919 : */
920 0 : if (state1 == NULL)
921 0 : PG_RETURN_NULL();
922 0 : PG_RETURN_POINTER(state1);
923 : }
924 :
925 80 : if (state1 == NULL)
926 : {
927 : /* We must copy state2's data into the agg_context */
928 60 : old_context = MemoryContextSwitchTo(agg_context);
929 :
930 60 : state1 = initArrayResultArr(state2->array_type, InvalidOid,
931 : agg_context, false);
932 :
933 60 : state1->abytes = state2->abytes;
934 60 : state1->data = (char *) palloc(state1->abytes);
935 :
936 60 : if (state2->nullbitmap)
937 : {
938 30 : int size = (state2->aitems + 7) / 8;
939 :
940 30 : state1->nullbitmap = (bits8 *) palloc(size);
941 30 : memcpy(state1->nullbitmap, state2->nullbitmap, size);
942 : }
943 :
944 60 : memcpy(state1->data, state2->data, state2->nbytes);
945 60 : state1->nbytes = state2->nbytes;
946 60 : state1->aitems = state2->aitems;
947 60 : state1->nitems = state2->nitems;
948 60 : state1->ndims = state2->ndims;
949 60 : memcpy(state1->dims, state2->dims, sizeof(state2->dims));
950 60 : memcpy(state1->lbs, state2->lbs, sizeof(state2->lbs));
951 60 : state1->array_type = state2->array_type;
952 60 : state1->element_type = state2->element_type;
953 :
954 60 : MemoryContextSwitchTo(old_context);
955 :
956 60 : PG_RETURN_POINTER(state1);
957 : }
958 :
959 : /* We only need to combine the two states if state2 has any items */
960 20 : else if (state2->nitems > 0)
961 : {
962 : MemoryContext oldContext;
963 20 : int reqsize = state1->nbytes + state2->nbytes;
964 : int i;
965 :
966 : /*
967 : * Check the states are compatible with each other. Ensure we use the
968 : * same error messages that are listed in accumArrayResultArr so that
969 : * the same error is shown as would have been if we'd not used the
970 : * combine function for the aggregation.
971 : */
972 20 : if (state1->ndims != state2->ndims)
973 0 : ereport(ERROR,
974 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
975 : errmsg("cannot accumulate arrays of different dimensionality")));
976 :
977 : /* Check dimensions match ignoring the first dimension. */
978 40 : for (i = 1; i < state1->ndims; i++)
979 : {
980 20 : if (state1->dims[i] != state2->dims[i] || state1->lbs[i] != state2->lbs[i])
981 0 : ereport(ERROR,
982 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
983 : errmsg("cannot accumulate arrays of different dimensionality")));
984 : }
985 :
986 :
987 20 : oldContext = MemoryContextSwitchTo(state1->mcontext);
988 :
989 : /*
990 : * If there's not enough space in state1 then we'll need to reallocate
991 : * more.
992 : */
993 20 : if (state1->abytes < reqsize)
994 : {
995 : /* use a power of 2 size rather than allocating just reqsize */
996 10 : state1->abytes = pg_nextpower2_32(reqsize);
997 10 : state1->data = (char *) repalloc(state1->data, state1->abytes);
998 : }
999 :
1000 20 : if (state2->nullbitmap)
1001 : {
1002 10 : int newnitems = state1->nitems + state2->nitems;
1003 :
1004 10 : if (state1->nullbitmap == NULL)
1005 : {
1006 : /*
1007 : * First input with nulls; we must retrospectively handle any
1008 : * previous inputs by marking all their items non-null.
1009 : */
1010 0 : state1->aitems = pg_nextpower2_32(Max(256, newnitems + 1));
1011 0 : state1->nullbitmap = (bits8 *) palloc((state1->aitems + 7) / 8);
1012 0 : array_bitmap_copy(state1->nullbitmap, 0,
1013 : NULL, 0,
1014 : state1->nitems);
1015 : }
1016 10 : else if (newnitems > state1->aitems)
1017 : {
1018 10 : int newaitems = state1->aitems + state2->aitems;
1019 :
1020 10 : state1->aitems = pg_nextpower2_32(newaitems);
1021 10 : state1->nullbitmap = (bits8 *)
1022 10 : repalloc(state1->nullbitmap, (state1->aitems + 7) / 8);
1023 : }
1024 10 : array_bitmap_copy(state1->nullbitmap, state1->nitems,
1025 10 : state2->nullbitmap, 0,
1026 : state2->nitems);
1027 : }
1028 :
1029 20 : memcpy(state1->data + state1->nbytes, state2->data, state2->nbytes);
1030 20 : state1->nbytes += state2->nbytes;
1031 20 : state1->nitems += state2->nitems;
1032 :
1033 20 : state1->dims[0] += state2->dims[0];
1034 : /* remaining dims already match, per test above */
1035 :
1036 : Assert(state1->array_type == state2->array_type);
1037 : Assert(state1->element_type == state2->element_type);
1038 :
1039 20 : MemoryContextSwitchTo(oldContext);
1040 : }
1041 :
1042 20 : PG_RETURN_POINTER(state1);
1043 : }
1044 :
1045 : /*
1046 : * array_agg_array_serialize
1047 : * Serialize ArrayBuildStateArr into bytea.
1048 : */
1049 : Datum
1050 80 : array_agg_array_serialize(PG_FUNCTION_ARGS)
1051 : {
1052 : ArrayBuildStateArr *state;
1053 : StringInfoData buf;
1054 : bytea *result;
1055 :
1056 : /* cannot be called directly because of internal-type argument */
1057 : Assert(AggCheckCallContext(fcinfo, NULL));
1058 :
1059 80 : state = (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
1060 :
1061 80 : pq_begintypsend(&buf);
1062 :
1063 : /*
1064 : * element_type. Putting this first is more convenient in deserialization
1065 : * so that we can init the new state sooner.
1066 : */
1067 80 : pq_sendint32(&buf, state->element_type);
1068 :
1069 : /* array_type */
1070 80 : pq_sendint32(&buf, state->array_type);
1071 :
1072 : /* nbytes */
1073 80 : pq_sendint32(&buf, state->nbytes);
1074 :
1075 : /* data */
1076 80 : pq_sendbytes(&buf, state->data, state->nbytes);
1077 :
1078 : /* abytes */
1079 80 : pq_sendint32(&buf, state->abytes);
1080 :
1081 : /* aitems */
1082 80 : pq_sendint32(&buf, state->aitems);
1083 :
1084 : /* nullbitmap */
1085 80 : if (state->nullbitmap)
1086 : {
1087 : Assert(state->aitems > 0);
1088 40 : pq_sendbytes(&buf, state->nullbitmap, (state->aitems + 7) / 8);
1089 : }
1090 :
1091 : /* nitems */
1092 80 : pq_sendint32(&buf, state->nitems);
1093 :
1094 : /* ndims */
1095 80 : pq_sendint32(&buf, state->ndims);
1096 :
1097 : /* dims: XXX should we just send ndims elements? */
1098 80 : pq_sendbytes(&buf, state->dims, sizeof(state->dims));
1099 :
1100 : /* lbs */
1101 80 : pq_sendbytes(&buf, state->lbs, sizeof(state->lbs));
1102 :
1103 80 : result = pq_endtypsend(&buf);
1104 :
1105 80 : PG_RETURN_BYTEA_P(result);
1106 : }
1107 :
1108 : Datum
1109 80 : array_agg_array_deserialize(PG_FUNCTION_ARGS)
1110 : {
1111 : bytea *sstate;
1112 : ArrayBuildStateArr *result;
1113 : StringInfoData buf;
1114 : Oid element_type;
1115 : Oid array_type;
1116 : int nbytes;
1117 : const char *temp;
1118 :
1119 : /* cannot be called directly because of internal-type argument */
1120 : Assert(AggCheckCallContext(fcinfo, NULL));
1121 :
1122 80 : sstate = PG_GETARG_BYTEA_PP(0);
1123 :
1124 : /*
1125 : * Initialize a StringInfo so that we can "receive" it using the standard
1126 : * recv-function infrastructure.
1127 : */
1128 80 : initReadOnlyStringInfo(&buf, VARDATA_ANY(sstate),
1129 80 : VARSIZE_ANY_EXHDR(sstate));
1130 :
1131 : /* element_type */
1132 80 : element_type = pq_getmsgint(&buf, 4);
1133 :
1134 : /* array_type */
1135 80 : array_type = pq_getmsgint(&buf, 4);
1136 :
1137 : /* nbytes */
1138 80 : nbytes = pq_getmsgint(&buf, 4);
1139 :
1140 80 : result = initArrayResultArr(array_type, element_type,
1141 : CurrentMemoryContext, false);
1142 :
1143 80 : result->abytes = 1024;
1144 110 : while (result->abytes < nbytes)
1145 30 : result->abytes *= 2;
1146 :
1147 80 : result->data = (char *) palloc(result->abytes);
1148 :
1149 : /* data */
1150 80 : temp = pq_getmsgbytes(&buf, nbytes);
1151 80 : memcpy(result->data, temp, nbytes);
1152 80 : result->nbytes = nbytes;
1153 :
1154 : /* abytes */
1155 80 : result->abytes = pq_getmsgint(&buf, 4);
1156 :
1157 : /* aitems: might be 0 */
1158 80 : result->aitems = pq_getmsgint(&buf, 4);
1159 :
1160 : /* nullbitmap */
1161 80 : if (result->aitems > 0)
1162 : {
1163 40 : int size = (result->aitems + 7) / 8;
1164 :
1165 40 : result->nullbitmap = (bits8 *) palloc(size);
1166 40 : temp = pq_getmsgbytes(&buf, size);
1167 40 : memcpy(result->nullbitmap, temp, size);
1168 : }
1169 : else
1170 40 : result->nullbitmap = NULL;
1171 :
1172 : /* nitems */
1173 80 : result->nitems = pq_getmsgint(&buf, 4);
1174 :
1175 : /* ndims */
1176 80 : result->ndims = pq_getmsgint(&buf, 4);
1177 :
1178 : /* dims */
1179 80 : temp = pq_getmsgbytes(&buf, sizeof(result->dims));
1180 80 : memcpy(result->dims, temp, sizeof(result->dims));
1181 :
1182 : /* lbs */
1183 80 : temp = pq_getmsgbytes(&buf, sizeof(result->lbs));
1184 80 : memcpy(result->lbs, temp, sizeof(result->lbs));
1185 :
1186 80 : pq_getmsgend(&buf);
1187 :
1188 80 : PG_RETURN_POINTER(result);
1189 : }
1190 :
1191 : Datum
1192 186 : array_agg_array_finalfn(PG_FUNCTION_ARGS)
1193 : {
1194 : Datum result;
1195 : ArrayBuildStateArr *state;
1196 :
1197 : /* cannot be called directly because of internal-type argument */
1198 : Assert(AggCheckCallContext(fcinfo, NULL));
1199 :
1200 186 : state = PG_ARGISNULL(0) ? NULL : (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
1201 :
1202 186 : if (state == NULL)
1203 0 : PG_RETURN_NULL(); /* returns null iff no input values */
1204 :
1205 : /*
1206 : * Make the result. We cannot release the ArrayBuildStateArr because
1207 : * sometimes aggregate final functions are re-executed. Rather, it is
1208 : * nodeAgg.c's responsibility to reset the aggcontext when it's safe to do
1209 : * so.
1210 : */
1211 186 : result = makeArrayResultArr(state, CurrentMemoryContext, false);
1212 :
1213 186 : PG_RETURN_DATUM(result);
1214 : }
1215 :
1216 : /*-----------------------------------------------------------------------------
1217 : * array_position, array_position_start :
1218 : * return the offset of a value in an array.
1219 : *
1220 : * IS NOT DISTINCT FROM semantics are used for comparisons. Return NULL when
1221 : * the value is not found.
1222 : *-----------------------------------------------------------------------------
1223 : */
1224 : Datum
1225 216 : array_position(PG_FUNCTION_ARGS)
1226 : {
1227 216 : return array_position_common(fcinfo);
1228 : }
1229 :
1230 : Datum
1231 18 : array_position_start(PG_FUNCTION_ARGS)
1232 : {
1233 18 : return array_position_common(fcinfo);
1234 : }
1235 :
1236 : /*
1237 : * array_position_common
1238 : * Common code for array_position and array_position_start
1239 : *
1240 : * These are separate wrappers for the sake of opr_sanity regression test.
1241 : * They are not strict so we have to test for null inputs explicitly.
1242 : */
1243 : static Datum
1244 234 : array_position_common(FunctionCallInfo fcinfo)
1245 : {
1246 : ArrayType *array;
1247 234 : Oid collation = PG_GET_COLLATION();
1248 : Oid element_type;
1249 : Datum searched_element,
1250 : value;
1251 : bool isnull;
1252 : int position,
1253 : position_min;
1254 234 : bool found = false;
1255 : TypeCacheEntry *typentry;
1256 : ArrayMetaState *my_extra;
1257 : bool null_search;
1258 : ArrayIterator array_iterator;
1259 :
1260 234 : if (PG_ARGISNULL(0))
1261 0 : PG_RETURN_NULL();
1262 :
1263 234 : array = PG_GETARG_ARRAYTYPE_P(0);
1264 :
1265 : /*
1266 : * We refuse to search for elements in multi-dimensional arrays, since we
1267 : * have no good way to report the element's location in the array.
1268 : */
1269 234 : if (ARR_NDIM(array) > 1)
1270 6 : ereport(ERROR,
1271 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1272 : errmsg("searching for elements in multidimensional arrays is not supported")));
1273 :
1274 : /* Searching in an empty array is well-defined, though: it always fails */
1275 228 : if (ARR_NDIM(array) < 1)
1276 0 : PG_RETURN_NULL();
1277 :
1278 228 : if (PG_ARGISNULL(1))
1279 : {
1280 : /* fast return when the array doesn't have nulls */
1281 12 : if (!array_contains_nulls(array))
1282 6 : PG_RETURN_NULL();
1283 6 : searched_element = (Datum) 0;
1284 6 : null_search = true;
1285 : }
1286 : else
1287 : {
1288 216 : searched_element = PG_GETARG_DATUM(1);
1289 216 : null_search = false;
1290 : }
1291 :
1292 222 : element_type = ARR_ELEMTYPE(array);
1293 222 : position = (ARR_LBOUND(array))[0] - 1;
1294 :
1295 : /* figure out where to start */
1296 222 : if (PG_NARGS() == 3)
1297 : {
1298 18 : if (PG_ARGISNULL(2))
1299 0 : ereport(ERROR,
1300 : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
1301 : errmsg("initial position must not be null")));
1302 :
1303 18 : position_min = PG_GETARG_INT32(2);
1304 : }
1305 : else
1306 204 : position_min = (ARR_LBOUND(array))[0];
1307 :
1308 : /*
1309 : * We arrange to look up type info for array_create_iterator only once per
1310 : * series of calls, assuming the element type doesn't change underneath
1311 : * us.
1312 : */
1313 222 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1314 222 : if (my_extra == NULL)
1315 : {
1316 84 : fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1317 : sizeof(ArrayMetaState));
1318 84 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1319 84 : my_extra->element_type = ~element_type;
1320 : }
1321 :
1322 222 : if (my_extra->element_type != element_type)
1323 : {
1324 84 : get_typlenbyvalalign(element_type,
1325 : &my_extra->typlen,
1326 : &my_extra->typbyval,
1327 : &my_extra->typalign);
1328 :
1329 84 : typentry = lookup_type_cache(element_type, TYPECACHE_EQ_OPR_FINFO);
1330 :
1331 84 : if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
1332 0 : ereport(ERROR,
1333 : (errcode(ERRCODE_UNDEFINED_FUNCTION),
1334 : errmsg("could not identify an equality operator for type %s",
1335 : format_type_be(element_type))));
1336 :
1337 84 : my_extra->element_type = element_type;
1338 84 : fmgr_info_cxt(typentry->eq_opr_finfo.fn_oid, &my_extra->proc,
1339 84 : fcinfo->flinfo->fn_mcxt);
1340 : }
1341 :
1342 : /* Examine each array element until we find a match. */
1343 222 : array_iterator = array_create_iterator(array, 0, my_extra);
1344 618 : while (array_iterate(array_iterator, &value, &isnull))
1345 : {
1346 576 : position++;
1347 :
1348 : /* skip initial elements if caller requested so */
1349 576 : if (position < position_min)
1350 78 : continue;
1351 :
1352 : /*
1353 : * Can't look at the array element's value if it's null; but if we
1354 : * search for null, we have a hit and are done.
1355 : */
1356 498 : if (isnull || null_search)
1357 : {
1358 42 : if (isnull && null_search)
1359 : {
1360 6 : found = true;
1361 6 : break;
1362 : }
1363 : else
1364 36 : continue;
1365 : }
1366 :
1367 : /* not nulls, so run the operator */
1368 456 : if (DatumGetBool(FunctionCall2Coll(&my_extra->proc, collation,
1369 : searched_element, value)))
1370 : {
1371 174 : found = true;
1372 174 : break;
1373 : }
1374 : }
1375 :
1376 222 : array_free_iterator(array_iterator);
1377 :
1378 : /* Avoid leaking memory when handed toasted input */
1379 222 : PG_FREE_IF_COPY(array, 0);
1380 :
1381 222 : if (!found)
1382 42 : PG_RETURN_NULL();
1383 :
1384 180 : PG_RETURN_INT32(position);
1385 : }
1386 :
1387 : /*-----------------------------------------------------------------------------
1388 : * array_positions :
1389 : * return an array of positions of a value in an array.
1390 : *
1391 : * IS NOT DISTINCT FROM semantics are used for comparisons. Returns NULL when
1392 : * the input array is NULL. When the value is not found in the array, returns
1393 : * an empty array.
1394 : *
1395 : * This is not strict so we have to test for null inputs explicitly.
1396 : *-----------------------------------------------------------------------------
1397 : */
1398 : Datum
1399 60 : array_positions(PG_FUNCTION_ARGS)
1400 : {
1401 : ArrayType *array;
1402 60 : Oid collation = PG_GET_COLLATION();
1403 : Oid element_type;
1404 : Datum searched_element,
1405 : value;
1406 : bool isnull;
1407 : int position;
1408 : TypeCacheEntry *typentry;
1409 : ArrayMetaState *my_extra;
1410 : bool null_search;
1411 : ArrayIterator array_iterator;
1412 60 : ArrayBuildState *astate = NULL;
1413 :
1414 60 : if (PG_ARGISNULL(0))
1415 12 : PG_RETURN_NULL();
1416 :
1417 48 : array = PG_GETARG_ARRAYTYPE_P(0);
1418 :
1419 : /*
1420 : * We refuse to search for elements in multi-dimensional arrays, since we
1421 : * have no good way to report the element's location in the array.
1422 : */
1423 48 : if (ARR_NDIM(array) > 1)
1424 6 : ereport(ERROR,
1425 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1426 : errmsg("searching for elements in multidimensional arrays is not supported")));
1427 :
1428 42 : astate = initArrayResult(INT4OID, CurrentMemoryContext, false);
1429 :
1430 : /* Searching in an empty array is well-defined, though: it always fails */
1431 42 : if (ARR_NDIM(array) < 1)
1432 0 : PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
1433 :
1434 42 : if (PG_ARGISNULL(1))
1435 : {
1436 : /* fast return when the array doesn't have nulls */
1437 12 : if (!array_contains_nulls(array))
1438 6 : PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
1439 6 : searched_element = (Datum) 0;
1440 6 : null_search = true;
1441 : }
1442 : else
1443 : {
1444 30 : searched_element = PG_GETARG_DATUM(1);
1445 30 : null_search = false;
1446 : }
1447 :
1448 36 : element_type = ARR_ELEMTYPE(array);
1449 36 : position = (ARR_LBOUND(array))[0] - 1;
1450 :
1451 : /*
1452 : * We arrange to look up type info for array_create_iterator only once per
1453 : * series of calls, assuming the element type doesn't change underneath
1454 : * us.
1455 : */
1456 36 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1457 36 : if (my_extra == NULL)
1458 : {
1459 30 : fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1460 : sizeof(ArrayMetaState));
1461 30 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1462 30 : my_extra->element_type = ~element_type;
1463 : }
1464 :
1465 36 : if (my_extra->element_type != element_type)
1466 : {
1467 30 : get_typlenbyvalalign(element_type,
1468 : &my_extra->typlen,
1469 : &my_extra->typbyval,
1470 : &my_extra->typalign);
1471 :
1472 30 : typentry = lookup_type_cache(element_type, TYPECACHE_EQ_OPR_FINFO);
1473 :
1474 30 : if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
1475 0 : ereport(ERROR,
1476 : (errcode(ERRCODE_UNDEFINED_FUNCTION),
1477 : errmsg("could not identify an equality operator for type %s",
1478 : format_type_be(element_type))));
1479 :
1480 30 : my_extra->element_type = element_type;
1481 30 : fmgr_info_cxt(typentry->eq_opr_finfo.fn_oid, &my_extra->proc,
1482 30 : fcinfo->flinfo->fn_mcxt);
1483 : }
1484 :
1485 : /*
1486 : * Accumulate each array position iff the element matches the given
1487 : * element.
1488 : */
1489 36 : array_iterator = array_create_iterator(array, 0, my_extra);
1490 816 : while (array_iterate(array_iterator, &value, &isnull))
1491 : {
1492 780 : position += 1;
1493 :
1494 : /*
1495 : * Can't look at the array element's value if it's null; but if we
1496 : * search for null, we have a hit.
1497 : */
1498 780 : if (isnull || null_search)
1499 : {
1500 72 : if (isnull && null_search)
1501 : astate =
1502 12 : accumArrayResult(astate, Int32GetDatum(position), false,
1503 : INT4OID, CurrentMemoryContext);
1504 :
1505 72 : continue;
1506 : }
1507 :
1508 : /* not nulls, so run the operator */
1509 708 : if (DatumGetBool(FunctionCall2Coll(&my_extra->proc, collation,
1510 : searched_element, value)))
1511 : astate =
1512 90 : accumArrayResult(astate, Int32GetDatum(position), false,
1513 : INT4OID, CurrentMemoryContext);
1514 : }
1515 :
1516 36 : array_free_iterator(array_iterator);
1517 :
1518 : /* Avoid leaking memory when handed toasted input */
1519 36 : PG_FREE_IF_COPY(array, 0);
1520 :
1521 36 : PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
1522 : }
1523 :
1524 : /*
1525 : * array_shuffle_n
1526 : * Return a copy of array with n randomly chosen items.
1527 : *
1528 : * The number of items must not exceed the size of the first dimension of the
1529 : * array. We preserve the first dimension's lower bound if keep_lb,
1530 : * else it's set to 1. Lower-order dimensions are preserved in any case.
1531 : *
1532 : * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
1533 : * from the system catalogs, given only the elmtyp. However, the caller is
1534 : * in a better position to cache this info across multiple calls.
1535 : */
1536 : static ArrayType *
1537 48 : array_shuffle_n(ArrayType *array, int n, bool keep_lb,
1538 : Oid elmtyp, TypeCacheEntry *typentry)
1539 : {
1540 : ArrayType *result;
1541 : int ndim,
1542 : *dims,
1543 : *lbs,
1544 : nelm,
1545 : nitem,
1546 : rdims[MAXDIM],
1547 : rlbs[MAXDIM];
1548 : int16 elmlen;
1549 : bool elmbyval;
1550 : char elmalign;
1551 : Datum *elms,
1552 : *ielms;
1553 : bool *nuls,
1554 : *inuls;
1555 :
1556 48 : ndim = ARR_NDIM(array);
1557 48 : dims = ARR_DIMS(array);
1558 48 : lbs = ARR_LBOUND(array);
1559 :
1560 48 : elmlen = typentry->typlen;
1561 48 : elmbyval = typentry->typbyval;
1562 48 : elmalign = typentry->typalign;
1563 :
1564 : /* If the target array is empty, exit fast */
1565 48 : if (ndim < 1 || dims[0] < 1 || n < 1)
1566 0 : return construct_empty_array(elmtyp);
1567 :
1568 48 : deconstruct_array(array, elmtyp, elmlen, elmbyval, elmalign,
1569 : &elms, &nuls, &nelm);
1570 :
1571 48 : nitem = dims[0]; /* total number of items */
1572 48 : nelm /= nitem; /* number of elements per item */
1573 :
1574 : Assert(n <= nitem); /* else it's caller error */
1575 :
1576 : /*
1577 : * Shuffle array using Fisher-Yates algorithm. Scan the array and swap
1578 : * current item (nelm datums starting at ielms) with a randomly chosen
1579 : * later item (nelm datums starting at jelms) in each iteration. We can
1580 : * stop once we've done n iterations; then first n items are the result.
1581 : */
1582 48 : ielms = elms;
1583 48 : inuls = nuls;
1584 228 : for (int i = 0; i < n; i++)
1585 : {
1586 180 : int j = (int) pg_prng_uint64_range(&pg_global_prng_state, i, nitem - 1) * nelm;
1587 180 : Datum *jelms = elms + j;
1588 180 : bool *jnuls = nuls + j;
1589 :
1590 : /* Swap i'th and j'th items; advance ielms/inuls to next item */
1591 492 : for (int k = 0; k < nelm; k++)
1592 : {
1593 312 : Datum elm = *ielms;
1594 312 : bool nul = *inuls;
1595 :
1596 312 : *ielms++ = *jelms;
1597 312 : *inuls++ = *jnuls;
1598 312 : *jelms++ = elm;
1599 312 : *jnuls++ = nul;
1600 : }
1601 : }
1602 :
1603 : /* Set up dimensions of the result */
1604 48 : memcpy(rdims, dims, ndim * sizeof(int));
1605 48 : memcpy(rlbs, lbs, ndim * sizeof(int));
1606 48 : rdims[0] = n;
1607 48 : if (!keep_lb)
1608 24 : rlbs[0] = 1;
1609 :
1610 48 : result = construct_md_array(elms, nuls, ndim, rdims, rlbs,
1611 : elmtyp, elmlen, elmbyval, elmalign);
1612 :
1613 48 : pfree(elms);
1614 48 : pfree(nuls);
1615 :
1616 48 : return result;
1617 : }
1618 :
1619 : /*
1620 : * array_shuffle
1621 : *
1622 : * Returns an array with the same dimensions as the input array, with its
1623 : * first-dimension elements in random order.
1624 : */
1625 : Datum
1626 24 : array_shuffle(PG_FUNCTION_ARGS)
1627 : {
1628 24 : ArrayType *array = PG_GETARG_ARRAYTYPE_P(0);
1629 : ArrayType *result;
1630 : Oid elmtyp;
1631 : TypeCacheEntry *typentry;
1632 :
1633 : /*
1634 : * There is no point in shuffling empty arrays or arrays with less than
1635 : * two items.
1636 : */
1637 24 : if (ARR_NDIM(array) < 1 || ARR_DIMS(array)[0] < 2)
1638 0 : PG_RETURN_ARRAYTYPE_P(array);
1639 :
1640 24 : elmtyp = ARR_ELEMTYPE(array);
1641 24 : typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
1642 24 : if (typentry == NULL || typentry->type_id != elmtyp)
1643 : {
1644 24 : typentry = lookup_type_cache(elmtyp, 0);
1645 24 : fcinfo->flinfo->fn_extra = (void *) typentry;
1646 : }
1647 :
1648 24 : result = array_shuffle_n(array, ARR_DIMS(array)[0], true, elmtyp, typentry);
1649 :
1650 24 : PG_RETURN_ARRAYTYPE_P(result);
1651 : }
1652 :
1653 : /*
1654 : * array_sample
1655 : *
1656 : * Returns an array of n randomly chosen first-dimension elements
1657 : * from the input array.
1658 : */
1659 : Datum
1660 36 : array_sample(PG_FUNCTION_ARGS)
1661 : {
1662 36 : ArrayType *array = PG_GETARG_ARRAYTYPE_P(0);
1663 36 : int n = PG_GETARG_INT32(1);
1664 : ArrayType *result;
1665 : Oid elmtyp;
1666 : TypeCacheEntry *typentry;
1667 : int nitem;
1668 :
1669 36 : nitem = (ARR_NDIM(array) < 1) ? 0 : ARR_DIMS(array)[0];
1670 :
1671 36 : if (n < 0 || n > nitem)
1672 12 : ereport(ERROR,
1673 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1674 : errmsg("sample size must be between 0 and %d", nitem)));
1675 :
1676 24 : elmtyp = ARR_ELEMTYPE(array);
1677 24 : typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
1678 24 : if (typentry == NULL || typentry->type_id != elmtyp)
1679 : {
1680 24 : typentry = lookup_type_cache(elmtyp, 0);
1681 24 : fcinfo->flinfo->fn_extra = (void *) typentry;
1682 : }
1683 :
1684 24 : result = array_shuffle_n(array, n, false, elmtyp, typentry);
1685 :
1686 24 : PG_RETURN_ARRAYTYPE_P(result);
1687 : }
|