Age Owner TLA Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * array_userfuncs.c
4 : * Misc user-visible array support functions
5 : *
6 : * Copyright (c) 2003-2023, 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 "libpq/pqformat.h"
17 : #include "common/int.h"
18 : #include "common/pg_prng.h"
19 : #include "port/pg_bitutils.h"
20 : #include "utils/array.h"
21 : #include "utils/datum.h"
22 : #include "utils/builtins.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 : {
32 : FmgrInfo typsend;
33 : } SerialIOData;
34 :
35 : /*
36 : * DeserialIOData
37 : * Used for caching element-type data in array_agg_deserialize
38 : */
39 : typedef struct DeserialIOData
40 : {
41 : FmgrInfo typreceive;
42 : Oid typioparam;
43 : } DeserialIOData;
44 :
45 : static Datum array_position_common(FunctionCallInfo fcinfo);
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 *
2972 tgl 64 GIC 970 : fetch_array_arg_replace_nulls(FunctionCallInfo fcinfo, int argno)
65 : {
66 : ExpandedArrayHeader *eah;
67 : Oid element_type;
68 : ArrayMetaState *my_extra;
69 : MemoryContext resultcxt;
70 :
71 : /* If first time through, create datatype cache struct */
2887 72 970 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
73 970 : if (my_extra == NULL)
74 : {
75 : my_extra = (ArrayMetaState *)
76 603 : MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
77 : sizeof(ArrayMetaState));
78 603 : my_extra->element_type = InvalidOid;
79 603 : fcinfo->flinfo->fn_extra = my_extra;
80 : }
81 :
82 : /* Figure out which context we want the result in */
2352 83 970 : if (!AggCheckCallContext(fcinfo, &resultcxt))
84 910 : resultcxt = CurrentMemoryContext;
85 :
2887 tgl 86 ECB : /* Now collect the array value */
2965 tgl 87 GIC 970 : if (!PG_ARGISNULL(argno))
88 : {
2352 89 958 : MemoryContext oldcxt = MemoryContextSwitchTo(resultcxt);
90 :
2887 91 958 : eah = PG_GETARG_EXPANDED_ARRAYX(argno, my_extra);
2352 92 958 : MemoryContextSwitchTo(oldcxt);
93 : }
2965 tgl 94 ECB : else
2972 95 : {
96 : /* We have to look up the array type and element type */
2972 tgl 97 GIC 12 : Oid arr_typeid = get_fn_expr_argtype(fcinfo->flinfo, argno);
2972 tgl 98 ECB :
2972 tgl 99 GIC 12 : if (!OidIsValid(arr_typeid))
2972 tgl 100 LBC 0 : ereport(ERROR,
2972 tgl 101 ECB : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
102 : errmsg("could not determine input data type")));
2972 tgl 103 GIC 12 : element_type = get_element_type(arr_typeid);
104 12 : if (!OidIsValid(element_type))
2972 tgl 105 LBC 0 : ereport(ERROR,
2972 tgl 106 ECB : (errcode(ERRCODE_DATATYPE_MISMATCH),
107 : errmsg("input data type is not an array")));
108 :
2887 tgl 109 CBC 12 : eah = construct_empty_expanded_array(element_type,
110 : resultcxt,
2887 tgl 111 ECB : my_extra);
112 : }
2972 113 :
2887 tgl 114 CBC 970 : return eah;
115 : }
116 :
117 : /*-----------------------------------------------------------------------------
118 : * array_append :
2972 tgl 119 ECB : * push an element onto the end of a one-dimensional array
120 : *----------------------------------------------------------------------------
7306 121 : */
7306 tgl 122 EUB : Datum
2972 tgl 123 GIC 931 : array_append(PG_FUNCTION_ARGS)
124 : {
2887 tgl 125 ECB : ExpandedArrayHeader *eah;
7306 126 : Datum newelem;
6352 tgl 127 EUB : bool isNull;
128 : Datum result;
129 : int *dimv,
130 : *lb;
7306 tgl 131 ECB : int indx;
132 : ArrayMetaState *my_extra;
133 :
2887 tgl 134 GIC 931 : eah = fetch_array_arg_replace_nulls(fcinfo, 0);
2972 135 931 : isNull = PG_ARGISNULL(1);
2972 tgl 136 CBC 931 : if (isNull)
2972 tgl 137 UIC 0 : newelem = (Datum) 0;
138 : else
2972 tgl 139 GIC 931 : newelem = PG_GETARG_DATUM(1);
140 :
2887 141 931 : if (eah->ndims == 1)
142 : {
143 : /* append newelem */
144 723 : lb = eah->lbound;
2887 tgl 145 CBC 723 : dimv = eah->dims;
146 :
147 : /* index of added elem is at lb[0] + (dimv[0] - 1) + 1 */
1944 andres 148 GIC 723 : if (pg_add_s32_overflow(lb[0], dimv[0], &indx))
2972 tgl 149 UIC 0 : ereport(ERROR,
150 : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
151 : errmsg("integer out of range")));
152 : }
2887 tgl 153 GIC 208 : else if (eah->ndims == 0)
2972 154 208 : indx = 1;
155 : else
7196 tgl 156 LBC 0 : ereport(ERROR,
2972 tgl 157 ECB : (errcode(ERRCODE_DATA_EXCEPTION),
158 : errmsg("argument must be empty or one-dimensional array")));
2972 tgl 159 EUB :
160 : /* Perform element insertion */
2972 tgl 161 CBC 931 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
162 :
2887 163 931 : result = array_set_element(EOHPGetRWDatum(&eah->hdr),
164 : 1, &indx, newelem, isNull,
2118 tgl 165 GIC 931 : -1, my_extra->typlen, my_extra->typbyval, my_extra->typalign);
2972 tgl 166 ECB :
2887 tgl 167 CBC 931 : PG_RETURN_DATUM(result);
168 : }
169 :
2972 tgl 170 ECB : /*-----------------------------------------------------------------------------
2972 tgl 171 EUB : * array_prepend :
172 : * push an element onto the front of a one-dimensional array
173 : *----------------------------------------------------------------------------
174 : */
2972 tgl 175 ECB : Datum
2972 tgl 176 CBC 39 : array_prepend(PG_FUNCTION_ARGS)
177 : {
2887 tgl 178 EUB : ExpandedArrayHeader *eah;
179 : Datum newelem;
180 : bool isNull;
181 : Datum result;
182 : int *lb;
2972 tgl 183 ECB : int indx;
184 : int lb0;
185 : ArrayMetaState *my_extra;
186 :
2972 tgl 187 CBC 39 : isNull = PG_ARGISNULL(0);
2972 tgl 188 GIC 39 : if (isNull)
2972 tgl 189 LBC 0 : newelem = (Datum) 0;
190 : else
2972 tgl 191 GIC 39 : newelem = PG_GETARG_DATUM(0);
2887 192 39 : eah = fetch_array_arg_replace_nulls(fcinfo, 1);
193 :
194 39 : if (eah->ndims == 1)
195 : {
196 : /* prepend newelem */
197 39 : lb = eah->lbound;
2887 tgl 198 CBC 39 : lb0 = lb[0];
199 :
1944 andres 200 GIC 39 : if (pg_sub_s32_overflow(lb0, 1, &indx))
2972 tgl 201 UIC 0 : ereport(ERROR,
202 : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
203 : errmsg("integer out of range")));
204 : }
2887 205 0 : else if (eah->ndims == 0)
206 : {
7226 207 0 : indx = 1;
2887 208 0 : lb0 = 1;
2887 tgl 209 ECB : }
7229 bruce 210 : else
7196 tgl 211 UBC 0 : ereport(ERROR,
212 : (errcode(ERRCODE_DATA_EXCEPTION),
6385 bruce 213 ECB : errmsg("argument must be empty or one-dimensional array")));
7226 tgl 214 :
215 : /* Perform element insertion */
7226 tgl 216 CBC 39 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
217 :
2887 tgl 218 GIC 39 : result = array_set_element(EOHPGetRWDatum(&eah->hdr),
2887 tgl 219 ECB : 1, &indx, newelem, isNull,
2118 tgl 220 CBC 39 : -1, my_extra->typlen, my_extra->typbyval, my_extra->typalign);
221 :
2972 tgl 222 ECB : /* Readjust result's LB to match the input's, as expected for prepend */
2887 tgl 223 GBC 39 : Assert(result == EOHPGetRWDatum(&eah->hdr));
2887 tgl 224 GIC 39 : if (eah->ndims == 1)
225 : {
226 : /* This is ok whether we've deconstructed or not */
2887 tgl 227 GBC 39 : eah->lbound[0] = lb0;
228 : }
6350 tgl 229 EUB :
2887 tgl 230 GBC 39 : PG_RETURN_DATUM(result);
231 : }
232 :
7306 tgl 233 EUB : /*-----------------------------------------------------------------------------
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 : *----------------------------------------------------------------------------
7306 tgl 238 ECB : */
239 : Datum
7306 tgl 240 CBC 1214 : array_cat(PG_FUNCTION_ARGS)
241 : {
7188 bruce 242 ECB : 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 */
6352 tgl 272 GIC 1214 : if (PG_ARGISNULL(0))
273 : {
274 597 : if (PG_ARGISNULL(1))
6352 tgl 275 UIC 0 : PG_RETURN_NULL();
6352 tgl 276 GIC 597 : result = PG_GETARG_ARRAYTYPE_P(1);
277 597 : PG_RETURN_ARRAYTYPE_P(result);
278 : }
279 617 : if (PG_ARGISNULL(1))
280 : {
6352 tgl 281 UIC 0 : result = PG_GETARG_ARRAYTYPE_P(0);
282 0 : PG_RETURN_ARRAYTYPE_P(result);
283 : }
284 :
7306 tgl 285 GIC 617 : v1 = PG_GETARG_ARRAYTYPE_P(0);
286 617 : v2 = PG_GETARG_ARRAYTYPE_P(1);
287 :
6687 288 617 : element_type1 = ARR_ELEMTYPE(v1);
289 617 : element_type2 = ARR_ELEMTYPE(v2);
290 :
291 : /* Check we have matching element types */
292 617 : if (element_type1 != element_type2)
6687 tgl 293 UIC 0 : ereport(ERROR,
6687 tgl 294 ECB : (errcode(ERRCODE_DATATYPE_MISMATCH),
295 : errmsg("cannot concatenate incompatible arrays"),
296 : errdetail("Arrays with element types %s and %s are not "
6687 tgl 297 EUB : "compatible for concatenation.",
6687 tgl 298 ECB : format_type_be(element_type1),
299 : format_type_be(element_type2))));
300 :
301 : /* OK, use it */
6687 tgl 302 GIC 617 : element_type = element_type1;
6687 tgl 303 EUB :
7175 304 : /*----------
305 : * We must have one of the following combinations of inputs:
306 : * 1) one empty array, and one non-empty array
7175 tgl 307 ECB : * 2) both arrays empty
308 : * 3) two arrays with ndims1 == ndims2
309 : * 4) ndims1 == ndims2 - 1
310 : * 5) ndims1 == ndims2 + 1
311 : *----------
312 : */
7306 tgl 313 GIC 617 : ndims1 = ARR_NDIM(v1);
7306 tgl 314 CBC 617 : ndims2 = ARR_NDIM(v2);
7306 tgl 315 EUB :
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 : */
7226 tgl 322 GIC 617 : if (ndims1 == 0 && ndims2 > 0)
7226 tgl 323 UIC 0 : PG_RETURN_ARRAYTYPE_P(v2);
7226 tgl 324 ECB :
7226 tgl 325 GIC 617 : if (ndims2 == 0)
326 17 : PG_RETURN_ARRAYTYPE_P(v1);
327 :
328 : /* the rest fall under rule 3, 4, or 5 */
7175 329 600 : if (ndims1 != ndims2 &&
330 9 : ndims1 != ndims2 - 1 &&
331 6 : ndims1 != ndims2 + 1)
7196 tgl 332 UIC 0 : ereport(ERROR,
333 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
334 : errmsg("cannot concatenate incompatible arrays"),
7196 tgl 335 ECB : errdetail("Arrays of %d and %d dimensions are not "
7188 bruce 336 : "compatible for concatenation.",
337 : ndims1, ndims2)));
338 :
339 : /* get argument array details */
7306 tgl 340 GIC 600 : lbs1 = ARR_LBOUND(v1);
341 600 : lbs2 = ARR_LBOUND(v2);
342 600 : dims1 = ARR_DIMS(v1);
343 600 : dims2 = ARR_DIMS(v2);
7306 tgl 344 CBC 600 : dat1 = ARR_DATA_PTR(v1);
7306 tgl 345 GBC 600 : dat2 = ARR_DATA_PTR(v2);
6352 tgl 346 GIC 600 : bitmap1 = ARR_NULLBITMAP(v1);
6352 tgl 347 CBC 600 : bitmap2 = ARR_NULLBITMAP(v2);
348 600 : nitems1 = ArrayGetNItems(ndims1, dims1);
6352 tgl 349 GIC 600 : nitems2 = ArrayGetNItems(ndims2, dims2);
350 600 : ndatabytes1 = ARR_SIZE(v1) - ARR_DATA_OFFSET(v1);
6352 tgl 351 CBC 600 : ndatabytes2 = ARR_SIZE(v2) - ARR_DATA_OFFSET(v2);
7306 tgl 352 ECB :
7306 tgl 353 CBC 600 : if (ndims1 == ndims2)
7306 tgl 354 EUB : {
355 : /*
356 : * resulting array is made up of the elements (possibly arrays
357 : * themselves) of the input argument arrays
358 : */
7175 tgl 359 GIC 591 : ndims = ndims1;
7306 360 591 : dims = (int *) palloc(ndims * sizeof(int));
361 591 : lbs = (int *) palloc(ndims * sizeof(int));
7306 tgl 362 ECB :
7175 tgl 363 CBC 591 : dims[0] = dims1[0] + dims2[0];
364 591 : lbs[0] = lbs1[0];
7306 tgl 365 ECB :
7175 tgl 366 CBC 597 : for (i = 1; i < ndims; i++)
7306 tgl 367 ECB : {
7306 tgl 368 CBC 6 : if (dims1[i] != dims2[i] || lbs1[i] != lbs2[i])
7196 tgl 369 LBC 0 : ereport(ERROR,
7196 tgl 370 ECB : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
371 : errmsg("cannot concatenate incompatible arrays"),
2118 372 : errdetail("Arrays with differing element dimensions are "
373 : "not compatible for concatenation.")));
374 :
7175 tgl 375 CBC 6 : dims[i] = dims1[i];
7175 tgl 376 GIC 6 : lbs[i] = lbs1[i];
377 : }
378 : }
7306 379 9 : else if (ndims1 == ndims2 - 1)
380 : {
7306 tgl 381 ECB : /*
6385 bruce 382 : * resulting array has the second argument as the outer array, with
6350 tgl 383 : * the first argument inserted at the front of the outer dimension
384 : */
7306 tgl 385 CBC 3 : ndims = ndims2;
7175 386 3 : dims = (int *) palloc(ndims * sizeof(int));
7175 tgl 387 GIC 3 : lbs = (int *) palloc(ndims * sizeof(int));
7175 tgl 388 CBC 3 : memcpy(dims, dims2, ndims * sizeof(int));
7175 tgl 389 GIC 3 : memcpy(lbs, lbs2, ndims * sizeof(int));
7306 tgl 390 ECB :
7306 tgl 391 EUB : /* increment number of elements in outer array */
7306 tgl 392 GIC 3 : dims[0] += 1;
393 :
394 : /* make sure the added element matches our existing elements */
395 6 : for (i = 0; i < ndims1; i++)
396 : {
7306 tgl 397 CBC 3 : if (dims1[i] != dims[i + 1] || lbs1[i] != lbs[i + 1])
7196 tgl 398 LBC 0 : ereport(ERROR,
399 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
400 : errmsg("cannot concatenate incompatible arrays"),
6385 bruce 401 ECB : errdetail("Arrays with differing dimensions are not "
402 : "compatible for concatenation.")));
403 : }
404 : }
405 : else
406 : {
7306 tgl 407 : /*
7175 408 : * (ndims1 == ndims2 + 1)
409 : *
6385 bruce 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 : */
7306 tgl 413 GIC 6 : ndims = ndims1;
7175 tgl 414 CBC 6 : dims = (int *) palloc(ndims * sizeof(int));
7175 tgl 415 GIC 6 : lbs = (int *) palloc(ndims * sizeof(int));
416 6 : memcpy(dims, dims1, ndims * sizeof(int));
7175 tgl 417 CBC 6 : memcpy(lbs, lbs1, ndims * sizeof(int));
418 :
7306 tgl 419 ECB : /* increment number of elements in outer array */
7306 tgl 420 GBC 6 : dims[0] += 1;
421 :
422 : /* make sure the added element matches our existing elements */
7306 tgl 423 GIC 12 : for (i = 0; i < ndims2; i++)
424 : {
425 6 : if (dims2[i] != dims[i + 1] || lbs2[i] != lbs[i + 1])
7196 tgl 426 UIC 0 : 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 */
6352 tgl 435 CBC 600 : nitems = ArrayGetNItems(ndims, dims);
699 436 600 : ArrayCheckBounds(ndims, dims, lbs);
6352 tgl 437 ECB :
7306 438 : /* build the result array */
7306 tgl 439 CBC 600 : ndatabytes = ndatabytes1 + ndatabytes2;
6352 tgl 440 GIC 600 : if (ARR_HASNULL(v1) || ARR_HASNULL(v2))
441 : {
6352 tgl 442 LBC 0 : dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
6352 tgl 443 UIC 0 : nbytes = ndatabytes + dataoffset;
444 : }
6352 tgl 445 ECB : else
446 : {
6352 tgl 447 CBC 600 : dataoffset = 0; /* marker for no null bitmap */
6352 tgl 448 GBC 600 : nbytes = ndatabytes + ARR_OVERHEAD_NONULLS(ndims);
449 : }
4365 tgl 450 GIC 600 : result = (ArrayType *) palloc0(nbytes);
5885 451 600 : SET_VARSIZE(result, nbytes);
7306 452 600 : result->ndim = ndims;
6352 453 600 : result->dataoffset = dataoffset;
7306 454 600 : result->elemtype = element_type;
455 600 : memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
456 600 : memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
7306 tgl 457 ECB : /* data area is arg1 then arg2 */
7306 tgl 458 CBC 600 : memcpy(ARR_DATA_PTR(result), dat1, ndatabytes1);
7306 tgl 459 GIC 600 : memcpy(ARR_DATA_PTR(result) + ndatabytes1, dat2, ndatabytes2);
460 : /* handle the null bitmap if needed */
6352 tgl 461 CBC 600 : if (ARR_HASNULL(result))
6352 tgl 462 ECB : {
6352 tgl 463 UIC 0 : array_bitmap_copy(ARR_NULLBITMAP(result), 0,
6352 tgl 464 EUB : bitmap1, 0,
465 : nitems1);
6352 tgl 466 UIC 0 : array_bitmap_copy(ARR_NULLBITMAP(result), nitems1,
467 : bitmap2, 0,
468 : nitems2);
6352 tgl 469 ECB : }
7306 470 :
7306 tgl 471 GIC 600 : PG_RETURN_ARRAYTYPE_P(result);
7306 tgl 472 ECB : }
473 :
474 :
5259 475 : /*
3057 476 : * ARRAY_AGG(anynonarray) aggregate function
5259 477 : */
5260 peter_e 478 : Datum
5260 peter_e 479 GIC 862422 : array_agg_transfn(PG_FUNCTION_ARGS)
5260 peter_e 480 ECB : {
5050 bruce 481 CBC 862422 : Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
482 : MemoryContext aggcontext;
5259 tgl 483 ECB : ArrayBuildState *state;
484 : Datum elem;
5260 peter_e 485 EUB :
5260 peter_e 486 GIC 862422 : if (arg1_typeid == InvalidOid)
5259 tgl 487 UIC 0 : ereport(ERROR,
5259 tgl 488 EUB : (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
3057 tgl 493 ECB : * valid array element type, because the parser would have verified that
494 : * while resolving the input/result types of this polymorphic aggregate.
495 : */
496 :
4808 tgl 497 GIC 862422 : if (!AggCheckCallContext(fcinfo, &aggcontext))
498 : {
499 : /* cannot be called directly because of internal-type argument */
5215 tgl 500 UIC 0 : elog(ERROR, "array_agg_transfn called in non-aggregate context");
5215 tgl 501 ECB : }
502 :
2969 jdavis 503 CBC 862422 : if (PG_ARGISNULL(0))
2969 jdavis 504 GIC 50616 : state = initArrayResult(arg1_typeid, aggcontext, false);
505 : else
506 811806 : state = (ArrayBuildState *) PG_GETARG_POINTER(0);
507 :
5259 tgl 508 CBC 862422 : elem = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1);
2969 jdavis 509 EUB :
5259 tgl 510 GIC 862422 : state = accumArrayResult(state,
511 : elem,
512 862422 : 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
5050 bruce 519 ECB : * pass the ArrayBuildState pointer through nodeAgg.c's machinations.
520 : */
5259 tgl 521 GIC 862422 : PG_RETURN_POINTER(state);
5260 peter_e 522 EUB : }
523 :
524 : Datum
76 drowley 525 GNC 50 : array_agg_combine(PG_FUNCTION_ARGS)
526 : {
527 : ArrayBuildState *state1;
528 : ArrayBuildState *state2;
529 : MemoryContext agg_context;
530 : MemoryContext old_context;
531 :
532 50 : if (!AggCheckCallContext(fcinfo, &agg_context))
76 drowley 533 UNC 0 : elog(ERROR, "aggregate function called in non-aggregate context");
534 :
76 drowley 535 GNC 50 : state1 = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
536 50 : state2 = PG_ARGISNULL(1) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(1);
537 :
538 50 : if (state2 == NULL)
539 : {
540 : /*
541 : * NULL state2 is easy, just return state1, which we know is already
542 : * in the agg_context
543 : */
76 drowley 544 UNC 0 : if (state1 == NULL)
545 0 : PG_RETURN_NULL();
546 0 : PG_RETURN_POINTER(state1);
547 : }
548 :
76 drowley 549 GNC 50 : if (state1 == NULL)
550 : {
551 : /* We must copy state2's data into the agg_context */
552 30 : state1 = initArrayResultWithSize(state2->element_type, agg_context,
553 : false, state2->alen);
554 :
555 30 : old_context = MemoryContextSwitchTo(agg_context);
556 :
557 8274 : for (int i = 0; i < state2->nelems; i++)
558 : {
559 8244 : if (!state2->dnulls[i])
560 6271 : state1->dvalues[i] = datumCopy(state2->dvalues[i],
561 6271 : state1->typbyval,
562 6271 : state1->typlen);
563 : else
564 1973 : state1->dvalues[i] = (Datum) 0;
565 : }
566 :
567 30 : MemoryContextSwitchTo(old_context);
568 :
569 30 : memcpy(state1->dnulls, state2->dnulls, sizeof(bool) * state2->nelems);
570 :
571 30 : state1->nelems = state2->nelems;
572 :
573 30 : PG_RETURN_POINTER(state1);
574 : }
575 20 : else if (state2->nelems > 0)
576 : {
577 : /* We only need to combine the two states if state2 has any elements */
578 20 : int reqsize = state1->nelems + state2->nelems;
579 20 : MemoryContext oldContext = MemoryContextSwitchTo(state1->mcontext);
580 :
581 20 : Assert(state1->element_type == state2->element_type);
582 :
583 : /* Enlarge state1 arrays if needed */
584 20 : if (state1->alen < reqsize)
585 : {
586 : /* Use a power of 2 size rather than allocating just reqsize */
587 20 : state1->alen = pg_nextpower2_32(reqsize);
588 40 : state1->dvalues = (Datum *) repalloc(state1->dvalues,
589 20 : state1->alen * sizeof(Datum));
590 20 : state1->dnulls = (bool *) repalloc(state1->dnulls,
591 20 : state1->alen * sizeof(bool));
592 : }
593 :
594 : /* Copy in the state2 elements to the end of the state1 arrays */
595 6776 : for (int i = 0; i < state2->nelems; i++)
596 : {
597 6756 : if (!state2->dnulls[i])
598 4979 : state1->dvalues[i + state1->nelems] =
599 4979 : datumCopy(state2->dvalues[i],
600 4979 : state1->typbyval,
601 4979 : state1->typlen);
602 : else
603 1777 : state1->dvalues[i + state1->nelems] = (Datum) 0;
604 : }
605 :
606 20 : memcpy(&state1->dnulls[state1->nelems], state2->dnulls,
607 20 : sizeof(bool) * state2->nelems);
608 :
609 20 : state1->nelems = reqsize;
610 :
611 20 : MemoryContextSwitchTo(oldContext);
612 : }
613 :
614 20 : PG_RETURN_POINTER(state1);
615 : }
616 :
617 : /*
618 : * array_agg_serialize
619 : * Serialize ArrayBuildState into bytea.
620 : */
621 : Datum
622 50 : array_agg_serialize(PG_FUNCTION_ARGS)
623 : {
624 : ArrayBuildState *state;
625 : StringInfoData buf;
626 : bytea *result;
627 :
628 : /* cannot be called directly because of internal-type argument */
629 50 : Assert(AggCheckCallContext(fcinfo, NULL));
630 :
631 50 : state = (ArrayBuildState *) PG_GETARG_POINTER(0);
632 :
633 50 : pq_begintypsend(&buf);
634 :
635 : /*
636 : * element_type. Putting this first is more convenient in deserialization
637 : */
638 50 : 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 50 : pq_sendint64(&buf, state->nelems);
645 :
646 : /* alen can be decided during deserialization */
647 :
648 : /* typlen */
649 50 : pq_sendint16(&buf, state->typlen);
650 :
651 : /* typbyval */
652 50 : pq_sendbyte(&buf, state->typbyval);
653 :
654 : /* typalign */
655 50 : pq_sendbyte(&buf, state->typalign);
656 :
657 : /* dnulls */
54 peter 658 50 : 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 : */
76 drowley 667 50 : if (state->typbyval)
54 peter 668 50 : 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 */
76 drowley 675 UNC 0 : iodata = (SerialIOData *) fcinfo->flinfo->fn_extra;
676 0 : if (iodata == NULL)
677 : {
678 : Oid typsend;
679 : bool typisvarlena;
680 :
681 : iodata = (SerialIOData *)
682 0 : MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
683 : sizeof(SerialIOData));
684 0 : getTypeBinaryOutputInfo(state->element_type, &typsend,
685 : &typisvarlena);
686 0 : fmgr_info_cxt(typsend, &iodata->typsend,
687 0 : fcinfo->flinfo->fn_mcxt);
688 0 : fcinfo->flinfo->fn_extra = (void *) iodata;
689 : }
690 :
691 0 : for (i = 0; i < state->nelems; i++)
692 : {
693 : bytea *outputbytes;
694 :
695 0 : if (state->dnulls[i])
696 0 : continue;
697 0 : outputbytes = SendFunctionCall(&iodata->typsend,
698 0 : state->dvalues[i]);
699 0 : pq_sendint32(&buf, VARSIZE(outputbytes) - VARHDRSZ);
700 0 : pq_sendbytes(&buf, VARDATA(outputbytes),
701 0 : VARSIZE(outputbytes) - VARHDRSZ);
702 : }
703 : }
704 :
76 drowley 705 GNC 50 : result = pq_endtypsend(&buf);
706 :
707 50 : PG_RETURN_BYTEA_P(result);
708 : }
709 :
710 : Datum
711 50 : array_agg_deserialize(PG_FUNCTION_ARGS)
712 : {
713 : bytea *sstate;
714 : ArrayBuildState *result;
715 : StringInfoData buf;
716 : Oid element_type;
717 : int64 nelems;
718 : const char *temp;
719 :
720 50 : if (!AggCheckCallContext(fcinfo, NULL))
76 drowley 721 UNC 0 : elog(ERROR, "aggregate function called in non-aggregate context");
722 :
76 drowley 723 GNC 50 : sstate = PG_GETARG_BYTEA_PP(0);
724 :
725 : /*
726 : * Copy the bytea into a StringInfo so that we can "receive" it using the
727 : * standard recv-function infrastructure.
728 : */
729 50 : initStringInfo(&buf);
730 100 : appendBinaryStringInfo(&buf,
731 100 : VARDATA_ANY(sstate), VARSIZE_ANY_EXHDR(sstate));
732 :
733 : /* element_type */
734 50 : element_type = pq_getmsgint(&buf, 4);
735 :
736 : /* nelems */
737 50 : nelems = pq_getmsgint64(&buf);
738 :
739 : /* Create output ArrayBuildState with the needed number of elements */
740 50 : result = initArrayResultWithSize(element_type, CurrentMemoryContext,
741 : false, nelems);
742 50 : result->nelems = nelems;
743 :
744 : /* typlen */
745 50 : result->typlen = pq_getmsgint(&buf, 2);
746 :
747 : /* typbyval */
748 50 : result->typbyval = pq_getmsgbyte(&buf);
749 :
750 : /* typalign */
751 50 : result->typalign = pq_getmsgbyte(&buf);
752 :
753 : /* dnulls */
754 50 : temp = pq_getmsgbytes(&buf, sizeof(bool) * nelems);
755 50 : memcpy(result->dnulls, temp, sizeof(bool) * nelems);
756 :
757 : /* dvalues --- see comment in array_agg_serialize */
758 50 : if (result->typbyval)
759 : {
760 50 : temp = pq_getmsgbytes(&buf, sizeof(Datum) * nelems);
761 50 : memcpy(result->dvalues, temp, sizeof(Datum) * nelems);
762 : }
763 : else
764 : {
765 : DeserialIOData *iodata;
766 :
767 : /* Avoid repeat catalog lookups for typreceive function */
76 drowley 768 UNC 0 : iodata = (DeserialIOData *) fcinfo->flinfo->fn_extra;
769 0 : if (iodata == NULL)
770 : {
771 : Oid typreceive;
772 :
773 : iodata = (DeserialIOData *)
774 0 : MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
775 : sizeof(DeserialIOData));
776 0 : getTypeBinaryInputInfo(element_type, &typreceive,
777 : &iodata->typioparam);
778 0 : fmgr_info_cxt(typreceive, &iodata->typreceive,
779 0 : fcinfo->flinfo->fn_mcxt);
780 0 : fcinfo->flinfo->fn_extra = (void *) iodata;
781 : }
782 :
783 0 : for (int i = 0; i < nelems; i++)
784 : {
785 : int itemlen;
786 : StringInfoData elem_buf;
787 : char csave;
788 :
789 0 : if (result->dnulls[i])
790 : {
791 0 : result->dvalues[i] = (Datum) 0;
792 0 : continue;
793 : }
794 :
795 0 : itemlen = pq_getmsgint(&buf, 4);
796 0 : if (itemlen < 0 || itemlen > (buf.len - buf.cursor))
797 0 : ereport(ERROR,
798 : (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
799 : errmsg("insufficient data left in message")));
800 :
801 : /*
802 : * Rather than copying data around, we just set up a phony
803 : * StringInfo pointing to the correct portion of the input buffer.
804 : * We assume we can scribble on the input buffer so as to maintain
805 : * the convention that StringInfos have a trailing null.
806 : */
807 0 : elem_buf.data = &buf.data[buf.cursor];
808 0 : elem_buf.maxlen = itemlen + 1;
809 0 : elem_buf.len = itemlen;
810 0 : elem_buf.cursor = 0;
811 :
812 0 : buf.cursor += itemlen;
813 :
814 0 : csave = buf.data[buf.cursor];
815 0 : buf.data[buf.cursor] = '\0';
816 :
817 : /* Now call the element's receiveproc */
818 0 : result->dvalues[i] = ReceiveFunctionCall(&iodata->typreceive,
819 : &elem_buf,
820 : iodata->typioparam,
821 : -1);
822 :
823 0 : buf.data[buf.cursor] = csave;
824 : }
825 : }
826 :
76 drowley 827 GNC 50 : pq_getmsgend(&buf);
828 50 : pfree(buf.data);
829 :
830 50 : PG_RETURN_POINTER(result);
831 : }
832 :
833 : Datum
5260 peter_e 834 CBC 54235 : array_agg_finalfn(PG_FUNCTION_ARGS)
5260 peter_e 835 ECB : {
836 : Datum result;
5259 tgl 837 : ArrayBuildState *state;
838 : int dims[1];
5215 839 : int lbs[1];
840 :
5260 peter_e 841 : /* cannot be called directly because of internal-type argument */
4808 tgl 842 GIC 54235 : Assert(AggCheckCallContext(fcinfo, NULL));
5260 peter_e 843 ECB :
3057 tgl 844 GIC 54235 : state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
845 :
846 54235 : if (state == NULL)
847 3628 : PG_RETURN_NULL(); /* returns null iff no input values */
848 :
5215 849 50607 : dims[0] = state->nelems;
850 50607 : lbs[0] = 1;
851 :
5041 tgl 852 ECB : /*
853 : * Make the result. We cannot release the ArrayBuildState because
854 : * sometimes aggregate final functions are re-executed. Rather, it is
855 : * nodeAgg.c's responsibility to reset the aggcontext when it's safe to do
4790 bruce 856 : * so.
857 : */
5215 tgl 858 GIC 50607 : result = makeMdArrayResult(state, 1, dims, lbs,
859 : CurrentMemoryContext,
860 : false);
861 :
862 50607 : PG_RETURN_DATUM(result);
5260 peter_e 863 ECB : }
3057 tgl 864 EUB :
865 : /*
3057 tgl 866 ECB : * ARRAY_AGG(anyarray) aggregate function
867 : */
868 : Datum
3057 tgl 869 CBC 30075 : array_agg_array_transfn(PG_FUNCTION_ARGS)
870 : {
3057 tgl 871 GIC 30075 : Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
872 : MemoryContext aggcontext;
873 : ArrayBuildStateArr *state;
874 :
3057 tgl 875 GBC 30075 : if (arg1_typeid == InvalidOid)
3057 tgl 876 UBC 0 : ereport(ERROR,
3057 tgl 877 EUB : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
878 : errmsg("could not determine input data type")));
879 :
3057 tgl 880 ECB : /*
881 : * Note: we do not need a run-time check about whether arg1_typeid is a
882 : * valid array type, because the parser would have verified that while
883 : * resolving the input/result types of this polymorphic aggregate.
884 : */
885 :
3057 tgl 886 CBC 30075 : if (!AggCheckCallContext(fcinfo, &aggcontext))
887 : {
3057 tgl 888 ECB : /* cannot be called directly because of internal-type argument */
3057 tgl 889 UIC 0 : elog(ERROR, "array_agg_array_transfn called in non-aggregate context");
3057 tgl 890 ECB : }
891 :
2969 jdavis 892 :
2969 jdavis 893 CBC 30075 : if (PG_ARGISNULL(0))
2969 jdavis 894 GIC 110 : state = initArrayResultArr(arg1_typeid, InvalidOid, aggcontext, false);
2969 jdavis 895 ECB : else
2969 jdavis 896 GIC 29965 : state = (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
897 :
3057 tgl 898 CBC 30075 : state = accumArrayResultArr(state,
899 : PG_GETARG_DATUM(1),
900 30075 : PG_ARGISNULL(1),
901 : arg1_typeid,
3057 tgl 902 ECB : aggcontext);
903 :
904 : /*
905 : * The transition type for array_agg() is declared to be "internal", which
906 : * is a pass-by-value type the same size as a pointer. So we can safely
907 : * pass the ArrayBuildStateArr pointer through nodeAgg.c's machinations.
908 : */
3057 tgl 909 CBC 30066 : PG_RETURN_POINTER(state);
3057 tgl 910 ECB : }
911 :
912 : Datum
76 drowley 913 GNC 50 : array_agg_array_combine(PG_FUNCTION_ARGS)
914 : {
915 : ArrayBuildStateArr *state1;
916 : ArrayBuildStateArr *state2;
917 : MemoryContext agg_context;
918 : MemoryContext old_context;
919 :
920 50 : if (!AggCheckCallContext(fcinfo, &agg_context))
76 drowley 921 UNC 0 : elog(ERROR, "aggregate function called in non-aggregate context");
922 :
76 drowley 923 GNC 50 : state1 = PG_ARGISNULL(0) ? NULL : (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
924 50 : state2 = PG_ARGISNULL(1) ? NULL : (ArrayBuildStateArr *) PG_GETARG_POINTER(1);
925 :
926 50 : if (state2 == NULL)
927 : {
928 : /*
929 : * NULL state2 is easy, just return state1, which we know is already
930 : * in the agg_context
931 : */
76 drowley 932 UNC 0 : if (state1 == NULL)
933 0 : PG_RETURN_NULL();
934 0 : PG_RETURN_POINTER(state1);
935 : }
936 :
76 drowley 937 GNC 50 : if (state1 == NULL)
938 : {
939 : /* We must copy state2's data into the agg_context */
940 30 : old_context = MemoryContextSwitchTo(agg_context);
941 :
942 30 : state1 = initArrayResultArr(state2->array_type, InvalidOid,
943 : agg_context, false);
944 :
945 30 : state1->abytes = state2->abytes;
946 30 : state1->data = (char *) palloc(state1->abytes);
947 :
948 30 : if (state2->nullbitmap)
949 : {
950 15 : int size = (state2->aitems + 7) / 8;
951 :
952 15 : state1->nullbitmap = (bits8 *) palloc(size);
953 15 : memcpy(state1->nullbitmap, state2->nullbitmap, size);
954 : }
955 :
956 30 : memcpy(state1->data, state2->data, state2->nbytes);
957 30 : state1->nbytes = state2->nbytes;
958 30 : state1->aitems = state2->aitems;
959 30 : state1->nitems = state2->nitems;
960 30 : state1->ndims = state2->ndims;
961 30 : memcpy(state1->dims, state2->dims, sizeof(state2->dims));
962 30 : memcpy(state1->lbs, state2->lbs, sizeof(state2->lbs));
963 30 : state1->array_type = state2->array_type;
964 30 : state1->element_type = state2->element_type;
965 :
966 30 : MemoryContextSwitchTo(old_context);
967 :
968 30 : PG_RETURN_POINTER(state1);
969 : }
970 :
971 : /* We only need to combine the two states if state2 has any items */
972 20 : else if (state2->nitems > 0)
973 : {
974 : MemoryContext oldContext;
975 20 : int reqsize = state1->nbytes + state2->nbytes;
976 : int i;
977 :
978 : /*
979 : * Check the states are compatible with each other. Ensure we use the
980 : * same error messages that are listed in accumArrayResultArr so that
981 : * the same error is shown as would have been if we'd not used the
982 : * combine function for the aggregation.
983 : */
984 20 : if (state1->ndims != state2->ndims)
76 drowley 985 UNC 0 : ereport(ERROR,
986 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
987 : errmsg("cannot accumulate arrays of different dimensionality")));
988 :
989 : /* Check dimensions match ignoring the first dimension. */
76 drowley 990 GNC 40 : for (i = 1; i < state1->ndims; i++)
991 : {
992 20 : if (state1->dims[i] != state2->dims[i] || state1->lbs[i] != state2->lbs[i])
76 drowley 993 UNC 0 : ereport(ERROR,
994 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
995 : errmsg("cannot accumulate arrays of different dimensionality")));
996 : }
997 :
998 :
76 drowley 999 GNC 20 : oldContext = MemoryContextSwitchTo(state1->mcontext);
1000 :
1001 : /*
1002 : * If there's not enough space in state1 then we'll need to reallocate
1003 : * more.
1004 : */
1005 20 : if (state1->abytes < reqsize)
1006 : {
1007 : /* use a power of 2 size rather than allocating just reqsize */
1008 6 : state1->abytes = pg_nextpower2_32(reqsize);
1009 6 : state1->data = (char *) repalloc(state1->data, state1->abytes);
1010 : }
1011 :
1012 20 : if (state2->nullbitmap)
1013 : {
1014 10 : int newnitems = state1->nitems + state2->nitems;
1015 :
1016 10 : if (state1->nullbitmap == NULL)
1017 : {
1018 : /*
1019 : * First input with nulls; we must retrospectively handle any
1020 : * previous inputs by marking all their items non-null.
1021 : */
76 drowley 1022 UNC 0 : state1->aitems = pg_nextpower2_32(Max(256, newnitems + 1));
1023 0 : state1->nullbitmap = (bits8 *) palloc((state1->aitems + 7) / 8);
1024 0 : array_bitmap_copy(state1->nullbitmap, 0,
1025 : NULL, 0,
1026 : state1->nitems);
1027 : }
76 drowley 1028 GNC 10 : else if (newnitems > state1->aitems)
1029 : {
1030 7 : int newaitems = state1->aitems + state2->aitems;
1031 :
1032 7 : state1->aitems = pg_nextpower2_32(newaitems);
1033 7 : state1->nullbitmap = (bits8 *)
1034 7 : repalloc(state1->nullbitmap, (state1->aitems + 7) / 8);
1035 : }
1036 10 : array_bitmap_copy(state1->nullbitmap, state1->nitems,
1037 10 : state2->nullbitmap, 0,
1038 : state2->nitems);
1039 : }
1040 :
1041 20 : memcpy(state1->data + state1->nbytes, state2->data, state2->nbytes);
1042 20 : state1->nbytes += state2->nbytes;
1043 20 : state1->nitems += state2->nitems;
1044 :
1045 20 : state1->dims[0] += state2->dims[0];
1046 : /* remaing dims already match, per test above */
1047 :
1048 20 : Assert(state1->array_type == state2->array_type);
1049 20 : Assert(state1->element_type == state2->element_type);
1050 :
1051 20 : MemoryContextSwitchTo(oldContext);
1052 : }
1053 :
1054 20 : PG_RETURN_POINTER(state1);
1055 : }
1056 :
1057 : /*
1058 : * array_agg_array_serialize
1059 : * Serialize ArrayBuildStateArr into bytea.
1060 : */
1061 : Datum
1062 50 : array_agg_array_serialize(PG_FUNCTION_ARGS)
1063 : {
1064 : ArrayBuildStateArr *state;
1065 : StringInfoData buf;
1066 : bytea *result;
1067 :
1068 : /* cannot be called directly because of internal-type argument */
1069 50 : Assert(AggCheckCallContext(fcinfo, NULL));
1070 :
1071 50 : state = (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
1072 :
1073 50 : pq_begintypsend(&buf);
1074 :
1075 : /*
1076 : * element_type. Putting this first is more convenient in deserialization
1077 : * so that we can init the new state sooner.
1078 : */
1079 50 : pq_sendint32(&buf, state->element_type);
1080 :
1081 : /* array_type */
1082 50 : pq_sendint32(&buf, state->array_type);
1083 :
1084 : /* nbytes */
1085 50 : pq_sendint32(&buf, state->nbytes);
1086 :
1087 : /* data */
1088 50 : pq_sendbytes(&buf, state->data, state->nbytes);
1089 :
1090 : /* abytes */
1091 50 : pq_sendint32(&buf, state->abytes);
1092 :
1093 : /* aitems */
1094 50 : pq_sendint32(&buf, state->aitems);
1095 :
1096 : /* nullbitmap */
1097 50 : if (state->nullbitmap)
1098 : {
1099 25 : Assert(state->aitems > 0);
54 peter 1100 25 : pq_sendbytes(&buf, state->nullbitmap, (state->aitems + 7) / 8);
1101 : }
1102 :
1103 : /* nitems */
76 drowley 1104 50 : pq_sendint32(&buf, state->nitems);
1105 :
1106 : /* ndims */
1107 50 : pq_sendint32(&buf, state->ndims);
1108 :
1109 : /* dims: XXX should we just send ndims elements? */
54 peter 1110 50 : pq_sendbytes(&buf, state->dims, sizeof(state->dims));
1111 :
1112 : /* lbs */
1113 50 : pq_sendbytes(&buf, state->lbs, sizeof(state->lbs));
1114 :
76 drowley 1115 50 : result = pq_endtypsend(&buf);
1116 :
1117 50 : PG_RETURN_BYTEA_P(result);
1118 : }
1119 :
1120 : Datum
1121 50 : array_agg_array_deserialize(PG_FUNCTION_ARGS)
1122 : {
1123 : bytea *sstate;
1124 : ArrayBuildStateArr *result;
1125 : StringInfoData buf;
1126 : Oid element_type;
1127 : Oid array_type;
1128 : int nbytes;
1129 : const char *temp;
1130 :
1131 : /* cannot be called directly because of internal-type argument */
1132 50 : Assert(AggCheckCallContext(fcinfo, NULL));
1133 :
1134 50 : sstate = PG_GETARG_BYTEA_PP(0);
1135 :
1136 : /*
1137 : * Copy the bytea into a StringInfo so that we can "receive" it using the
1138 : * standard recv-function infrastructure.
1139 : */
1140 50 : initStringInfo(&buf);
1141 100 : appendBinaryStringInfo(&buf,
1142 100 : VARDATA_ANY(sstate), VARSIZE_ANY_EXHDR(sstate));
1143 :
1144 : /* element_type */
1145 50 : element_type = pq_getmsgint(&buf, 4);
1146 :
1147 : /* array_type */
1148 50 : array_type = pq_getmsgint(&buf, 4);
1149 :
1150 : /* nbytes */
1151 50 : nbytes = pq_getmsgint(&buf, 4);
1152 :
1153 50 : result = initArrayResultArr(array_type, element_type,
1154 : CurrentMemoryContext, false);
1155 :
1156 50 : result->abytes = 1024;
1157 65 : while (result->abytes < nbytes)
1158 15 : result->abytes *= 2;
1159 :
1160 50 : result->data = (char *) palloc(result->abytes);
1161 :
1162 : /* data */
1163 50 : temp = pq_getmsgbytes(&buf, nbytes);
1164 50 : memcpy(result->data, temp, nbytes);
1165 50 : result->nbytes = nbytes;
1166 :
1167 : /* abytes */
1168 50 : result->abytes = pq_getmsgint(&buf, 4);
1169 :
1170 : /* aitems: might be 0 */
1171 50 : result->aitems = pq_getmsgint(&buf, 4);
1172 :
1173 : /* nullbitmap */
1174 50 : if (result->aitems > 0)
1175 : {
1176 25 : int size = (result->aitems + 7) / 8;
1177 :
1178 25 : result->nullbitmap = (bits8 *) palloc(size);
1179 25 : temp = pq_getmsgbytes(&buf, size);
1180 25 : memcpy(result->nullbitmap, temp, size);
1181 : }
1182 : else
1183 25 : result->nullbitmap = NULL;
1184 :
1185 : /* nitems */
1186 50 : result->nitems = pq_getmsgint(&buf, 4);
1187 :
1188 : /* ndims */
1189 50 : result->ndims = pq_getmsgint(&buf, 4);
1190 :
1191 : /* dims */
1192 50 : temp = pq_getmsgbytes(&buf, sizeof(result->dims));
1193 50 : memcpy(result->dims, temp, sizeof(result->dims));
1194 :
1195 : /* lbs */
1196 50 : temp = pq_getmsgbytes(&buf, sizeof(result->lbs));
1197 50 : memcpy(result->lbs, temp, sizeof(result->lbs));
1198 :
1199 50 : pq_getmsgend(&buf);
1200 50 : pfree(buf.data);
1201 :
1202 50 : PG_RETURN_POINTER(result);
1203 : }
1204 :
3057 tgl 1205 ECB : Datum
3057 tgl 1206 GIC 81 : array_agg_array_finalfn(PG_FUNCTION_ARGS)
1207 : {
3057 tgl 1208 ECB : Datum result;
1209 : ArrayBuildStateArr *state;
1210 :
1211 : /* cannot be called directly because of internal-type argument */
3057 tgl 1212 CBC 81 : Assert(AggCheckCallContext(fcinfo, NULL));
3057 tgl 1213 ECB :
3057 tgl 1214 CBC 81 : state = PG_ARGISNULL(0) ? NULL : (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
3057 tgl 1215 ECB :
3057 tgl 1216 GIC 81 : if (state == NULL)
3057 tgl 1217 UIC 0 : PG_RETURN_NULL(); /* returns null iff no input values */
1218 :
3057 tgl 1219 ECB : /*
1220 : * Make the result. We cannot release the ArrayBuildStateArr because
1221 : * sometimes aggregate final functions are re-executed. Rather, it is
1222 : * nodeAgg.c's responsibility to reset the aggcontext when it's safe to do
1223 : * so.
1224 : */
3057 tgl 1225 CBC 81 : result = makeArrayResultArr(state, CurrentMemoryContext, false);
1226 :
1227 81 : PG_RETURN_DATUM(result);
1228 : }
1229 :
2944 alvherre 1230 ECB : /*-----------------------------------------------------------------------------
2932 1231 : * array_position, array_position_start :
1232 : * return the offset of a value in an array.
2944 1233 : *
1234 : * IS NOT DISTINCT FROM semantics are used for comparisons. Return NULL when
1235 : * the value is not found.
1236 : *-----------------------------------------------------------------------------
1237 : */
1238 : Datum
2932 alvherre 1239 GIC 108 : array_position(PG_FUNCTION_ARGS)
1240 : {
1241 108 : return array_position_common(fcinfo);
1242 : }
1243 :
1244 : Datum
1245 9 : array_position_start(PG_FUNCTION_ARGS)
2944 alvherre 1246 ECB : {
2932 alvherre 1247 GIC 9 : return array_position_common(fcinfo);
1248 : }
1249 :
1250 : /*
1251 : * array_position_common
1252 : * Common code for array_position and array_position_start
2944 alvherre 1253 ECB : *
1254 : * These are separate wrappers for the sake of opr_sanity regression test.
1255 : * They are not strict so we have to test for null inputs explicitly.
1256 : */
1257 : static Datum
2932 alvherre 1258 GIC 117 : array_position_common(FunctionCallInfo fcinfo)
1259 : {
1260 : ArrayType *array;
2944 1261 117 : Oid collation = PG_GET_COLLATION();
2944 alvherre 1262 ECB : Oid element_type;
1263 : Datum searched_element,
1264 : value;
1265 : bool isnull;
1266 : int position,
1267 : position_min;
2944 alvherre 1268 CBC 117 : bool found = false;
1269 : TypeCacheEntry *typentry;
1270 : ArrayMetaState *my_extra;
1271 : bool null_search;
1272 : ArrayIterator array_iterator;
2944 alvherre 1273 ECB :
2944 alvherre 1274 GIC 117 : if (PG_ARGISNULL(0))
2944 alvherre 1275 UIC 0 : PG_RETURN_NULL();
2944 alvherre 1276 ECB :
2944 alvherre 1277 GIC 117 : array = PG_GETARG_ARRAYTYPE_P(0);
1278 117 : element_type = ARR_ELEMTYPE(array);
2944 alvherre 1279 ECB :
1280 : /*
1281 : * We refuse to search for elements in multi-dimensional arrays, since we
1282 : * have no good way to report the element's location in the array.
1283 : */
2944 alvherre 1284 GIC 117 : if (ARR_NDIM(array) > 1)
1285 3 : ereport(ERROR,
1286 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1287 : errmsg("searching for elements in multidimensional arrays is not supported")));
1288 :
1289 114 : if (PG_ARGISNULL(1))
1290 : {
2919 heikki.linnakangas 1291 ECB : /* fast return when the array doesn't have nulls */
2944 alvherre 1292 CBC 6 : if (!array_contains_nulls(array))
2944 alvherre 1293 GIC 3 : PG_RETURN_NULL();
1294 3 : searched_element = (Datum) 0;
1295 3 : null_search = true;
1296 : }
1297 : else
1298 : {
2944 alvherre 1299 GBC 108 : searched_element = PG_GETARG_DATUM(1);
1300 108 : null_search = false;
1301 : }
1302 :
2932 alvherre 1303 GIC 111 : position = (ARR_LBOUND(array))[0] - 1;
1304 :
1305 : /* figure out where to start */
2944 alvherre 1306 GBC 111 : if (PG_NARGS() == 3)
1307 : {
1308 9 : if (PG_ARGISNULL(2))
2944 alvherre 1309 UIC 0 : ereport(ERROR,
2944 alvherre 1310 EUB : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
2701 peter_e 1311 : errmsg("initial position must not be null")));
2944 alvherre 1312 :
2932 alvherre 1313 GIC 9 : position_min = PG_GETARG_INT32(2);
1314 : }
2944 alvherre 1315 EUB : else
2932 alvherre 1316 GIC 102 : position_min = (ARR_LBOUND(array))[0];
1317 :
1318 : /*
2944 alvherre 1319 EUB : * We arrange to look up type info for array_create_iterator only once per
2878 bruce 1320 : * series of calls, assuming the element type doesn't change underneath
1321 : * us.
2944 alvherre 1322 : */
2944 alvherre 1323 GBC 111 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1324 111 : if (my_extra == NULL)
2944 alvherre 1325 EUB : {
2944 alvherre 1326 GIC 42 : fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1327 : sizeof(ArrayMetaState));
1328 42 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
2944 alvherre 1329 CBC 42 : my_extra->element_type = ~element_type;
1330 : }
2944 alvherre 1331 ECB :
2944 alvherre 1332 GIC 111 : if (my_extra->element_type != element_type)
1333 : {
1334 42 : get_typlenbyvalalign(element_type,
2944 alvherre 1335 ECB : &my_extra->typlen,
1336 : &my_extra->typbyval,
1337 : &my_extra->typalign);
1338 :
2944 alvherre 1339 GIC 42 : typentry = lookup_type_cache(element_type, TYPECACHE_EQ_OPR_FINFO);
1340 :
1341 42 : if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
2944 alvherre 1342 UIC 0 : ereport(ERROR,
1343 : (errcode(ERRCODE_UNDEFINED_FUNCTION),
2118 tgl 1344 ECB : errmsg("could not identify an equality operator for type %s",
2118 tgl 1345 EUB : format_type_be(element_type))));
1346 :
2944 alvherre 1347 CBC 42 : my_extra->element_type = element_type;
2312 alvherre 1348 GIC 42 : fmgr_info_cxt(typentry->eq_opr_finfo.fn_oid, &my_extra->proc,
1349 42 : fcinfo->flinfo->fn_mcxt);
1350 : }
1351 :
1352 : /* Examine each array element until we find a match. */
2944 alvherre 1353 CBC 111 : array_iterator = array_create_iterator(array, 0, my_extra);
1354 309 : while (array_iterate(array_iterator, &value, &isnull))
2944 alvherre 1355 ECB : {
2932 alvherre 1356 GIC 288 : position++;
1357 :
2944 alvherre 1358 ECB : /* skip initial elements if caller requested so */
2932 alvherre 1359 GIC 288 : if (position < position_min)
2944 1360 39 : continue;
2944 alvherre 1361 ECB :
1362 : /*
1363 : * Can't look at the array element's value if it's null; but if we
1364 : * search for null, we have a hit and are done.
1365 : */
2944 alvherre 1366 CBC 249 : if (isnull || null_search)
1367 : {
2944 alvherre 1368 GIC 21 : if (isnull && null_search)
2944 alvherre 1369 ECB : {
2944 alvherre 1370 GIC 3 : found = true;
1371 3 : break;
2944 alvherre 1372 ECB : }
1373 : else
2944 alvherre 1374 GIC 18 : continue;
2944 alvherre 1375 ECB : }
1376 :
1377 : /* not nulls, so run the operator */
2944 alvherre 1378 CBC 228 : if (DatumGetBool(FunctionCall2Coll(&my_extra->proc, collation,
2944 alvherre 1379 ECB : searched_element, value)))
1380 : {
2944 alvherre 1381 GIC 87 : found = true;
2944 alvherre 1382 CBC 87 : break;
1383 : }
2944 alvherre 1384 ECB : }
1385 :
2944 alvherre 1386 GIC 111 : array_free_iterator(array_iterator);
1387 :
1388 : /* Avoid leaking memory when handed toasted input */
1389 111 : PG_FREE_IF_COPY(array, 0);
1390 :
1391 111 : if (!found)
2944 alvherre 1392 GBC 21 : PG_RETURN_NULL();
2944 alvherre 1393 EUB :
2932 alvherre 1394 GIC 90 : PG_RETURN_INT32(position);
1395 : }
1396 :
1397 : /*-----------------------------------------------------------------------------
2932 alvherre 1398 EUB : * array_positions :
1399 : * return an array of positions of a value in an array.
2944 1400 : *
1401 : * IS NOT DISTINCT FROM semantics are used for comparisons. Returns NULL when
1402 : * the input array is NULL. When the value is not found in the array, returns
1403 : * an empty array.
1404 : *
1405 : * This is not strict so we have to test for null inputs explicitly.
1406 : *-----------------------------------------------------------------------------
1407 : */
1408 : Datum
2932 alvherre 1409 GIC 30 : array_positions(PG_FUNCTION_ARGS)
1410 : {
1411 : ArrayType *array;
2944 1412 30 : Oid collation = PG_GET_COLLATION();
2944 alvherre 1413 EUB : Oid element_type;
1414 : Datum searched_element,
1415 : value;
1416 : bool isnull;
1417 : int position;
1418 : TypeCacheEntry *typentry;
1419 : ArrayMetaState *my_extra;
1420 : bool null_search;
1421 : ArrayIterator array_iterator;
2944 alvherre 1422 GIC 30 : ArrayBuildState *astate = NULL;
1423 :
1424 30 : if (PG_ARGISNULL(0))
1425 6 : PG_RETURN_NULL();
1426 :
1427 24 : array = PG_GETARG_ARRAYTYPE_P(0);
1428 24 : element_type = ARR_ELEMTYPE(array);
1429 :
2932 1430 24 : position = (ARR_LBOUND(array))[0] - 1;
2932 alvherre 1431 EUB :
2944 1432 : /*
1433 : * We refuse to search for elements in multi-dimensional arrays, since we
1434 : * have no good way to report the element's location in the array.
1435 : */
2944 alvherre 1436 GBC 24 : if (ARR_NDIM(array) > 1)
2944 alvherre 1437 GIC 3 : ereport(ERROR,
2944 alvherre 1438 EUB : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1439 : errmsg("searching for elements in multidimensional arrays is not supported")));
1440 :
2944 alvherre 1441 GIC 21 : astate = initArrayResult(INT4OID, CurrentMemoryContext, false);
2944 alvherre 1442 EUB :
2944 alvherre 1443 GIC 21 : if (PG_ARGISNULL(1))
1444 : {
1445 : /* fast return when the array doesn't have nulls */
1446 6 : if (!array_contains_nulls(array))
2944 alvherre 1447 GBC 3 : PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
2944 alvherre 1448 GIC 3 : searched_element = (Datum) 0;
1449 3 : null_search = true;
1450 : }
2944 alvherre 1451 ECB : else
1452 : {
2944 alvherre 1453 GIC 15 : searched_element = PG_GETARG_DATUM(1);
2944 alvherre 1454 CBC 15 : null_search = false;
1455 : }
1456 :
1457 : /*
2944 alvherre 1458 ECB : * We arrange to look up type info for array_create_iterator only once per
1459 : * series of calls, assuming the element type doesn't change underneath
1460 : * us.
1461 : */
2944 alvherre 1462 GIC 18 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1463 18 : if (my_extra == NULL)
1464 : {
1465 15 : fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
2944 alvherre 1466 ECB : sizeof(ArrayMetaState));
2944 alvherre 1467 GIC 15 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
2944 alvherre 1468 CBC 15 : my_extra->element_type = ~element_type;
1469 : }
2944 alvherre 1470 ECB :
2944 alvherre 1471 CBC 18 : if (my_extra->element_type != element_type)
1472 : {
1473 15 : get_typlenbyvalalign(element_type,
2944 alvherre 1474 ECB : &my_extra->typlen,
1475 : &my_extra->typbyval,
1476 : &my_extra->typalign);
1477 :
2944 alvherre 1478 GIC 15 : typentry = lookup_type_cache(element_type, TYPECACHE_EQ_OPR_FINFO);
1479 :
1480 15 : if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
2944 alvherre 1481 UIC 0 : ereport(ERROR,
2944 alvherre 1482 ECB : (errcode(ERRCODE_UNDEFINED_FUNCTION),
1483 : errmsg("could not identify an equality operator for type %s",
1484 : format_type_be(element_type))));
1485 :
2944 alvherre 1486 CBC 15 : my_extra->element_type = element_type;
2312 alvherre 1487 GIC 15 : fmgr_info_cxt(typentry->eq_opr_finfo.fn_oid, &my_extra->proc,
1488 15 : fcinfo->flinfo->fn_mcxt);
1489 : }
1490 :
1491 : /*
1492 : * Accumulate each array position iff the element matches the given
2878 bruce 1493 ECB : * element.
1494 : */
2944 alvherre 1495 CBC 18 : array_iterator = array_create_iterator(array, 0, my_extra);
2944 alvherre 1496 GIC 408 : while (array_iterate(array_iterator, &value, &isnull))
1497 : {
2932 1498 390 : position += 1;
2944 alvherre 1499 ECB :
2944 alvherre 1500 EUB : /*
1501 : * Can't look at the array element's value if it's null; but if we
1502 : * search for null, we have a hit.
1503 : */
2944 alvherre 1504 GIC 390 : if (isnull || null_search)
1505 : {
1506 36 : if (isnull && null_search)
1507 : astate =
2932 1508 6 : accumArrayResult(astate, Int32GetDatum(position), false,
1509 : INT4OID, CurrentMemoryContext);
2944 alvherre 1510 ECB :
2944 alvherre 1511 GIC 36 : continue;
1512 : }
2944 alvherre 1513 EUB :
1514 : /* not nulls, so run the operator */
2944 alvherre 1515 GIC 354 : if (DatumGetBool(FunctionCall2Coll(&my_extra->proc, collation,
1516 : searched_element, value)))
2944 alvherre 1517 ECB : astate =
2932 alvherre 1518 CBC 45 : accumArrayResult(astate, Int32GetDatum(position), false,
1519 : INT4OID, CurrentMemoryContext);
2944 alvherre 1520 ECB : }
1521 :
2944 alvherre 1522 CBC 18 : array_free_iterator(array_iterator);
1523 :
2944 alvherre 1524 ECB : /* Avoid leaking memory when handed toasted input */
2944 alvherre 1525 GIC 18 : PG_FREE_IF_COPY(array, 0);
1526 :
1527 18 : PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
1528 : }
1529 :
1530 : /*
1531 : * array_shuffle_n
1532 : * Return a copy of array with n randomly chosen items.
1533 : *
1534 : * The number of items must not exceed the size of the first dimension of the
1535 : * array. We preserve the first dimension's lower bound if keep_lb,
1536 : * else it's set to 1. Lower-order dimensions are preserved in any case.
1537 : *
1538 : * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
1539 : * from the system catalogs, given only the elmtyp. However, the caller is
1540 : * in a better position to cache this info across multiple calls.
1541 : */
1542 : static ArrayType *
2 tgl 1543 GNC 24 : array_shuffle_n(ArrayType *array, int n, bool keep_lb,
1544 : Oid elmtyp, TypeCacheEntry *typentry)
1545 : {
1546 : ArrayType *result;
1547 : int ndim,
1548 : *dims,
1549 : *lbs,
1550 : nelm,
1551 : nitem,
1552 : rdims[MAXDIM],
1553 : rlbs[MAXDIM];
1554 : int16 elmlen;
1555 : bool elmbyval;
1556 : char elmalign;
1557 : Datum *elms,
1558 : *ielms;
1559 : bool *nuls,
1560 : *inuls;
1561 :
1562 24 : ndim = ARR_NDIM(array);
1563 24 : dims = ARR_DIMS(array);
1564 24 : lbs = ARR_LBOUND(array);
1565 :
1566 24 : elmlen = typentry->typlen;
1567 24 : elmbyval = typentry->typbyval;
1568 24 : elmalign = typentry->typalign;
1569 :
1570 : /* If the target array is empty, exit fast */
1571 24 : if (ndim < 1 || dims[0] < 1 || n < 1)
2 tgl 1572 UNC 0 : return construct_empty_array(elmtyp);
1573 :
2 tgl 1574 GNC 24 : deconstruct_array(array, elmtyp, elmlen, elmbyval, elmalign,
1575 : &elms, &nuls, &nelm);
1576 :
1577 24 : nitem = dims[0]; /* total number of items */
1578 24 : nelm /= nitem; /* number of elements per item */
1579 :
1580 24 : Assert(n <= nitem); /* else it's caller error */
1581 :
1582 : /*
1583 : * Shuffle array using Fisher-Yates algorithm. Scan the array and swap
1584 : * current item (nelm datums starting at ielms) with a randomly chosen
1585 : * later item (nelm datums starting at jelms) in each iteration. We can
1586 : * stop once we've done n iterations; then first n items are the result.
1587 : */
1588 24 : ielms = elms;
1589 24 : inuls = nuls;
1590 114 : for (int i = 0; i < n; i++)
1591 : {
1592 90 : int j = (int) pg_prng_uint64_range(&pg_global_prng_state, i, nitem - 1) * nelm;
1593 90 : Datum *jelms = elms + j;
1594 90 : bool *jnuls = nuls + j;
1595 :
1596 : /* Swap i'th and j'th items; advance ielms/inuls to next item */
1597 246 : for (int k = 0; k < nelm; k++)
1598 : {
1599 156 : Datum elm = *ielms;
1600 156 : bool nul = *inuls;
1601 :
1602 156 : *ielms++ = *jelms;
1603 156 : *inuls++ = *jnuls;
1604 156 : *jelms++ = elm;
1605 156 : *jnuls++ = nul;
1606 : }
1607 : }
1608 :
1609 : /* Set up dimensions of the result */
1610 24 : memcpy(rdims, dims, ndim * sizeof(int));
1611 24 : memcpy(rlbs, lbs, ndim * sizeof(int));
1612 24 : rdims[0] = n;
1613 24 : if (!keep_lb)
1614 12 : rlbs[0] = 1;
1615 :
1616 24 : result = construct_md_array(elms, nuls, ndim, rdims, rlbs,
1617 : elmtyp, elmlen, elmbyval, elmalign);
1618 :
1619 24 : pfree(elms);
1620 24 : pfree(nuls);
1621 :
1622 24 : return result;
1623 : }
1624 :
1625 : /*
1626 : * array_shuffle
1627 : *
1628 : * Returns an array with the same dimensions as the input array, with its
1629 : * first-dimension elements in random order.
1630 : */
1631 : Datum
1632 12 : array_shuffle(PG_FUNCTION_ARGS)
1633 : {
1634 12 : ArrayType *array = PG_GETARG_ARRAYTYPE_P(0);
1635 : ArrayType *result;
1636 : Oid elmtyp;
1637 : TypeCacheEntry *typentry;
1638 :
1639 : /*
1640 : * There is no point in shuffling empty arrays or arrays with less than
1641 : * two items.
1642 : */
1643 12 : if (ARR_NDIM(array) < 1 || ARR_DIMS(array)[0] < 2)
2 tgl 1644 UNC 0 : PG_RETURN_ARRAYTYPE_P(array);
1645 :
2 tgl 1646 GNC 12 : elmtyp = ARR_ELEMTYPE(array);
1647 12 : typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
1648 12 : if (typentry == NULL || typentry->type_id != elmtyp)
1649 : {
1650 12 : typentry = lookup_type_cache(elmtyp, 0);
1651 12 : fcinfo->flinfo->fn_extra = (void *) typentry;
1652 : }
1653 :
1654 12 : result = array_shuffle_n(array, ARR_DIMS(array)[0], true, elmtyp, typentry);
1655 :
1656 12 : PG_RETURN_ARRAYTYPE_P(result);
1657 : }
1658 :
1659 : /*
1660 : * array_sample
1661 : *
1662 : * Returns an array of n randomly chosen first-dimension elements
1663 : * from the input array.
1664 : */
1665 : Datum
1666 18 : array_sample(PG_FUNCTION_ARGS)
1667 : {
1668 18 : ArrayType *array = PG_GETARG_ARRAYTYPE_P(0);
1669 18 : int n = PG_GETARG_INT32(1);
1670 : ArrayType *result;
1671 : Oid elmtyp;
1672 : TypeCacheEntry *typentry;
1673 : int nitem;
1674 :
1675 18 : nitem = (ARR_NDIM(array) < 1) ? 0 : ARR_DIMS(array)[0];
1676 :
1677 18 : if (n < 0 || n > nitem)
1678 6 : ereport(ERROR,
1679 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1680 : errmsg("sample size must be between 0 and %d", nitem)));
1681 :
1682 12 : elmtyp = ARR_ELEMTYPE(array);
1683 12 : typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
1684 12 : if (typentry == NULL || typentry->type_id != elmtyp)
1685 : {
1686 12 : typentry = lookup_type_cache(elmtyp, 0);
1687 12 : fcinfo->flinfo->fn_extra = (void *) typentry;
1688 : }
1689 :
1690 12 : result = array_shuffle_n(array, n, false, elmtyp, typentry);
1691 :
1692 12 : PG_RETURN_ARRAYTYPE_P(result);
1693 : }
|