PostgreSQL Source Code git master
Loading...
Searching...
No Matches
array_userfuncs.c
Go to the documentation of this file.
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 */
39
40/*
41 * DeserialIOData
42 * Used for caching element-type data in array_agg_deserialize
43 */
49
50/*
51 * ArraySortCachedInfo
52 * Used for caching catalog data in array_sort
53 */
54typedef 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 */
61
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 */
82{
84 Oid element_type;
87
88 /* If first time through, create datatype cache struct */
90 if (my_extra == NULL)
91 {
94 sizeof(ArrayMetaState));
95 my_extra->element_type = InvalidOid;
96 fcinfo->flinfo->fn_extra = my_extra;
97 }
98
99 /* Figure out which context we want the result in */
100 if (!AggCheckCallContext(fcinfo, &resultcxt))
102
103 /* Now collect the array value */
104 if (!PG_ARGISNULL(argno))
105 {
107
110 }
111 else
112 {
113 /* We have to look up the array type and element type */
115
119 errmsg("could not determine input data type")));
120 element_type = get_element_type(arr_typeid);
121 if (!OidIsValid(element_type))
124 errmsg("input data type is not an array")));
125
126 eah = construct_empty_expanded_array(element_type,
127 resultcxt,
128 my_extra);
129 }
130
131 return eah;
132}
133
134/*-----------------------------------------------------------------------------
135 * array_append :
136 * push an element onto the end of a one-dimensional array
137 *----------------------------------------------------------------------------
138 */
139Datum
141{
144 bool isNull;
146 int *dimv,
147 *lb;
148 int indx;
150
152 isNull = PG_ARGISNULL(1);
153 if (isNull)
154 newelem = (Datum) 0;
155 else
157
158 if (eah->ndims == 1)
159 {
160 /* append newelem */
161 lb = eah->lbound;
162 dimv = eah->dims;
163
164 /* index of added elem is at lb[0] + (dimv[0] - 1) + 1 */
165 if (pg_add_s32_overflow(lb[0], dimv[0], &indx))
168 errmsg("integer out of range")));
169 }
170 else if (eah->ndims == 0)
171 indx = 1;
172 else
175 errmsg("argument must be empty or one-dimensional array")));
176
177 /* Perform element insertion */
178 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
179
181 1, &indx, newelem, isNull,
182 -1, my_extra->typlen, my_extra->typbyval, my_extra->typalign);
183
185}
186
187/*
188 * array_append_support()
189 *
190 * Planner support function for array_append()
191 */
192Datum
194{
196 Node *ret = NULL;
197
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 */
206 Param *arg = (Param *) linitial(req->args);
207
208 if (arg && IsA(arg, Param) &&
209 arg->paramkind == PARAM_EXTERN &&
210 arg->paramid == req->paramid)
211 ret = (Node *) arg;
212 }
213
215}
216
217/*-----------------------------------------------------------------------------
218 * array_prepend :
219 * push an element onto the front of a one-dimensional array
220 *----------------------------------------------------------------------------
221 */
222Datum
224{
227 bool isNull;
229 int *lb;
230 int indx;
231 int lb0;
233
234 isNull = PG_ARGISNULL(0);
235 if (isNull)
236 newelem = (Datum) 0;
237 else
240
241 if (eah->ndims == 1)
242 {
243 /* prepend newelem */
244 lb = eah->lbound;
245 lb0 = lb[0];
246
247 if (pg_sub_s32_overflow(lb0, 1, &indx))
250 errmsg("integer out of range")));
251 }
252 else if (eah->ndims == 0)
253 {
254 indx = 1;
255 lb0 = 1;
256 }
257 else
260 errmsg("argument must be empty or one-dimensional array")));
261
262 /* Perform element insertion */
263 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
264
266 1, &indx, newelem, isNull,
267 -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 if (eah->ndims == 1)
272 {
273 /* This is ok whether we've deconstructed or not */
274 eah->lbound[0] = lb0;
275 }
276
278}
279
280/*
281 * array_prepend_support()
282 *
283 * Planner support function for array_prepend()
284 */
285Datum
287{
289 Node *ret = NULL;
290
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 */
299 Param *arg = (Param *) lsecond(req->args);
300
301 if (arg && IsA(arg, Param) &&
302 arg->paramkind == PARAM_EXTERN &&
303 arg->paramid == req->paramid)
304 ret = (Node *) arg;
305 }
306
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 */
316Datum
318{
319 ArrayType *v1,
320 *v2;
322 int *dims,
323 *lbs,
324 ndims,
325 nitems,
327 nbytes;
328 int *dims1,
329 *lbs1,
330 ndims1,
331 nitems1,
333 int *dims2,
334 *lbs2,
335 ndims2,
336 nitems2,
338 int i;
339 char *dat1,
340 *dat2;
341 uint8 *bitmap1,
342 *bitmap2;
343 Oid element_type;
346 int32 dataoffset;
347
348 /* Concatenating a null array is a no-op, just return the other input */
349 if (PG_ARGISNULL(0))
350 {
351 if (PG_ARGISNULL(1))
355 }
356 if (PG_ARGISNULL(1))
357 {
360 }
361
362 v1 = PG_GETARG_ARRAYTYPE_P(0);
364
367
368 /* Check we have matching element types */
372 errmsg("cannot concatenate incompatible arrays"),
373 errdetail("Arrays with element types %s and %s are not "
374 "compatible for concatenation.",
377
378 /* OK, use it */
379 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 ndims1 = ARR_NDIM(v1);
391 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 if (ndims1 == 0 && ndims2 > 0)
401
402 if (ndims2 == 0)
404
405 /* the rest fall under rule 3, 4, or 5 */
406 if (ndims1 != ndims2 &&
407 ndims1 != ndims2 - 1 &&
408 ndims1 != ndims2 + 1)
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 lbs1 = ARR_LBOUND(v1);
418 lbs2 = ARR_LBOUND(v2);
419 dims1 = ARR_DIMS(v1);
420 dims2 = ARR_DIMS(v2);
421 dat1 = ARR_DATA_PTR(v1);
429
430 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 ndims = ndims1;
437 dims = palloc_array(int, ndims);
438 lbs = palloc_array(int, ndims);
439
440 dims[0] = dims1[0] + dims2[0];
441 lbs[0] = lbs1[0];
442
443 for (i = 1; i < ndims; i++)
444 {
445 if (dims1[i] != dims2[i] || lbs1[i] != lbs2[i])
448 errmsg("cannot concatenate incompatible arrays"),
449 errdetail("Arrays with differing element dimensions are "
450 "not compatible for concatenation.")));
451
452 dims[i] = dims1[i];
453 lbs[i] = lbs1[i];
454 }
455 }
456 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 ndims = ndims2;
463 dims = palloc_array(int, ndims);
464 lbs = palloc_array(int, ndims);
465 memcpy(dims, dims2, ndims * sizeof(int));
466 memcpy(lbs, lbs2, ndims * sizeof(int));
467
468 /* increment number of elements in outer array */
469 dims[0] += 1;
470
471 /* make sure the added element matches our existing elements */
472 for (i = 0; i < ndims1; i++)
473 {
474 if (dims1[i] != dims[i + 1] || lbs1[i] != lbs[i + 1])
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 ndims = ndims1;
491 dims = palloc_array(int, ndims);
492 lbs = palloc_array(int, ndims);
493 memcpy(dims, dims1, ndims * sizeof(int));
494 memcpy(lbs, lbs1, ndims * sizeof(int));
495
496 /* increment number of elements in outer array */
497 dims[0] += 1;
498
499 /* make sure the added element matches our existing elements */
500 for (i = 0; i < ndims2; i++)
501 {
502 if (dims2[i] != dims[i + 1] || lbs2[i] != lbs[i + 1])
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 nitems = ArrayGetNItems(ndims, dims);
513 ArrayCheckBounds(ndims, dims, lbs);
514
515 /* build the result array */
517 if (ARR_HASNULL(v1) || ARR_HASNULL(v2))
518 {
519 dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
520 nbytes = ndatabytes + dataoffset;
521 }
522 else
523 {
524 dataoffset = 0; /* marker for no null bitmap */
525 nbytes = ndatabytes + ARR_OVERHEAD_NONULLS(ndims);
526 }
527 result = (ArrayType *) palloc0(nbytes);
528 SET_VARSIZE(result, nbytes);
529 result->ndim = ndims;
530 result->dataoffset = dataoffset;
531 result->elemtype = element_type;
532 memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
533 memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
534 /* data area is arg1 then arg2 */
537 /* handle the null bitmap if needed */
538 if (ARR_HASNULL(result))
539 {
541 bitmap1, 0,
542 nitems1);
544 bitmap2, 0,
545 nitems2);
546 }
547
549}
550
551
552/*
553 * ARRAY_AGG(anynonarray) aggregate function
554 */
555Datum
557{
558 Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
559 MemoryContext aggcontext;
561 Datum elem;
562
563 if (arg1_typeid == InvalidOid)
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 if (!AggCheckCallContext(fcinfo, &aggcontext))
575 {
576 /* cannot be called directly because of internal-type argument */
577 elog(ERROR, "array_agg_transfn called in non-aggregate context");
578 }
579
580 if (PG_ARGISNULL(0))
581 state = initArrayResult(arg1_typeid, aggcontext, false);
582 else
584
585 elem = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1);
586
588 elem,
589 PG_ARGISNULL(1),
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 */
599}
600
601Datum
603{
606 MemoryContext agg_context;
608
609 if (!AggCheckCallContext(fcinfo, &agg_context))
610 elog(ERROR, "aggregate function called in non-aggregate context");
611
614
615 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 if (state1 == NULL)
624 }
625
626 if (state1 == NULL)
627 {
628 /* We must copy state2's data into the agg_context */
629 state1 = initArrayResultWithSize(state2->element_type, agg_context,
630 false, state2->alen);
631
632 old_context = MemoryContextSwitchTo(agg_context);
633
634 for (int i = 0; i < state2->nelems; i++)
635 {
636 if (!state2->dnulls[i])
637 state1->dvalues[i] = datumCopy(state2->dvalues[i],
638 state1->typbyval,
639 state1->typlen);
640 else
641 state1->dvalues[i] = (Datum) 0;
642 }
643
645
646 memcpy(state1->dnulls, state2->dnulls, sizeof(bool) * state2->nelems);
647
648 state1->nelems = state2->nelems;
649
651 }
652 else if (state2->nelems > 0)
653 {
654 /* We only need to combine the two states if state2 has any elements */
655 int reqsize = state1->nelems + state2->nelems;
657
658 Assert(state1->element_type == state2->element_type);
659
660 /* Enlarge state1 arrays if needed */
661 if (state1->alen < reqsize)
662 {
663 /* Use a power of 2 size rather than allocating just reqsize */
665 state1->dvalues = (Datum *) repalloc(state1->dvalues,
666 state1->alen * sizeof(Datum));
667 state1->dnulls = (bool *) repalloc(state1->dnulls,
668 state1->alen * sizeof(bool));
669 }
670
671 /* Copy in the state2 elements to the end of the state1 arrays */
672 for (int i = 0; i < state2->nelems; i++)
673 {
674 if (!state2->dnulls[i])
675 state1->dvalues[i + state1->nelems] =
676 datumCopy(state2->dvalues[i],
677 state1->typbyval,
678 state1->typlen);
679 else
680 state1->dvalues[i + state1->nelems] = (Datum) 0;
681 }
682
683 memcpy(&state1->dnulls[state1->nelems], state2->dnulls,
684 sizeof(bool) * state2->nelems);
685
686 state1->nelems = reqsize;
687
689 }
690
692}
693
694/*
695 * array_agg_serialize
696 * Serialize ArrayBuildState into bytea.
697 */
698Datum
700{
703 bytea *result;
704
705 /* cannot be called directly because of internal-type argument */
707
709
711
712 /*
713 * element_type. Putting this first is more convenient in deserialization
714 */
715 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 pq_sendint64(&buf, state->nelems);
722
723 /* alen can be decided during deserialization */
724
725 /* typlen */
726 pq_sendint16(&buf, state->typlen);
727
728 /* typbyval */
729 pq_sendbyte(&buf, state->typbyval);
730
731 /* typalign */
732 pq_sendbyte(&buf, state->typalign);
733
734 /* dnulls */
735 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 if (state->typbyval)
745 pq_sendbytes(&buf, state->dvalues, sizeof(Datum) * state->nelems);
746 else
747 {
749 int i;
750
751 /* Avoid repeat catalog lookups for typsend function */
752 iodata = (SerialIOData *) fcinfo->flinfo->fn_extra;
753 if (iodata == NULL)
754 {
755 Oid typsend;
756 bool typisvarlena;
757
758 iodata = (SerialIOData *)
759 MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
760 sizeof(SerialIOData));
761 getTypeBinaryOutputInfo(state->element_type, &typsend,
762 &typisvarlena);
763 fmgr_info_cxt(typsend, &iodata->typsend,
764 fcinfo->flinfo->fn_mcxt);
765 fcinfo->flinfo->fn_extra = iodata;
766 }
767
768 for (i = 0; i < state->nelems; i++)
769 {
771
772 if (state->dnulls[i])
773 continue;
775 state->dvalues[i]);
779 }
780 }
781
783
785}
786
787Datum
789{
790 bytea *sstate;
793 Oid element_type;
794 int64 nelems;
795 const char *temp;
796
797 if (!AggCheckCallContext(fcinfo, NULL))
798 elog(ERROR, "aggregate function called in non-aggregate context");
799
800 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 */
807 VARSIZE_ANY_EXHDR(sstate));
808
809 /* element_type */
810 element_type = pq_getmsgint(&buf, 4);
811
812 /* nelems */
813 nelems = pq_getmsgint64(&buf);
814
815 /* Create output ArrayBuildState with the needed number of elements */
817 false, nelems);
818 result->nelems = nelems;
819
820 /* typlen */
821 result->typlen = pq_getmsgint(&buf, 2);
822
823 /* typbyval */
824 result->typbyval = pq_getmsgbyte(&buf);
825
826 /* typalign */
827 result->typalign = pq_getmsgbyte(&buf);
828
829 /* dnulls */
830 temp = pq_getmsgbytes(&buf, sizeof(bool) * nelems);
831 memcpy(result->dnulls, temp, sizeof(bool) * nelems);
832
833 /* dvalues --- see comment in array_agg_serialize */
834 if (result->typbyval)
835 {
836 temp = pq_getmsgbytes(&buf, sizeof(Datum) * nelems);
837 memcpy(result->dvalues, temp, sizeof(Datum) * nelems);
838 }
839 else
840 {
842
843 /* Avoid repeat catalog lookups for typreceive function */
844 iodata = (DeserialIOData *) fcinfo->flinfo->fn_extra;
845 if (iodata == NULL)
846 {
847 Oid typreceive;
848
850 MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
851 sizeof(DeserialIOData));
852 getTypeBinaryInputInfo(element_type, &typreceive,
853 &iodata->typioparam);
854 fmgr_info_cxt(typreceive, &iodata->typreceive,
855 fcinfo->flinfo->fn_mcxt);
856 fcinfo->flinfo->fn_extra = iodata;
857 }
858
859 for (int i = 0; i < nelems; i++)
860 {
861 int itemlen;
863
864 if (result->dnulls[i])
865 {
866 result->dvalues[i] = (Datum) 0;
867 continue;
868 }
869
870 itemlen = pq_getmsgint(&buf, 4);
871 if (itemlen < 0 || itemlen > (buf.len - buf.cursor))
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 */
882
883 buf.cursor += itemlen;
884
885 /* Now call the element's receiveproc */
886 result->dvalues[i] = ReceiveFunctionCall(&iodata->typreceive,
887 &elem_buf,
888 iodata->typioparam,
889 -1);
890 }
891 }
892
894
896}
897
898Datum
900{
903 int dims[1];
904 int lbs[1];
905
906 /* cannot be called directly because of internal-type argument */
908
910
911 if (state == NULL)
912 PG_RETURN_NULL(); /* returns null iff no input values */
913
914 dims[0] = state->nelems;
915 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 result = makeMdArrayResult(state, 1, dims, lbs,
925 false);
926
928}
929
930/*
931 * ARRAY_AGG(anyarray) aggregate function
932 */
933Datum
935{
936 Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
937 MemoryContext aggcontext;
939
940 if (arg1_typeid == InvalidOid)
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 if (!AggCheckCallContext(fcinfo, &aggcontext))
952 {
953 /* cannot be called directly because of internal-type argument */
954 elog(ERROR, "array_agg_array_transfn called in non-aggregate context");
955 }
956
957
958 if (PG_ARGISNULL(0))
959 state = initArrayResultArr(arg1_typeid, InvalidOid, aggcontext, false);
960 else
962
965 PG_ARGISNULL(1),
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 */
975}
976
977Datum
979{
982 MemoryContext agg_context;
984
985 if (!AggCheckCallContext(fcinfo, &agg_context))
986 elog(ERROR, "aggregate function called in non-aggregate context");
987
990
991 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 if (state1 == NULL)
1000 }
1001
1002 if (state1 == NULL)
1003 {
1004 /* We must copy state2's data into the agg_context */
1005 old_context = MemoryContextSwitchTo(agg_context);
1006
1008 agg_context, false);
1009
1010 state1->abytes = state2->abytes;
1011 state1->data = (char *) palloc(state1->abytes);
1012
1013 if (state2->nullbitmap)
1014 {
1015 int size = (state2->aitems + 7) / 8;
1016
1017 state1->nullbitmap = (uint8 *) palloc(size);
1018 memcpy(state1->nullbitmap, state2->nullbitmap, size);
1019 }
1020
1021 memcpy(state1->data, state2->data, state2->nbytes);
1022 state1->nbytes = state2->nbytes;
1023 state1->aitems = state2->aitems;
1024 state1->nitems = state2->nitems;
1025 state1->ndims = state2->ndims;
1026 memcpy(state1->dims, state2->dims, sizeof(state2->dims));
1027 memcpy(state1->lbs, state2->lbs, sizeof(state2->lbs));
1028 state1->array_type = state2->array_type;
1029 state1->element_type = state2->element_type;
1030
1032
1034 }
1035
1036 /* We only need to combine the two states if state2 has any items */
1037 if (state2->nitems > 0)
1038 {
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 if (state1->ndims != state2->ndims)
1051 ereport(ERROR,
1053 errmsg("cannot accumulate arrays of different dimensionality")));
1054
1055 /* Check dimensions match ignoring the first dimension. */
1056 for (i = 1; i < state1->ndims; i++)
1057 {
1058 if (state1->dims[i] != state2->dims[i] || state1->lbs[i] != state2->lbs[i])
1059 ereport(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 if (pg_add_s32_overflow(state1->nbytes, state2->nbytes, &reqsize) ||
1070 pg_add_s32_overflow(state1->nitems, state2->nitems, &newnitems))
1071 ereport(ERROR,
1073 errmsg("array size exceeds the maximum allowed (%zu)",
1074 MaxArraySize)));
1075
1077
1078 /*
1079 * If there's not enough space in state1 then we'll need to reallocate
1080 * more.
1081 */
1082 if (state1->abytes < reqsize)
1083 {
1084 /* use a power of 2 size rather than allocating just reqsize */
1085 state1->abytes = pg_nextpower2_32(reqsize);
1086 state1->data = (char *) repalloc(state1->data, state1->abytes);
1087 }
1088
1089 /* Combine the null bitmaps, if present. */
1090 if (state1->nullbitmap || state2->nullbitmap)
1091 {
1092 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 state1->aitems = pg_nextpower2_32(Max(256, newnitems));
1099 state1->nullbitmap = (uint8 *) palloc((state1->aitems + 7) / 8);
1100 array_bitmap_copy(state1->nullbitmap, 0,
1101 NULL, 0,
1102 state1->nitems);
1103 }
1104 else if (newnitems > state1->aitems)
1105 {
1107 state1->nullbitmap = (uint8 *)
1108 repalloc(state1->nullbitmap, (state1->aitems + 7) / 8);
1109 }
1110 /* This will do the right thing if state2->nullbitmap is NULL: */
1111 array_bitmap_copy(state1->nullbitmap, state1->nitems,
1112 state2->nullbitmap, 0,
1113 state2->nitems);
1114 }
1115
1116 /* Finally, combine the data and adjust sizes. */
1117 memcpy(state1->data + state1->nbytes, state2->data, state2->nbytes);
1118 state1->nbytes += state2->nbytes;
1119 state1->nitems += state2->nitems;
1120
1121 state1->dims[0] += state2->dims[0];
1122 /* remaining dims already match, per test above */
1123
1125 }
1126
1128}
1129
1130/*
1131 * array_agg_array_serialize
1132 * Serialize ArrayBuildStateArr into bytea.
1133 */
1134Datum
1136{
1139 bytea *result;
1140
1141 /* cannot be called directly because of internal-type argument */
1143
1145
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 pq_sendint32(&buf, state->element_type);
1153
1154 /* array_type */
1155 pq_sendint32(&buf, state->array_type);
1156
1157 /* nbytes */
1158 pq_sendint32(&buf, state->nbytes);
1159
1160 /* data */
1161 pq_sendbytes(&buf, state->data, state->nbytes);
1162
1163 /* abytes */
1164 pq_sendint32(&buf, state->abytes);
1165
1166 /* aitems */
1167 pq_sendint32(&buf, state->aitems);
1168
1169 /* nullbitmap */
1170 if (state->nullbitmap)
1171 {
1172 Assert(state->aitems > 0);
1173 pq_sendbytes(&buf, state->nullbitmap, (state->aitems + 7) / 8);
1174 }
1175
1176 /* nitems */
1177 pq_sendint32(&buf, state->nitems);
1178
1179 /* ndims */
1180 pq_sendint32(&buf, state->ndims);
1181
1182 /* dims: XXX should we just send ndims elements? */
1183 pq_sendbytes(&buf, state->dims, sizeof(state->dims));
1184
1185 /* lbs */
1186 pq_sendbytes(&buf, state->lbs, sizeof(state->lbs));
1187
1189
1191}
1192
1193Datum
1195{
1196 bytea *sstate;
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 */
1206
1207 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 */
1214 VARSIZE_ANY_EXHDR(sstate));
1215
1216 /* element_type */
1217 element_type = pq_getmsgint(&buf, 4);
1218
1219 /* array_type */
1220 array_type = pq_getmsgint(&buf, 4);
1221
1222 /* nbytes */
1223 nbytes = pq_getmsgint(&buf, 4);
1224
1225 result = initArrayResultArr(array_type, element_type,
1226 CurrentMemoryContext, false);
1227
1228 result->abytes = 1024;
1229 while (result->abytes < nbytes)
1230 result->abytes *= 2;
1231
1232 result->data = (char *) palloc(result->abytes);
1233
1234 /* data */
1235 temp = pq_getmsgbytes(&buf, nbytes);
1236 memcpy(result->data, temp, nbytes);
1237 result->nbytes = nbytes;
1238
1239 /* abytes */
1240 result->abytes = pq_getmsgint(&buf, 4);
1241
1242 /* aitems: might be 0 */
1243 result->aitems = pq_getmsgint(&buf, 4);
1244
1245 /* nullbitmap */
1246 if (result->aitems > 0)
1247 {
1248 int size = (result->aitems + 7) / 8;
1249
1250 result->nullbitmap = (uint8 *) palloc(size);
1251 temp = pq_getmsgbytes(&buf, size);
1252 memcpy(result->nullbitmap, temp, size);
1253 }
1254 else
1255 result->nullbitmap = NULL;
1256
1257 /* nitems */
1258 result->nitems = pq_getmsgint(&buf, 4);
1259
1260 /* ndims */
1261 result->ndims = pq_getmsgint(&buf, 4);
1262
1263 /* dims */
1264 temp = pq_getmsgbytes(&buf, sizeof(result->dims));
1265 memcpy(result->dims, temp, sizeof(result->dims));
1266
1267 /* lbs */
1268 temp = pq_getmsgbytes(&buf, sizeof(result->lbs));
1269 memcpy(result->lbs, temp, sizeof(result->lbs));
1270
1271 pq_getmsgend(&buf);
1272
1274}
1275
1276Datum
1278{
1279 Datum result;
1281
1282 /* cannot be called directly because of internal-type argument */
1284
1286
1287 if (state == NULL)
1288 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 */
1297
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 */
1309Datum
1314
1315Datum
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 */
1328static Datum
1330{
1331 ArrayType *array;
1332 Oid collation = PG_GET_COLLATION();
1333 Oid element_type;
1335 value;
1336 bool isnull;
1337 int position,
1339 bool found = false;
1340 TypeCacheEntry *typentry;
1342 bool null_search;
1344
1345 if (PG_ARGISNULL(0))
1347
1348 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 if (ARR_NDIM(array) > 1)
1355 ereport(ERROR,
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 if (ARR_NDIM(array) < 1)
1362
1363 if (PG_ARGISNULL(1))
1364 {
1365 /* fast return when the array doesn't have nulls */
1366 if (!array_contains_nulls(array))
1368 searched_element = (Datum) 0;
1369 null_search = true;
1370 }
1371 else
1372 {
1374 null_search = false;
1375 }
1376
1377 element_type = ARR_ELEMTYPE(array);
1378 position = (ARR_LBOUND(array))[0] - 1;
1379
1380 /* figure out where to start */
1381 if (PG_NARGS() == 3)
1382 {
1383 if (PG_ARGISNULL(2))
1384 ereport(ERROR,
1386 errmsg("initial position must not be null")));
1387
1389 }
1390 else
1391 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 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1399 if (my_extra == NULL)
1400 {
1401 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1402 sizeof(ArrayMetaState));
1403 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1404 my_extra->element_type = ~element_type;
1405 }
1406
1407 if (my_extra->element_type != element_type)
1408 {
1409 get_typlenbyvalalign(element_type,
1410 &my_extra->typlen,
1411 &my_extra->typbyval,
1412 &my_extra->typalign);
1413
1414 typentry = lookup_type_cache(element_type, TYPECACHE_EQ_OPR_FINFO);
1415
1416 if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
1417 ereport(ERROR,
1419 errmsg("could not identify an equality operator for type %s",
1420 format_type_be(element_type))));
1421
1422 my_extra->element_type = element_type;
1423 fmgr_info_cxt(typentry->eq_opr_finfo.fn_oid, &my_extra->proc,
1424 fcinfo->flinfo->fn_mcxt);
1425 }
1426
1427 /* Examine each array element until we find a match. */
1429 while (array_iterate(array_iterator, &value, &isnull))
1430 {
1431 position++;
1432
1433 /* skip initial elements if caller requested so */
1434 if (position < position_min)
1435 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 if (isnull || null_search)
1442 {
1443 if (isnull && null_search)
1444 {
1445 found = true;
1446 break;
1447 }
1448 else
1449 continue;
1450 }
1451
1452 /* not nulls, so run the operator */
1453 if (DatumGetBool(FunctionCall2Coll(&my_extra->proc, collation,
1455 {
1456 found = true;
1457 break;
1458 }
1459 }
1460
1462
1463 /* Avoid leaking memory when handed toasted input */
1464 PG_FREE_IF_COPY(array, 0);
1465
1466 if (!found)
1468
1469 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 */
1483Datum
1485{
1486 ArrayType *array;
1487 Oid collation = PG_GET_COLLATION();
1488 Oid element_type;
1490 value;
1491 bool isnull;
1492 int position;
1493 TypeCacheEntry *typentry;
1495 bool null_search;
1497 ArrayBuildState *astate = NULL;
1498
1499 if (PG_ARGISNULL(0))
1501
1502 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 if (ARR_NDIM(array) > 1)
1509 ereport(ERROR,
1511 errmsg("searching for elements in multidimensional arrays is not supported")));
1512
1514
1515 /* Searching in an empty array is well-defined, though: it always fails */
1516 if (ARR_NDIM(array) < 1)
1518
1519 if (PG_ARGISNULL(1))
1520 {
1521 /* fast return when the array doesn't have nulls */
1522 if (!array_contains_nulls(array))
1524 searched_element = (Datum) 0;
1525 null_search = true;
1526 }
1527 else
1528 {
1530 null_search = false;
1531 }
1532
1533 element_type = ARR_ELEMTYPE(array);
1534 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 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1542 if (my_extra == NULL)
1543 {
1544 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1545 sizeof(ArrayMetaState));
1546 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1547 my_extra->element_type = ~element_type;
1548 }
1549
1550 if (my_extra->element_type != element_type)
1551 {
1552 get_typlenbyvalalign(element_type,
1553 &my_extra->typlen,
1554 &my_extra->typbyval,
1555 &my_extra->typalign);
1556
1557 typentry = lookup_type_cache(element_type, TYPECACHE_EQ_OPR_FINFO);
1558
1559 if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
1560 ereport(ERROR,
1562 errmsg("could not identify an equality operator for type %s",
1563 format_type_be(element_type))));
1564
1565 my_extra->element_type = element_type;
1566 fmgr_info_cxt(typentry->eq_opr_finfo.fn_oid, &my_extra->proc,
1567 fcinfo->flinfo->fn_mcxt);
1568 }
1569
1570 /*
1571 * Accumulate each array position iff the element matches the given
1572 * element.
1573 */
1575 while (array_iterate(array_iterator, &value, &isnull))
1576 {
1577 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 if (isnull || null_search)
1584 {
1585 if (isnull && null_search)
1586 astate =
1587 accumArrayResult(astate, Int32GetDatum(position), false,
1589
1590 continue;
1591 }
1592
1593 /* not nulls, so run the operator */
1594 if (DatumGetBool(FunctionCall2Coll(&my_extra->proc, collation,
1596 astate =
1597 accumArrayResult(astate, Int32GetDatum(position), false,
1599 }
1600
1602
1603 /* Avoid leaking memory when handed toasted input */
1604 PG_FREE_IF_COPY(array, 0);
1605
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 */
1621static ArrayType *
1623 Oid elmtyp, TypeCacheEntry *typentry)
1624{
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 ndim = ARR_NDIM(array);
1642 dims = ARR_DIMS(array);
1643 lbs = ARR_LBOUND(array);
1644
1645 elmlen = typentry->typlen;
1646 elmbyval = typentry->typbyval;
1647 elmalign = typentry->typalign;
1648
1649 /* If the target array is empty, exit fast */
1650 if (ndim < 1 || dims[0] < 1 || n < 1)
1652
1653 deconstruct_array(array, elmtyp, elmlen, elmbyval, elmalign,
1654 &elms, &nuls, &nelm);
1655
1656 nitem = dims[0]; /* total number of items */
1657 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 ielms = elms;
1668 inuls = nuls;
1669 for (int i = 0; i < n; i++)
1670 {
1671 int j = (int) pg_prng_uint64_range(&pg_global_prng_state, i, nitem - 1) * nelm;
1672 Datum *jelms = elms + j;
1673 bool *jnuls = nuls + j;
1674
1675 /* Swap i'th and j'th items; advance ielms/inuls to next item */
1676 for (int k = 0; k < nelm; k++)
1677 {
1678 Datum elm = *ielms;
1679 bool nul = *inuls;
1680
1681 *ielms++ = *jelms;
1682 *inuls++ = *jnuls;
1683 *jelms++ = elm;
1684 *jnuls++ = nul;
1685 }
1686 }
1687
1688 /* Set up dimensions of the result */
1689 memcpy(rdims, dims, ndim * sizeof(int));
1690 memcpy(rlbs, lbs, ndim * sizeof(int));
1691 rdims[0] = n;
1692 if (!keep_lb)
1693 rlbs[0] = 1;
1694
1695 result = construct_md_array(elms, nuls, ndim, rdims, rlbs,
1696 elmtyp, elmlen, elmbyval, elmalign);
1697
1698 pfree(elms);
1699 pfree(nuls);
1700
1701 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 */
1710Datum
1712{
1713 ArrayType *array = PG_GETARG_ARRAYTYPE_P(0);
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 if (ARR_NDIM(array) < 1 || ARR_DIMS(array)[0] < 2)
1723 PG_RETURN_ARRAYTYPE_P(array);
1724
1725 elmtyp = ARR_ELEMTYPE(array);
1726 typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
1727 if (typentry == NULL || typentry->type_id != elmtyp)
1728 {
1729 typentry = lookup_type_cache(elmtyp, 0);
1730 fcinfo->flinfo->fn_extra = typentry;
1731 }
1732
1733 result = array_shuffle_n(array, ARR_DIMS(array)[0], true, elmtyp, typentry);
1734
1736}
1737
1738/*
1739 * array_sample
1740 *
1741 * Returns an array of n randomly chosen first-dimension elements
1742 * from the input array.
1743 */
1744Datum
1746{
1747 ArrayType *array = PG_GETARG_ARRAYTYPE_P(0);
1748 int n = PG_GETARG_INT32(1);
1750 Oid elmtyp;
1751 TypeCacheEntry *typentry;
1752 int nitem;
1753
1754 nitem = (ARR_NDIM(array) < 1) ? 0 : ARR_DIMS(array)[0];
1755
1756 if (n < 0 || n > nitem)
1757 ereport(ERROR,
1759 errmsg("sample size must be between 0 and %d", nitem)));
1760
1761 elmtyp = ARR_ELEMTYPE(array);
1762 typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
1763 if (typentry == NULL || typentry->type_id != elmtyp)
1764 {
1765 typentry = lookup_type_cache(elmtyp, 0);
1766 fcinfo->flinfo->fn_extra = typentry;
1767 }
1768
1769 result = array_shuffle_n(array, n, false, elmtyp, typentry);
1770
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 */
1783static ArrayType *
1785{
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 ndim = ARR_NDIM(array);
1803 dims = ARR_DIMS(array);
1804 lbs = ARR_LBOUND(array);
1805
1806 elmlen = typentry->typlen;
1807 elmbyval = typentry->typbyval;
1808 elmalign = typentry->typalign;
1809
1810 deconstruct_array(array, elmtyp, elmlen, elmbyval, elmalign,
1811 &elms, &nuls, &nelm);
1812
1813 nitem = dims[0]; /* total number of items */
1814 nelm /= nitem; /* number of elements per item */
1815
1816 /* Reverse the array */
1817 ielms = elms;
1818 inuls = nuls;
1819 for (int i = 0; i < nitem / 2; i++)
1820 {
1821 int j = (nitem - i - 1) * nelm;
1822 Datum *jelms = elms + j;
1823 bool *jnuls = nuls + j;
1824
1825 /* Swap i'th and j'th items; advance ielms/inuls to next item */
1826 for (int k = 0; k < nelm; k++)
1827 {
1828 Datum elm = *ielms;
1829 bool nul = *inuls;
1830
1831 *ielms++ = *jelms;
1832 *inuls++ = *jnuls;
1833 *jelms++ = elm;
1834 *jnuls++ = nul;
1835 }
1836 }
1837
1838 /* Set up dimensions of the result */
1839 memcpy(rdims, dims, ndim * sizeof(int));
1840 memcpy(rlbs, lbs, ndim * sizeof(int));
1841 rdims[0] = nitem;
1842
1843 result = construct_md_array(elms, nuls, ndim, rdims, rlbs,
1844 elmtyp, elmlen, elmbyval, elmalign);
1845
1846 pfree(elms);
1847 pfree(nuls);
1848
1849 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 */
1858Datum
1860{
1861 ArrayType *array = PG_GETARG_ARRAYTYPE_P(0);
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 if (ARR_NDIM(array) < 1 || ARR_DIMS(array)[0] < 2)
1871 PG_RETURN_ARRAYTYPE_P(array);
1872
1873 elmtyp = ARR_ELEMTYPE(array);
1874 typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
1875 if (typentry == NULL || typentry->type_id != elmtyp)
1876 {
1877 typentry = lookup_type_cache(elmtyp, 0);
1878 fcinfo->flinfo->fn_extra = (void *) typentry;
1879 }
1880
1881 result = array_reverse_n(array, elmtyp, typentry);
1882
1884}
1885
1886/*
1887 * array_sort
1888 *
1889 * Sorts the first dimension of the array.
1890 */
1891static ArrayType *
1892array_sort_internal(ArrayType *array, bool descending, bool nulls_first,
1893 FunctionCallInfo fcinfo)
1894{
1896 Oid collation = PG_GET_COLLATION();
1897 int ndim,
1898 *dims,
1899 *lbs;
1901 Oid elmtyp;
1902 Oid sort_typ;
1903 Oid sort_opr;
1904 Tuplesortstate *tuplesortstate;
1906 Datum value;
1907 bool isnull;
1908 ArrayBuildStateAny *astate = NULL;
1909
1910 ndim = ARR_NDIM(array);
1911 dims = ARR_DIMS(array);
1912 lbs = ARR_LBOUND(array);
1913
1914 /* Quick exit if we don't need to sort */
1915 if (ndim < 1 || dims[0] < 2)
1916 return array;
1917
1918 /* Set up cache area if we didn't already */
1920 if (cache_info == NULL)
1921 {
1924 sizeof(ArraySortCachedInfo));
1925 fcinfo->flinfo->fn_extra = cache_info;
1926 }
1927
1928 /* Fetch and cache required data if we don't have it */
1929 elmtyp = ARR_ELEMTYPE(array);
1930 if (elmtyp != cache_info->array_meta.element_type)
1931 {
1932 TypeCacheEntry *typentry;
1933
1934 typentry = lookup_type_cache(elmtyp,
1936 cache_info->array_meta.element_type = elmtyp;
1937 cache_info->array_meta.typlen = typentry->typlen;
1938 cache_info->array_meta.typbyval = typentry->typbyval;
1939 cache_info->array_meta.typalign = typentry->typalign;
1940 cache_info->elem_lt_opr = typentry->lt_opr;
1941 cache_info->elem_gt_opr = typentry->gt_opr;
1942 cache_info->array_type = typentry->typarray;
1943 }
1944
1945 /* Identify the sort operator to use */
1946 if (ndim == 1)
1947 {
1948 /* Need to sort the element type */
1949 sort_typ = elmtyp;
1950 sort_opr = (descending ? cache_info->elem_gt_opr : cache_info->elem_lt_opr);
1951 }
1952 else
1953 {
1954 /* Otherwise we're sorting arrays */
1955 sort_typ = cache_info->array_type;
1956 if (!OidIsValid(sort_typ))
1957 ereport(ERROR,
1959 errmsg("could not find array type for data type %s",
1961 /* We know what operators to use for arrays */
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 if (!OidIsValid(sort_opr))
1970 ereport(ERROR,
1972 errmsg("could not identify a comparison function for type %s",
1974
1975 /* Put the things to be sorted (elements or sub-arrays) into a tuplesort */
1976 tuplesortstate = tuplesort_begin_datum(sort_typ,
1977 sort_opr,
1978 collation,
1979 nulls_first,
1980 work_mem,
1981 NULL,
1983
1984 array_iterator = array_create_iterator(array, ndim - 1,
1985 &cache_info->array_meta);
1986 while (array_iterate(array_iterator, &value, &isnull))
1987 {
1988 tuplesort_putdatum(tuplesortstate, value, isnull);
1989 }
1991
1992 /* Do the sort */
1993 tuplesort_performsort(tuplesortstate);
1994
1995 /* Extract results into a new array */
1996 while (tuplesort_getdatum(tuplesortstate, true, false, &value, &isnull, NULL))
1997 {
1998 astate = accumArrayResultAny(astate, value, isnull,
2000 }
2001 tuplesort_end(tuplesortstate);
2002
2005 true));
2006
2007 /* Adjust lower bound to match the input */
2008 ARR_LBOUND(newarray)[0] = lbs[0];
2009
2010 return newarray;
2011}
2012
2013Datum
2015{
2016 ArrayType *array = PG_GETARG_ARRAYTYPE_P(0);
2017
2019 false,
2020 false,
2021 fcinfo));
2022}
2023
2024Datum
2026{
2027 ArrayType *array = PG_GETARG_ARRAYTYPE_P(0);
2028 bool descending = PG_GETARG_BOOL(1);
2029
2031 descending,
2032 descending,
2033 fcinfo));
2034}
2035
2036Datum
2038{
2039 ArrayType *array = PG_GETARG_ARRAYTYPE_P(0);
2040 bool descending = PG_GETARG_BOOL(1);
2041 bool nulls_first = PG_GETARG_BOOL(2);
2042
2044 descending,
2045 nulls_first,
2046 fcinfo));
2047}
static bool array_iterator(ArrayType *la, PGCALL2 callback, void *param, ltree **found)
Definition _ltree_op.c:38
#define ARR_NDIM(a)
Definition array.h:290
#define PG_GETARG_ARRAYTYPE_P(n)
Definition array.h:263
#define ARR_DATA_PTR(a)
Definition array.h:322
#define MAXDIM
Definition array.h:75
#define ARR_NULLBITMAP(a)
Definition array.h:300
#define MaxArraySize
Definition array.h:82
#define ARR_OVERHEAD_WITHNULLS(ndims, nitems)
Definition array.h:312
#define DatumGetArrayTypeP(X)
Definition array.h:261
#define ARR_ELEMTYPE(a)
Definition array.h:292
#define PG_RETURN_ARRAYTYPE_P(x)
Definition array.h:265
#define ARR_SIZE(a)
Definition array.h:289
#define ARR_OVERHEAD_NONULLS(ndims)
Definition array.h:310
#define ARR_DATA_OFFSET(a)
Definition array.h:316
#define ARR_DIMS(a)
Definition array.h:294
#define ARR_HASNULL(a)
Definition array.h:291
#define PG_GETARG_EXPANDED_ARRAYX(n, metacache)
Definition array.h:269
#define ARR_LBOUND(a)
Definition array.h:296
Datum array_sample(PG_FUNCTION_ARGS)
Datum array_prepend(PG_FUNCTION_ARGS)
Datum array_agg_deserialize(PG_FUNCTION_ARGS)
Datum array_positions(PG_FUNCTION_ARGS)
static Datum array_position_common(FunctionCallInfo fcinfo)
Datum array_position(PG_FUNCTION_ARGS)
static ExpandedArrayHeader * fetch_array_arg_replace_nulls(FunctionCallInfo fcinfo, int argno)
Datum array_agg_combine(PG_FUNCTION_ARGS)
Datum array_sort(PG_FUNCTION_ARGS)
Datum array_reverse(PG_FUNCTION_ARGS)
Datum array_append(PG_FUNCTION_ARGS)
Datum array_agg_array_combine(PG_FUNCTION_ARGS)
Datum array_agg_serialize(PG_FUNCTION_ARGS)
Datum array_agg_array_deserialize(PG_FUNCTION_ARGS)
Datum array_append_support(PG_FUNCTION_ARGS)
Datum array_agg_finalfn(PG_FUNCTION_ARGS)
Datum array_agg_array_transfn(PG_FUNCTION_ARGS)
Datum array_position_start(PG_FUNCTION_ARGS)
static ArrayType * array_sort_internal(ArrayType *array, bool descending, bool nulls_first, FunctionCallInfo fcinfo)
Datum array_shuffle(PG_FUNCTION_ARGS)
Datum array_cat(PG_FUNCTION_ARGS)
static ArrayType * array_reverse_n(ArrayType *array, Oid elmtyp, TypeCacheEntry *typentry)
Datum array_sort_order(PG_FUNCTION_ARGS)
static ArrayType * array_shuffle_n(ArrayType *array, int n, bool keep_lb, Oid elmtyp, TypeCacheEntry *typentry)
Datum array_sort_order_nulls_first(PG_FUNCTION_ARGS)
Datum array_agg_transfn(PG_FUNCTION_ARGS)
Datum array_agg_array_finalfn(PG_FUNCTION_ARGS)
Datum array_agg_array_serialize(PG_FUNCTION_ARGS)
Datum array_prepend_support(PG_FUNCTION_ARGS)
ArrayBuildState * accumArrayResult(ArrayBuildState *astate, Datum dvalue, bool disnull, Oid element_type, MemoryContext rcontext)
ExpandedArrayHeader * construct_empty_expanded_array(Oid element_type, MemoryContext parentcontext, ArrayMetaState *metacache)
bool array_iterate(ArrayIterator iterator, Datum *value, bool *isnull)
void array_free_iterator(ArrayIterator iterator)
ArrayBuildStateAny * accumArrayResultAny(ArrayBuildStateAny *astate, Datum dvalue, bool disnull, Oid input_type, MemoryContext rcontext)
ArrayType * construct_empty_array(Oid elmtype)
void array_bitmap_copy(uint8 *destbitmap, int destoffset, const uint8 *srcbitmap, int srcoffset, int nitems)
bool array_contains_nulls(const ArrayType *array)
Datum makeArrayResultArr(ArrayBuildStateArr *astate, MemoryContext rcontext, bool release)
Datum makeArrayResultAny(ArrayBuildStateAny *astate, MemoryContext rcontext, bool release)
Datum array_set_element(Datum arraydatum, int nSubscripts, int *indx, Datum dataValue, bool isNull, int arraytyplen, int elmlen, bool elmbyval, char elmalign)
Datum makeMdArrayResult(ArrayBuildState *astate, int ndims, int *dims, int *lbs, MemoryContext rcontext, bool release)
ArrayBuildStateArr * initArrayResultArr(Oid array_type, Oid element_type, MemoryContext rcontext, bool subcontext)
ArrayBuildStateArr * accumArrayResultArr(ArrayBuildStateArr *astate, Datum dvalue, bool disnull, Oid array_type, MemoryContext rcontext)
ArrayIterator array_create_iterator(ArrayType *arr, int slice_ndim, ArrayMetaState *mstate)
ArrayBuildState * initArrayResultWithSize(Oid element_type, MemoryContext rcontext, bool subcontext, int initsize)
ArrayType * construct_md_array(Datum *elems, bool *nulls, int ndims, int *dims, int *lbs, Oid elmtype, int elmlen, bool elmbyval, char elmalign)
ArrayBuildState * initArrayResult(Oid element_type, MemoryContext rcontext, bool subcontext)
Datum makeArrayResult(ArrayBuildState *astate, MemoryContext rcontext)
void deconstruct_array(const ArrayType *array, Oid elmtype, int elmlen, bool elmbyval, char elmalign, Datum **elemsp, bool **nullsp, int *nelemsp)
int ArrayGetNItems(int ndim, const int *dims)
Definition arrayutils.c:57
void ArrayCheckBounds(int ndim, const int *dims, const int *lb)
Definition arrayutils.c:117
uint8_t uint8
Definition c.h:622
#define Max(x, y)
Definition c.h:1085
#define VARHDRSZ
Definition c.h:781
#define Assert(condition)
Definition c.h:943
int64_t int64
Definition c.h:621
int16_t int16
Definition c.h:619
int32_t int32
Definition c.h:620
#define OidIsValid(objectId)
Definition c.h:858
uint32 result
memcpy(sums, checksumBaseOffsets, sizeof(checksumBaseOffsets))
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition datum.c:132
Datum arg
Definition elog.c:1322
int errcode(int sqlerrcode)
Definition elog.c:874
int errdetail(const char *fmt,...) pg_attribute_printf(1
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
#define ereport(elevel,...)
Definition elog.h:152
static Datum EOHPGetRWDatum(const struct ExpandedObjectHeader *eohptr)
#define palloc_array(type, count)
Definition fe_memutils.h:76
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition fmgr.c:1151
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition fmgr.c:139
bytea * SendFunctionCall(FmgrInfo *flinfo, Datum val)
Definition fmgr.c:1745
Oid get_fn_expr_argtype(FmgrInfo *flinfo, int argnum)
Definition fmgr.c:1876
Datum ReceiveFunctionCall(FmgrInfo *flinfo, StringInfo buf, Oid typioparam, int32 typmod)
Definition fmgr.c:1698
#define PG_FREE_IF_COPY(ptr, n)
Definition fmgr.h:260
#define PG_GETARG_BYTEA_PP(n)
Definition fmgr.h:309
#define PG_RETURN_BYTEA_P(x)
Definition fmgr.h:373
#define PG_GETARG_POINTER(n)
Definition fmgr.h:277
#define PG_ARGISNULL(n)
Definition fmgr.h:209
#define PG_GETARG_DATUM(n)
Definition fmgr.h:268
#define PG_NARGS()
Definition fmgr.h:203
#define PG_RETURN_NULL()
Definition fmgr.h:346
#define PG_RETURN_INT32(x)
Definition fmgr.h:355
#define PG_GETARG_INT32(n)
Definition fmgr.h:269
#define PG_GETARG_BOOL(n)
Definition fmgr.h:274
#define PG_RETURN_DATUM(x)
Definition fmgr.h:354
#define PG_RETURN_POINTER(x)
Definition fmgr.h:363
#define PG_GET_COLLATION()
Definition fmgr.h:198
#define PG_FUNCTION_ARGS
Definition fmgr.h:193
char * format_type_be(Oid type_oid)
int work_mem
Definition globals.c:133
#define nitems(x)
Definition indent.h:31
static struct @177 value
static bool pg_sub_s32_overflow(int32 a, int32 b, int32 *result)
Definition int.h:169
static bool pg_add_s32_overflow(int32 a, int32 b, int32 *result)
Definition int.h:151
int j
Definition isn.c:78
int i
Definition isn.c:77
void getTypeBinaryOutputInfo(Oid type, Oid *typSend, bool *typIsVarlena)
Definition lsyscache.c:3195
Oid get_element_type(Oid typid)
Definition lsyscache.c:2981
void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval, char *typalign)
Definition lsyscache.c:2491
void getTypeBinaryInputInfo(Oid type, Oid *typReceive, Oid *typIOParam)
Definition lsyscache.c:3162
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition mcxt.c:1232
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition mcxt.c:1266
void * repalloc(void *pointer, Size size)
Definition mcxt.c:1632
void pfree(void *pointer)
Definition mcxt.c:1616
void * palloc0(Size size)
Definition mcxt.c:1417
void * palloc(Size size)
Definition mcxt.c:1387
MemoryContext CurrentMemoryContext
Definition mcxt.c:160
int AggCheckCallContext(FunctionCallInfo fcinfo, MemoryContext *aggcontext)
Definition nodeAgg.c:4609
#define IsA(nodeptr, _type_)
Definition nodes.h:164
static char * errmsg
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:124
static uint32 pg_nextpower2_32(uint32 num)
#define linitial(l)
Definition pg_list.h:178
#define lsecond(l)
Definition pg_list.h:183
uint64 pg_prng_uint64_range(pg_prng_state *state, uint64 rmin, uint64 rmax)
Definition pg_prng.c:144
pg_prng_state pg_global_prng_state
Definition pg_prng.c:34
static char buf[DEFAULT_XLOG_SEG_SIZE]
static bool DatumGetBool(Datum X)
Definition postgres.h:100
uint64_t Datum
Definition postgres.h:70
static Datum Int32GetDatum(int32 X)
Definition postgres.h:212
#define InvalidOid
unsigned int Oid
unsigned int pq_getmsgint(StringInfo msg, int b)
Definition pqformat.c:414
void pq_sendbytes(StringInfo buf, const void *data, int datalen)
Definition pqformat.c:126
void pq_getmsgend(StringInfo msg)
Definition pqformat.c:634
void pq_begintypsend(StringInfo buf)
Definition pqformat.c:325
int pq_getmsgbyte(StringInfo msg)
Definition pqformat.c:398
int64 pq_getmsgint64(StringInfo msg)
Definition pqformat.c:452
const char * pq_getmsgbytes(StringInfo msg, int datalen)
Definition pqformat.c:507
bytea * pq_endtypsend(StringInfo buf)
Definition pqformat.c:345
static void pq_sendint32(StringInfo buf, uint32 i)
Definition pqformat.h:144
static void pq_sendbyte(StringInfo buf, uint8 byt)
Definition pqformat.h:160
static void pq_sendint64(StringInfo buf, uint64 i)
Definition pqformat.h:152
static void pq_sendint16(StringInfo buf, uint16 i)
Definition pqformat.h:136
static int fb(int x)
@ PARAM_EXTERN
Definition primnodes.h:385
static void initReadOnlyStringInfo(StringInfo str, char *data, int len)
Definition stringinfo.h:157
ArrayMetaState array_meta
void * fn_extra
Definition fmgr.h:64
MemoryContext fn_mcxt
Definition fmgr.h:65
Oid fn_oid
Definition fmgr.h:59
FmgrInfo * flinfo
Definition fmgr.h:87
Definition nodes.h:135
FmgrInfo eq_opr_finfo
Definition typcache.h:76
Definition c.h:776
void tuplesort_performsort(Tuplesortstate *state)
Definition tuplesort.c:1260
void tuplesort_end(Tuplesortstate *state)
Definition tuplesort.c:848
#define TUPLESORT_NONE
Definition tuplesort.h:67
void tuplesort_putdatum(Tuplesortstate *state, Datum val, bool isNull)
Tuplesortstate * tuplesort_begin_datum(Oid datumType, Oid sortOperator, Oid sortCollation, bool nullsFirstFlag, int workMem, SortCoordinate coordinate, int sortopt)
bool tuplesort_getdatum(Tuplesortstate *state, bool forward, bool copy, Datum *val, bool *isNull, Datum *abbrev)
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition typcache.c:389
#define TYPECACHE_EQ_OPR_FINFO
Definition typcache.h:143
#define TYPECACHE_GT_OPR
Definition typcache.h:140
#define TYPECACHE_LT_OPR
Definition typcache.h:139
static Size VARSIZE_ANY_EXHDR(const void *PTR)
Definition varatt.h:472
static Size VARSIZE(const void *PTR)
Definition varatt.h:298
static char * VARDATA(const void *PTR)
Definition varatt.h:305
static char * VARDATA_ANY(const void *PTR)
Definition varatt.h:486
static void SET_VARSIZE(void *PTR, Size len)
Definition varatt.h:432