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