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