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