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