PostgreSQL Source Code  git master
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  */
30 typedef struct SerialIOData
31 {
34 
35 /*
36  * DeserialIOData
37  * Used for caching element-type data in array_agg_deserialize
38  */
39 typedef 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  */
63 static ExpandedArrayHeader *
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);
92  MemoryContextSwitchTo(oldcxt);
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))
100  ereport(ERROR,
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))
105  ereport(ERROR,
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  */
122 Datum
124 {
125  ExpandedArrayHeader *eah;
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))
149  ereport(ERROR,
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
156  ereport(ERROR,
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  */
175 Datum
177 {
178  ExpandedArrayHeader *eah;
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))
201  ereport(ERROR,
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
211  ereport(ERROR,
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  */
239 Datum
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))
275  PG_RETURN_NULL();
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)
293  ereport(ERROR,
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)
332  ereport(ERROR,
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])
369  ereport(ERROR,
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])
398  ereport(ERROR,
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])
426  ereport(ERROR,
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  */
478 Datum
480 {
481  Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
482  MemoryContext aggcontext;
484  Datum elem;
485 
486  if (arg1_typeid == InvalidOid)
487  ereport(ERROR,
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 
524 Datum
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)
545  PG_RETURN_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  */
621 Datum
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 
710 Datum
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))
795  ereport(ERROR,
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 
816  pq_getmsgend(&buf);
817 
818  PG_RETURN_POINTER(result);
819 }
820 
821 Datum
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  */
856 Datum
858 {
859  Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
860  MemoryContext aggcontext;
862 
863  if (arg1_typeid == InvalidOid)
864  ereport(ERROR,
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 
887  PG_GETARG_DATUM(1),
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 
900 Datum
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)
921  PG_RETURN_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)
973  ereport(ERROR,
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])
981  ereport(ERROR,
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  */
1049 Datum
1051 {
1054  bytea *result;
1055 
1056  /* cannot be called directly because of internal-type argument */
1057  Assert(AggCheckCallContext(fcinfo, NULL));
1058 
1060 
1061  pq_begintypsend(&buf);
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 
1108 Datum
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 
1191 Datum
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  */
1211  result = makeArrayResultArr(state, CurrentMemoryContext, false);
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  */
1224 Datum
1226 {
1227  return array_position_common(fcinfo);
1228 }
1229 
1230 Datum
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  */
1243 static 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))
1261  PG_RETURN_NULL();
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)
1276  PG_RETURN_NULL();
1277 
1278  if (PG_ARGISNULL(1))
1279  {
1280  /* fast return when the array doesn't have nulls */
1281  if (!array_contains_nulls(array))
1282  PG_RETURN_NULL();
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)
1382  PG_RETURN_NULL();
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  */
1398 Datum
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))
1415  PG_RETURN_NULL();
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  */
1536 static ArrayType *
1537 array_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  */
1625 Datum
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  */
1659 Datum
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  */
1698 static ArrayType *
1699 array_reverse_n(ArrayType *array, Oid elmtyp, TypeCacheEntry *typentry)
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  */
1773 Datum
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)
static ArrayType * array_shuffle_n(ArrayType *array, int n, bool keep_lb, Oid elmtyp, TypeCacheEntry *typentry)
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)
struct SerialIOData SerialIOData
Datum array_agg_combine(PG_FUNCTION_ARGS)
Datum array_reverse(PG_FUNCTION_ARGS)
static ArrayType * array_reverse_n(ArrayType *array, Oid elmtyp, TypeCacheEntry *typentry)
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)
static ExpandedArrayHeader * fetch_array_arg_replace_nulls(FunctionCallInfo fcinfo, int argno)
Datum array_cat(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)
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
ArrayBuildState * initArrayResult(Oid element_type, MemoryContext rcontext, bool subcontext)
Definition: arrayfuncs.c:5293
bool array_iterate(ArrayIterator iterator, Datum *value, bool *isnull)
Definition: arrayfuncs.c:4676
ArrayBuildState * initArrayResultWithSize(Oid element_type, MemoryContext rcontext, bool subcontext, int initsize)
Definition: arrayfuncs.c:5310
void array_free_iterator(ArrayIterator iterator)
Definition: arrayfuncs.c:4759
ArrayBuildStateArr * initArrayResultArr(Oid array_type, Oid element_type, MemoryContext rcontext, bool subcontext)
Definition: arrayfuncs.c:5504
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
ArrayIterator array_create_iterator(ArrayType *arr, int slice_ndim, ArrayMetaState *mstate)
Definition: arrayfuncs.c:4597
ArrayType * construct_empty_array(Oid elmtype)
Definition: arrayfuncs.c:3580
void deconstruct_array(ArrayType *array, Oid elmtype, int elmlen, bool elmbyval, char elmalign, Datum **elemsp, bool **nullsp, int *nelemsp)
Definition: arrayfuncs.c:3631
ArrayBuildStateArr * accumArrayResultArr(ArrayBuildStateArr *astate, Datum dvalue, bool disnull, Oid array_type, MemoryContext rcontext)
Definition: arrayfuncs.c:5550
Datum makeArrayResult(ArrayBuildState *astate, MemoryContext rcontext)
Definition: arrayfuncs.c:5420
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
void array_bitmap_copy(bits8 *destbitmap, int destoffset, const bits8 *srcbitmap, int srcoffset, int nitems)
Definition: arrayfuncs.c:4966
ExpandedArrayHeader * construct_empty_expanded_array(Oid element_type, MemoryContext parentcontext, ArrayMetaState *metacache)
Definition: arrayfuncs.c:3597
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
Oid get_fn_expr_argtype(FmgrInfo *flinfo, int argnum)
Definition: fmgr.c:1910
bytea * SendFunctionCall(FmgrInfo *flinfo, Datum val)
Definition: fmgr.c:1744
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 @160 value
static bool pg_sub_s32_overflow(int32 a, int32 b, int32 *result)
Definition: int.h:153
static bool pg_add_s32_overflow(int32 a, int32 b, int32 *result)
Definition: int.h:135
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 pfree(void *pointer)
Definition: mcxt.c:1521
void * palloc0(Size size)
Definition: mcxt.c:1347
MemoryContext CurrentMemoryContext
Definition: mcxt.c:143
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1541
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:1181
void * palloc(Size size)
Definition: mcxt.c:1317
int AggCheckCallContext(FunctionCallInfo fcinfo, MemoryContext *aggcontext)
Definition: nodeAgg.c:4510
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
const char * pq_getmsgbytes(StringInfo msg, int datalen)
Definition: pqformat.c:508
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
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