Age Owner Branch data TLA Line data Source code
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 : : {
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 *
3343 tgl@sss.pgh.pa.us 64 :CBC 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 */
3258 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 */
2723 83 [ + + ]: 970 : if (!AggCheckCallContext(fcinfo, &resultcxt))
84 : 910 : resultcxt = CurrentMemoryContext;
85 : :
86 : : /* Now collect the array value */
3336 87 [ + + ]: 970 : if (!PG_ARGISNULL(argno))
88 : : {
2723 89 : 958 : MemoryContext oldcxt = MemoryContextSwitchTo(resultcxt);
90 : :
3258 91 : 958 : eah = PG_GETARG_EXPANDED_ARRAYX(argno, my_extra);
2723 92 : 958 : MemoryContextSwitchTo(oldcxt);
93 : : }
94 : : else
95 : : {
96 : : /* We have to look up the array type and element type */
3343 97 : 12 : Oid arr_typeid = get_fn_expr_argtype(fcinfo->flinfo, argno);
98 : :
99 [ - + ]: 12 : if (!OidIsValid(arr_typeid))
3343 tgl@sss.pgh.pa.us 100 [ # # ]:UBC 0 : ereport(ERROR,
101 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
102 : : errmsg("could not determine input data type")));
3343 tgl@sss.pgh.pa.us 103 :CBC 12 : element_type = get_element_type(arr_typeid);
104 [ - + ]: 12 : if (!OidIsValid(element_type))
3343 tgl@sss.pgh.pa.us 105 [ # # ]:UBC 0 : ereport(ERROR,
106 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
107 : : errmsg("input data type is not an array")));
108 : :
3258 tgl@sss.pgh.pa.us 109 :CBC 12 : eah = construct_empty_expanded_array(element_type,
110 : : resultcxt,
111 : : my_extra);
112 : : }
113 : :
114 : 970 : return eah;
115 : : }
116 : :
117 : : /*-----------------------------------------------------------------------------
118 : : * array_append :
119 : : * push an element onto the end of a one-dimensional array
120 : : *----------------------------------------------------------------------------
121 : : */
122 : : Datum
3343 123 : 931 : array_append(PG_FUNCTION_ARGS)
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 : :
3258 134 : 931 : eah = fetch_array_arg_replace_nulls(fcinfo, 0);
3343 135 : 931 : isNull = PG_ARGISNULL(1);
136 [ - + ]: 931 : if (isNull)
3343 tgl@sss.pgh.pa.us 137 :UBC 0 : newelem = (Datum) 0;
138 : : else
3343 tgl@sss.pgh.pa.us 139 :CBC 931 : newelem = PG_GETARG_DATUM(1);
140 : :
3258 141 [ + + ]: 931 : if (eah->ndims == 1)
142 : : {
143 : : /* append newelem */
144 : 723 : lb = eah->lbound;
145 : 723 : dimv = eah->dims;
146 : :
147 : : /* index of added elem is at lb[0] + (dimv[0] - 1) + 1 */
2315 andres@anarazel.de 148 [ - + ]: 723 : if (pg_add_s32_overflow(lb[0], dimv[0], &indx))
3343 tgl@sss.pgh.pa.us 149 [ # # ]:UBC 0 : ereport(ERROR,
150 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
151 : : errmsg("integer out of range")));
152 : : }
3258 tgl@sss.pgh.pa.us 153 [ + - ]:CBC 208 : else if (eah->ndims == 0)
3343 154 : 208 : indx = 1;
155 : : else
7567 tgl@sss.pgh.pa.us 156 [ # # ]:UBC 0 : ereport(ERROR,
157 : : (errcode(ERRCODE_DATA_EXCEPTION),
158 : : errmsg("argument must be empty or one-dimensional array")));
159 : :
160 : : /* Perform element insertion */
3343 tgl@sss.pgh.pa.us 161 :CBC 931 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
162 : :
3258 163 : 931 : result = array_set_element(EOHPGetRWDatum(&eah->hdr),
164 : : 1, &indx, newelem, isNull,
2489 165 : 931 : -1, my_extra->typlen, my_extra->typbyval, my_extra->typalign);
166 : :
3258 167 : 931 : 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
3343 176 : 39 : array_prepend(PG_FUNCTION_ARGS)
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 : 39 : isNull = PG_ARGISNULL(0);
188 [ - + ]: 39 : if (isNull)
3343 tgl@sss.pgh.pa.us 189 :UBC 0 : newelem = (Datum) 0;
190 : : else
3343 tgl@sss.pgh.pa.us 191 :CBC 39 : newelem = PG_GETARG_DATUM(0);
3258 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;
198 : 39 : lb0 = lb[0];
199 : :
2315 andres@anarazel.de 200 [ - + ]: 39 : if (pg_sub_s32_overflow(lb0, 1, &indx))
3343 tgl@sss.pgh.pa.us 201 [ # # ]:UBC 0 : ereport(ERROR,
202 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
203 : : errmsg("integer out of range")));
204 : : }
3258 205 [ # # ]: 0 : else if (eah->ndims == 0)
206 : : {
7597 207 : 0 : indx = 1;
3258 208 : 0 : lb0 = 1;
209 : : }
210 : : else
7567 211 [ # # ]: 0 : ereport(ERROR,
212 : : (errcode(ERRCODE_DATA_EXCEPTION),
213 : : errmsg("argument must be empty or one-dimensional array")));
214 : :
215 : : /* Perform element insertion */
7597 tgl@sss.pgh.pa.us 216 :CBC 39 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
217 : :
3258 218 : 39 : result = array_set_element(EOHPGetRWDatum(&eah->hdr),
219 : : 1, &indx, newelem, isNull,
2489 220 : 39 : -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 */
3258 223 [ - + ]: 39 : Assert(result == EOHPGetRWDatum(&eah->hdr));
224 [ + - ]: 39 : if (eah->ndims == 1)
225 : : {
226 : : /* This is ok whether we've deconstructed or not */
227 : 39 : eah->lbound[0] = lb0;
228 : : }
229 : :
230 : 39 : 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
7677 240 : 14831 : array_cat(PG_FUNCTION_ARGS)
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 */
6723 272 [ + + ]: 14831 : if (PG_ARGISNULL(0))
273 : : {
274 [ - + ]: 720 : if (PG_ARGISNULL(1))
6723 tgl@sss.pgh.pa.us 275 :UBC 0 : PG_RETURN_NULL();
6723 tgl@sss.pgh.pa.us 276 :CBC 720 : result = PG_GETARG_ARRAYTYPE_P(1);
277 : 720 : PG_RETURN_ARRAYTYPE_P(result);
278 : : }
279 [ - + ]: 14111 : if (PG_ARGISNULL(1))
280 : : {
6723 tgl@sss.pgh.pa.us 281 :UBC 0 : result = PG_GETARG_ARRAYTYPE_P(0);
282 : 0 : PG_RETURN_ARRAYTYPE_P(result);
283 : : }
284 : :
7677 tgl@sss.pgh.pa.us 285 :CBC 14111 : v1 = PG_GETARG_ARRAYTYPE_P(0);
286 : 14111 : v2 = PG_GETARG_ARRAYTYPE_P(1);
287 : :
7058 288 : 14111 : element_type1 = ARR_ELEMTYPE(v1);
289 : 14111 : element_type2 = ARR_ELEMTYPE(v2);
290 : :
291 : : /* Check we have matching element types */
292 [ - + ]: 14111 : if (element_type1 != element_type2)
7058 tgl@sss.pgh.pa.us 293 [ # # ]:UBC 0 : 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 */
7058 tgl@sss.pgh.pa.us 302 :CBC 14111 : 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 : : */
7677 313 : 14111 : ndims1 = ARR_NDIM(v1);
314 : 14111 : 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 : : */
7597 322 [ - + - - ]: 14111 : if (ndims1 == 0 && ndims2 > 0)
7597 tgl@sss.pgh.pa.us 323 :UBC 0 : PG_RETURN_ARRAYTYPE_P(v2);
324 : :
7597 tgl@sss.pgh.pa.us 325 [ + + ]:CBC 14111 : if (ndims2 == 0)
326 : 17 : PG_RETURN_ARRAYTYPE_P(v1);
327 : :
328 : : /* the rest fall under rule 3, 4, or 5 */
7546 329 [ + + ]: 14094 : if (ndims1 != ndims2 &&
330 [ + + ]: 9 : ndims1 != ndims2 - 1 &&
331 [ - + ]: 6 : ndims1 != ndims2 + 1)
7567 tgl@sss.pgh.pa.us 332 [ # # ]:UBC 0 : 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 */
7677 tgl@sss.pgh.pa.us 340 :CBC 14094 : lbs1 = ARR_LBOUND(v1);
341 : 14094 : lbs2 = ARR_LBOUND(v2);
342 : 14094 : dims1 = ARR_DIMS(v1);
343 : 14094 : dims2 = ARR_DIMS(v2);
344 [ - + ]: 14094 : dat1 = ARR_DATA_PTR(v1);
345 [ - + ]: 14094 : dat2 = ARR_DATA_PTR(v2);
6723 346 [ - + ]: 14094 : bitmap1 = ARR_NULLBITMAP(v1);
347 [ - + ]: 14094 : bitmap2 = ARR_NULLBITMAP(v2);
348 : 14094 : nitems1 = ArrayGetNItems(ndims1, dims1);
349 : 14094 : nitems2 = ArrayGetNItems(ndims2, dims2);
350 [ - + ]: 14094 : ndatabytes1 = ARR_SIZE(v1) - ARR_DATA_OFFSET(v1);
351 [ - + ]: 14094 : ndatabytes2 = ARR_SIZE(v2) - ARR_DATA_OFFSET(v2);
352 : :
7677 353 [ + + ]: 14094 : if (ndims1 == ndims2)
354 : : {
355 : : /*
356 : : * resulting array is made up of the elements (possibly arrays
357 : : * themselves) of the input argument arrays
358 : : */
7546 359 : 14085 : ndims = ndims1;
7677 360 : 14085 : dims = (int *) palloc(ndims * sizeof(int));
361 : 14085 : lbs = (int *) palloc(ndims * sizeof(int));
362 : :
7546 363 : 14085 : dims[0] = dims1[0] + dims2[0];
364 : 14085 : lbs[0] = lbs1[0];
365 : :
366 [ + + ]: 14091 : for (i = 1; i < ndims; i++)
367 : : {
7677 368 [ + - - + ]: 6 : if (dims1[i] != dims2[i] || lbs1[i] != lbs2[i])
7567 tgl@sss.pgh.pa.us 369 [ # # ]:UBC 0 : 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 : :
7546 tgl@sss.pgh.pa.us 375 :CBC 6 : dims[i] = dims1[i];
376 : 6 : lbs[i] = lbs1[i];
377 : : }
378 : : }
7677 379 [ + + ]: 9 : 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 : 3 : ndims = ndims2;
7546 386 : 3 : dims = (int *) palloc(ndims * sizeof(int));
387 : 3 : lbs = (int *) palloc(ndims * sizeof(int));
388 : 3 : memcpy(dims, dims2, ndims * sizeof(int));
389 : 3 : memcpy(lbs, lbs2, ndims * sizeof(int));
390 : :
391 : : /* increment number of elements in outer array */
7677 392 : 3 : dims[0] += 1;
393 : :
394 : : /* make sure the added element matches our existing elements */
395 [ + + ]: 6 : for (i = 0; i < ndims1; i++)
396 : : {
397 [ + - - + ]: 3 : if (dims1[i] != dims[i + 1] || lbs1[i] != lbs[i + 1])
7567 tgl@sss.pgh.pa.us 398 [ # # ]:UBC 0 : 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 : : */
7677 tgl@sss.pgh.pa.us 413 :CBC 6 : ndims = ndims1;
7546 414 : 6 : dims = (int *) palloc(ndims * sizeof(int));
415 : 6 : lbs = (int *) palloc(ndims * sizeof(int));
416 : 6 : memcpy(dims, dims1, ndims * sizeof(int));
417 : 6 : memcpy(lbs, lbs1, ndims * sizeof(int));
418 : :
419 : : /* increment number of elements in outer array */
7677 420 : 6 : dims[0] += 1;
421 : :
422 : : /* make sure the added element matches our existing elements */
423 [ + + ]: 12 : for (i = 0; i < ndims2; i++)
424 : : {
425 [ + - - + ]: 6 : if (dims2[i] != dims[i + 1] || lbs2[i] != lbs[i + 1])
7567 tgl@sss.pgh.pa.us 426 [ # # ]:UBC 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 */
6723 tgl@sss.pgh.pa.us 435 :CBC 14094 : nitems = ArrayGetNItems(ndims, dims);
1070 436 : 14094 : ArrayCheckBounds(ndims, dims, lbs);
437 : :
438 : : /* build the result array */
7677 439 : 14094 : ndatabytes = ndatabytes1 + ndatabytes2;
6723 440 [ + - - + ]: 14094 : if (ARR_HASNULL(v1) || ARR_HASNULL(v2))
441 : : {
6723 tgl@sss.pgh.pa.us 442 :UBC 0 : dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
443 : 0 : nbytes = ndatabytes + dataoffset;
444 : : }
445 : : else
446 : : {
6723 tgl@sss.pgh.pa.us 447 :CBC 14094 : dataoffset = 0; /* marker for no null bitmap */
448 : 14094 : nbytes = ndatabytes + ARR_OVERHEAD_NONULLS(ndims);
449 : : }
4736 450 : 14094 : result = (ArrayType *) palloc0(nbytes);
6256 451 : 14094 : SET_VARSIZE(result, nbytes);
7677 452 : 14094 : result->ndim = ndims;
6723 453 : 14094 : result->dataoffset = dataoffset;
7677 454 : 14094 : result->elemtype = element_type;
455 : 14094 : memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
456 : 14094 : memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
457 : : /* data area is arg1 then arg2 */
458 [ - + ]: 14094 : memcpy(ARR_DATA_PTR(result), dat1, ndatabytes1);
459 [ - + ]: 14094 : memcpy(ARR_DATA_PTR(result) + ndatabytes1, dat2, ndatabytes2);
460 : : /* handle the null bitmap if needed */
6723 461 [ - + ]: 14094 : if (ARR_HASNULL(result))
462 : : {
6723 tgl@sss.pgh.pa.us 463 [ # # ]:UBC 0 : array_bitmap_copy(ARR_NULLBITMAP(result), 0,
464 : : bitmap1, 0,
465 : : nitems1);
466 [ # # ]: 0 : array_bitmap_copy(ARR_NULLBITMAP(result), nitems1,
467 : : bitmap2, 0,
468 : : nitems2);
469 : : }
470 : :
7677 tgl@sss.pgh.pa.us 471 :CBC 14094 : PG_RETURN_ARRAYTYPE_P(result);
472 : : }
473 : :
474 : :
475 : : /*
476 : : * ARRAY_AGG(anynonarray) aggregate function
477 : : */
478 : : Datum
5631 peter_e@gmx.net 479 : 892602 : array_agg_transfn(PG_FUNCTION_ARGS)
480 : : {
5421 bruce@momjian.us 481 : 892602 : Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
482 : : MemoryContext aggcontext;
483 : : ArrayBuildState *state;
484 : : Datum elem;
485 : :
5631 peter_e@gmx.net 486 [ - + ]: 892602 : if (arg1_typeid == InvalidOid)
5630 tgl@sss.pgh.pa.us 487 [ # # ]:UBC 0 : 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 : :
5179 tgl@sss.pgh.pa.us 497 [ - + ]:CBC 892602 : if (!AggCheckCallContext(fcinfo, &aggcontext))
498 : : {
499 : : /* cannot be called directly because of internal-type argument */
5586 tgl@sss.pgh.pa.us 500 [ # # ]:UBC 0 : elog(ERROR, "array_agg_transfn called in non-aggregate context");
501 : : }
502 : :
3340 jdavis@postgresql.or 503 [ + + ]:CBC 892602 : if (PG_ARGISNULL(0))
504 : 38462 : state = initArrayResult(arg1_typeid, aggcontext, false);
505 : : else
506 : 854140 : state = (ArrayBuildState *) PG_GETARG_POINTER(0);
507 : :
5630 tgl@sss.pgh.pa.us 508 [ + + ]: 892602 : elem = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1);
509 : :
510 : 892602 : state = accumArrayResult(state,
511 : : elem,
512 : 892602 : 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 : : */
521 : 892602 : PG_RETURN_POINTER(state);
522 : : }
523 : :
524 : : Datum
447 drowley@postgresql.o 525 : 60 : array_agg_combine(PG_FUNCTION_ARGS)
526 : : {
527 : : ArrayBuildState *state1;
528 : : ArrayBuildState *state2;
529 : : MemoryContext agg_context;
530 : : MemoryContext old_context;
531 : :
532 [ - + ]: 60 : if (!AggCheckCallContext(fcinfo, &agg_context))
447 drowley@postgresql.o 533 [ # # ]:UBC 0 : elog(ERROR, "aggregate function called in non-aggregate context");
534 : :
447 drowley@postgresql.o 535 [ + + ]:CBC 60 : state1 = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
536 [ + - ]: 60 : state2 = PG_ARGISNULL(1) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(1);
537 : :
538 [ - + ]: 60 : if (state2 == NULL)
539 : : {
540 : : /*
541 : : * NULL state2 is easy, just return state1, which we know is already
542 : : * in the agg_context
543 : : */
447 drowley@postgresql.o 544 [ # # ]:UBC 0 : if (state1 == NULL)
545 : 0 : PG_RETURN_NULL();
546 : 0 : PG_RETURN_POINTER(state1);
547 : : }
548 : :
447 drowley@postgresql.o 549 [ + + ]:CBC 60 : 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 [ + + ]: 4380 : for (int i = 0; i < state2->nelems; i++)
558 : : {
559 [ + + ]: 4350 : if (!state2->dnulls[i])
560 : 3268 : state1->dvalues[i] = datumCopy(state2->dvalues[i],
561 : 3268 : state1->typbyval,
562 : 3268 : state1->typlen);
563 : : else
564 : 1082 : 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 [ + - ]: 30 : else if (state2->nelems > 0)
576 : : {
577 : : /* We only need to combine the two states if state2 has any elements */
578 : 30 : int reqsize = state1->nelems + state2->nelems;
579 : 30 : MemoryContext oldContext = MemoryContextSwitchTo(state1->mcontext);
580 : :
581 [ - + ]: 30 : Assert(state1->element_type == state2->element_type);
582 : :
583 : : /* Enlarge state1 arrays if needed */
584 [ + - ]: 30 : if (state1->alen < reqsize)
585 : : {
586 : : /* Use a power of 2 size rather than allocating just reqsize */
587 : 30 : state1->alen = pg_nextpower2_32(reqsize);
588 : 60 : state1->dvalues = (Datum *) repalloc(state1->dvalues,
589 : 30 : state1->alen * sizeof(Datum));
590 : 30 : state1->dnulls = (bool *) repalloc(state1->dnulls,
591 : 30 : state1->alen * sizeof(bool));
592 : : }
593 : :
594 : : /* Copy in the state2 elements to the end of the state1 arrays */
595 [ + + ]: 10680 : for (int i = 0; i < state2->nelems; i++)
596 : : {
597 [ + + ]: 10650 : if (!state2->dnulls[i])
598 : 7982 : state1->dvalues[i + state1->nelems] =
599 : 7982 : datumCopy(state2->dvalues[i],
600 : 7982 : state1->typbyval,
601 : 7982 : state1->typlen);
602 : : else
603 : 2668 : state1->dvalues[i + state1->nelems] = (Datum) 0;
604 : : }
605 : :
606 : 30 : memcpy(&state1->dnulls[state1->nelems], state2->dnulls,
607 : 30 : sizeof(bool) * state2->nelems);
608 : :
609 : 30 : state1->nelems = reqsize;
610 : :
611 : 30 : MemoryContextSwitchTo(oldContext);
612 : : }
613 : :
614 : 30 : PG_RETURN_POINTER(state1);
615 : : }
616 : :
617 : : /*
618 : : * array_agg_serialize
619 : : * Serialize ArrayBuildState into bytea.
620 : : */
621 : : Datum
622 : 60 : 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 [ - + ]: 60 : Assert(AggCheckCallContext(fcinfo, NULL));
630 : :
631 : 60 : state = (ArrayBuildState *) PG_GETARG_POINTER(0);
632 : :
633 : 60 : pq_begintypsend(&buf);
634 : :
635 : : /*
636 : : * element_type. Putting this first is more convenient in deserialization
637 : : */
638 : 60 : 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 : 60 : pq_sendint64(&buf, state->nelems);
645 : :
646 : : /* alen can be decided during deserialization */
647 : :
648 : : /* typlen */
649 : 60 : pq_sendint16(&buf, state->typlen);
650 : :
651 : : /* typbyval */
652 : 60 : pq_sendbyte(&buf, state->typbyval);
653 : :
654 : : /* typalign */
655 : 60 : pq_sendbyte(&buf, state->typalign);
656 : :
657 : : /* dnulls */
425 peter@eisentraut.org 658 : 60 : 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 : : */
447 drowley@postgresql.o 667 [ + - ]: 60 : if (state->typbyval)
425 peter@eisentraut.org 668 : 60 : 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 */
447 drowley@postgresql.o 675 :UBC 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 : :
447 drowley@postgresql.o 705 :CBC 60 : result = pq_endtypsend(&buf);
706 : :
707 : 60 : PG_RETURN_BYTEA_P(result);
708 : : }
709 : :
710 : : Datum
711 : 60 : 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 [ - + ]: 60 : if (!AggCheckCallContext(fcinfo, NULL))
447 drowley@postgresql.o 721 [ # # ]:UBC 0 : elog(ERROR, "aggregate function called in non-aggregate context");
722 : :
447 drowley@postgresql.o 723 :CBC 60 : 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 : : */
170 drowley@postgresql.o 729 [ - + ]:GNC 60 : initReadOnlyStringInfo(&buf, VARDATA_ANY(sstate),
730 [ - + - - : 60 : VARSIZE_ANY_EXHDR(sstate));
- - - - -
+ ]
731 : :
732 : : /* element_type */
447 drowley@postgresql.o 733 :CBC 60 : element_type = pq_getmsgint(&buf, 4);
734 : :
735 : : /* nelems */
736 : 60 : nelems = pq_getmsgint64(&buf);
737 : :
738 : : /* Create output ArrayBuildState with the needed number of elements */
739 : 60 : result = initArrayResultWithSize(element_type, CurrentMemoryContext,
740 : : false, nelems);
741 : 60 : result->nelems = nelems;
742 : :
743 : : /* typlen */
744 : 60 : result->typlen = pq_getmsgint(&buf, 2);
745 : :
746 : : /* typbyval */
747 : 60 : result->typbyval = pq_getmsgbyte(&buf);
748 : :
749 : : /* typalign */
750 : 60 : result->typalign = pq_getmsgbyte(&buf);
751 : :
752 : : /* dnulls */
753 : 60 : temp = pq_getmsgbytes(&buf, sizeof(bool) * nelems);
754 : 60 : memcpy(result->dnulls, temp, sizeof(bool) * nelems);
755 : :
756 : : /* dvalues --- see comment in array_agg_serialize */
757 [ + - ]: 60 : if (result->typbyval)
758 : : {
759 : 60 : temp = pq_getmsgbytes(&buf, sizeof(Datum) * nelems);
760 : 60 : memcpy(result->dvalues, temp, sizeof(Datum) * nelems);
761 : : }
762 : : else
763 : : {
764 : : DeserialIOData *iodata;
765 : :
766 : : /* Avoid repeat catalog lookups for typreceive function */
447 drowley@postgresql.o 767 :UBC 0 : iodata = (DeserialIOData *) fcinfo->flinfo->fn_extra;
768 [ # # ]: 0 : if (iodata == NULL)
769 : : {
770 : : Oid typreceive;
771 : :
772 : : iodata = (DeserialIOData *)
773 : 0 : MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
774 : : sizeof(DeserialIOData));
775 : 0 : getTypeBinaryInputInfo(element_type, &typreceive,
776 : : &iodata->typioparam);
777 : 0 : fmgr_info_cxt(typreceive, &iodata->typreceive,
778 : 0 : fcinfo->flinfo->fn_mcxt);
779 : 0 : fcinfo->flinfo->fn_extra = (void *) iodata;
780 : : }
781 : :
782 [ # # ]: 0 : for (int i = 0; i < nelems; i++)
783 : : {
784 : : int itemlen;
785 : : StringInfoData elem_buf;
786 : :
787 [ # # ]: 0 : if (result->dnulls[i])
788 : : {
789 : 0 : result->dvalues[i] = (Datum) 0;
790 : 0 : continue;
791 : : }
792 : :
793 : 0 : itemlen = pq_getmsgint(&buf, 4);
794 [ # # # # ]: 0 : if (itemlen < 0 || itemlen > (buf.len - buf.cursor))
795 [ # # ]: 0 : 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 : : */
171 drowley@postgresql.o 804 :UNC 0 : initReadOnlyStringInfo(&elem_buf, &buf.data[buf.cursor], itemlen);
805 : :
447 drowley@postgresql.o 806 :UBC 0 : buf.cursor += itemlen;
807 : :
808 : : /* Now call the element's receiveproc */
809 : 0 : result->dvalues[i] = ReceiveFunctionCall(&iodata->typreceive,
810 : : &elem_buf,
811 : : iodata->typioparam,
812 : : -1);
813 : : }
814 : : }
815 : :
447 drowley@postgresql.o 816 :CBC 60 : pq_getmsgend(&buf);
817 : :
818 : 60 : PG_RETURN_POINTER(result);
819 : : }
820 : :
821 : : Datum
5631 peter_e@gmx.net 822 : 42819 : array_agg_finalfn(PG_FUNCTION_ARGS)
823 : : {
824 : : Datum result;
825 : : ArrayBuildState *state;
826 : : int dims[1];
827 : : int lbs[1];
828 : :
829 : : /* cannot be called directly because of internal-type argument */
5179 tgl@sss.pgh.pa.us 830 [ - + ]: 42819 : Assert(AggCheckCallContext(fcinfo, NULL));
831 : :
3428 832 [ + + ]: 42819 : state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
833 : :
834 [ + + ]: 42819 : if (state == NULL)
835 : 4376 : PG_RETURN_NULL(); /* returns null iff no input values */
836 : :
5586 837 : 38443 : dims[0] = state->nelems;
838 : 38443 : 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 : 38443 : result = makeMdArrayResult(state, 1, dims, lbs,
847 : : CurrentMemoryContext,
848 : : false);
849 : :
850 : 38443 : PG_RETURN_DATUM(result);
851 : : }
852 : :
853 : : /*
854 : : * ARRAY_AGG(anyarray) aggregate function
855 : : */
856 : : Datum
3428 857 : 30075 : array_agg_array_transfn(PG_FUNCTION_ARGS)
858 : : {
859 : 30075 : Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
860 : : MemoryContext aggcontext;
861 : : ArrayBuildStateArr *state;
862 : :
863 [ - + ]: 30075 : if (arg1_typeid == InvalidOid)
3428 tgl@sss.pgh.pa.us 864 [ # # ]:UBC 0 : 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 : :
3428 tgl@sss.pgh.pa.us 874 [ - + ]:CBC 30075 : if (!AggCheckCallContext(fcinfo, &aggcontext))
875 : : {
876 : : /* cannot be called directly because of internal-type argument */
3428 tgl@sss.pgh.pa.us 877 [ # # ]:UBC 0 : elog(ERROR, "array_agg_array_transfn called in non-aggregate context");
878 : : }
879 : :
880 : :
3340 jdavis@postgresql.or 881 [ + + ]:CBC 30075 : if (PG_ARGISNULL(0))
882 : 120 : state = initArrayResultArr(arg1_typeid, InvalidOid, aggcontext, false);
883 : : else
884 : 29955 : state = (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
885 : :
3428 tgl@sss.pgh.pa.us 886 : 30075 : state = accumArrayResultArr(state,
887 : : PG_GETARG_DATUM(1),
888 : 30075 : 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 : : */
897 : 30066 : PG_RETURN_POINTER(state);
898 : : }
899 : :
900 : : Datum
447 drowley@postgresql.o 901 : 60 : array_agg_array_combine(PG_FUNCTION_ARGS)
902 : : {
903 : : ArrayBuildStateArr *state1;
904 : : ArrayBuildStateArr *state2;
905 : : MemoryContext agg_context;
906 : : MemoryContext old_context;
907 : :
908 [ - + ]: 60 : if (!AggCheckCallContext(fcinfo, &agg_context))
447 drowley@postgresql.o 909 [ # # ]:UBC 0 : elog(ERROR, "aggregate function called in non-aggregate context");
910 : :
447 drowley@postgresql.o 911 [ + + ]:CBC 60 : state1 = PG_ARGISNULL(0) ? NULL : (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
912 [ + - ]: 60 : state2 = PG_ARGISNULL(1) ? NULL : (ArrayBuildStateArr *) PG_GETARG_POINTER(1);
913 : :
914 [ - + ]: 60 : if (state2 == NULL)
915 : : {
916 : : /*
917 : : * NULL state2 is easy, just return state1, which we know is already
918 : : * in the agg_context
919 : : */
447 drowley@postgresql.o 920 [ # # ]:UBC 0 : if (state1 == NULL)
921 : 0 : PG_RETURN_NULL();
922 : 0 : PG_RETURN_POINTER(state1);
923 : : }
924 : :
447 drowley@postgresql.o 925 [ + + ]:CBC 60 : if (state1 == NULL)
926 : : {
927 : : /* We must copy state2's data into the agg_context */
928 : 30 : old_context = MemoryContextSwitchTo(agg_context);
929 : :
930 : 30 : state1 = initArrayResultArr(state2->array_type, InvalidOid,
931 : : agg_context, false);
932 : :
933 : 30 : state1->abytes = state2->abytes;
934 : 30 : state1->data = (char *) palloc(state1->abytes);
935 : :
936 [ + + ]: 30 : if (state2->nullbitmap)
937 : : {
938 : 15 : int size = (state2->aitems + 7) / 8;
939 : :
940 : 15 : state1->nullbitmap = (bits8 *) palloc(size);
941 : 15 : memcpy(state1->nullbitmap, state2->nullbitmap, size);
942 : : }
943 : :
944 : 30 : memcpy(state1->data, state2->data, state2->nbytes);
945 : 30 : state1->nbytes = state2->nbytes;
946 : 30 : state1->aitems = state2->aitems;
947 : 30 : state1->nitems = state2->nitems;
948 : 30 : state1->ndims = state2->ndims;
949 : 30 : memcpy(state1->dims, state2->dims, sizeof(state2->dims));
950 : 30 : memcpy(state1->lbs, state2->lbs, sizeof(state2->lbs));
951 : 30 : state1->array_type = state2->array_type;
952 : 30 : state1->element_type = state2->element_type;
953 : :
954 : 30 : MemoryContextSwitchTo(old_context);
955 : :
956 : 30 : PG_RETURN_POINTER(state1);
957 : : }
958 : :
959 : : /* We only need to combine the two states if state2 has any items */
960 [ + - ]: 30 : else if (state2->nitems > 0)
961 : : {
962 : : MemoryContext oldContext;
963 : 30 : 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 [ - + ]: 30 : if (state1->ndims != state2->ndims)
447 drowley@postgresql.o 973 [ # # ]:UBC 0 : 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. */
447 drowley@postgresql.o 978 [ + + ]:CBC 60 : for (i = 1; i < state1->ndims; i++)
979 : : {
980 [ + - - + ]: 30 : if (state1->dims[i] != state2->dims[i] || state1->lbs[i] != state2->lbs[i])
447 drowley@postgresql.o 981 [ # # ]:UBC 0 : ereport(ERROR,
982 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
983 : : errmsg("cannot accumulate arrays of different dimensionality")));
984 : : }
985 : :
986 : :
447 drowley@postgresql.o 987 :CBC 30 : 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 [ + + ]: 30 : if (state1->abytes < reqsize)
994 : : {
995 : : /* use a power of 2 size rather than allocating just reqsize */
996 : 15 : state1->abytes = pg_nextpower2_32(reqsize);
997 : 15 : state1->data = (char *) repalloc(state1->data, state1->abytes);
998 : : }
999 : :
1000 [ + + ]: 30 : if (state2->nullbitmap)
1001 : : {
1002 : 15 : int newnitems = state1->nitems + state2->nitems;
1003 : :
1004 [ - + ]: 15 : 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 : : */
447 drowley@postgresql.o 1010 :UBC 0 : state1->aitems = pg_nextpower2_32(Max(256, newnitems + 1));
1011 : 0 : state1->nullbitmap = (bits8 *) palloc((state1->aitems + 7) / 8);
1012 : 0 : array_bitmap_copy(state1->nullbitmap, 0,
1013 : : NULL, 0,
1014 : : state1->nitems);
1015 : : }
447 drowley@postgresql.o 1016 [ + - ]:CBC 15 : else if (newnitems > state1->aitems)
1017 : : {
1018 : 15 : int newaitems = state1->aitems + state2->aitems;
1019 : :
1020 : 15 : state1->aitems = pg_nextpower2_32(newaitems);
1021 : 15 : state1->nullbitmap = (bits8 *)
1022 : 15 : repalloc(state1->nullbitmap, (state1->aitems + 7) / 8);
1023 : : }
1024 : 15 : array_bitmap_copy(state1->nullbitmap, state1->nitems,
1025 : 15 : state2->nullbitmap, 0,
1026 : : state2->nitems);
1027 : : }
1028 : :
1029 : 30 : memcpy(state1->data + state1->nbytes, state2->data, state2->nbytes);
1030 : 30 : state1->nbytes += state2->nbytes;
1031 : 30 : state1->nitems += state2->nitems;
1032 : :
1033 : 30 : state1->dims[0] += state2->dims[0];
1034 : : /* remaining dims already match, per test above */
1035 : :
1036 [ - + ]: 30 : Assert(state1->array_type == state2->array_type);
1037 [ - + ]: 30 : Assert(state1->element_type == state2->element_type);
1038 : :
1039 : 30 : MemoryContextSwitchTo(oldContext);
1040 : : }
1041 : :
1042 : 30 : PG_RETURN_POINTER(state1);
1043 : : }
1044 : :
1045 : : /*
1046 : : * array_agg_array_serialize
1047 : : * Serialize ArrayBuildStateArr into bytea.
1048 : : */
1049 : : Datum
1050 : 60 : array_agg_array_serialize(PG_FUNCTION_ARGS)
1051 : : {
1052 : : ArrayBuildStateArr *state;
1053 : : StringInfoData buf;
1054 : : bytea *result;
1055 : :
1056 : : /* cannot be called directly because of internal-type argument */
1057 [ - + ]: 60 : Assert(AggCheckCallContext(fcinfo, NULL));
1058 : :
1059 : 60 : state = (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
1060 : :
1061 : 60 : 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 : 60 : pq_sendint32(&buf, state->element_type);
1068 : :
1069 : : /* array_type */
1070 : 60 : pq_sendint32(&buf, state->array_type);
1071 : :
1072 : : /* nbytes */
1073 : 60 : pq_sendint32(&buf, state->nbytes);
1074 : :
1075 : : /* data */
1076 : 60 : pq_sendbytes(&buf, state->data, state->nbytes);
1077 : :
1078 : : /* abytes */
1079 : 60 : pq_sendint32(&buf, state->abytes);
1080 : :
1081 : : /* aitems */
1082 : 60 : pq_sendint32(&buf, state->aitems);
1083 : :
1084 : : /* nullbitmap */
1085 [ + + ]: 60 : if (state->nullbitmap)
1086 : : {
1087 [ - + ]: 30 : Assert(state->aitems > 0);
425 peter@eisentraut.org 1088 : 30 : pq_sendbytes(&buf, state->nullbitmap, (state->aitems + 7) / 8);
1089 : : }
1090 : :
1091 : : /* nitems */
447 drowley@postgresql.o 1092 : 60 : pq_sendint32(&buf, state->nitems);
1093 : :
1094 : : /* ndims */
1095 : 60 : pq_sendint32(&buf, state->ndims);
1096 : :
1097 : : /* dims: XXX should we just send ndims elements? */
425 peter@eisentraut.org 1098 : 60 : pq_sendbytes(&buf, state->dims, sizeof(state->dims));
1099 : :
1100 : : /* lbs */
1101 : 60 : pq_sendbytes(&buf, state->lbs, sizeof(state->lbs));
1102 : :
447 drowley@postgresql.o 1103 : 60 : result = pq_endtypsend(&buf);
1104 : :
1105 : 60 : PG_RETURN_BYTEA_P(result);
1106 : : }
1107 : :
1108 : : Datum
1109 : 60 : array_agg_array_deserialize(PG_FUNCTION_ARGS)
1110 : : {
1111 : : bytea *sstate;
1112 : : ArrayBuildStateArr *result;
1113 : : StringInfoData buf;
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 [ - + ]: 60 : Assert(AggCheckCallContext(fcinfo, NULL));
1121 : :
1122 : 60 : 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 : : */
170 drowley@postgresql.o 1128 [ - + ]:GNC 60 : initReadOnlyStringInfo(&buf, VARDATA_ANY(sstate),
1129 [ - + - - : 60 : VARSIZE_ANY_EXHDR(sstate));
- - - - -
+ ]
1130 : :
1131 : : /* element_type */
447 drowley@postgresql.o 1132 :CBC 60 : element_type = pq_getmsgint(&buf, 4);
1133 : :
1134 : : /* array_type */
1135 : 60 : array_type = pq_getmsgint(&buf, 4);
1136 : :
1137 : : /* nbytes */
1138 : 60 : nbytes = pq_getmsgint(&buf, 4);
1139 : :
1140 : 60 : result = initArrayResultArr(array_type, element_type,
1141 : : CurrentMemoryContext, false);
1142 : :
1143 : 60 : result->abytes = 1024;
1144 [ + + ]: 70 : while (result->abytes < nbytes)
1145 : 10 : result->abytes *= 2;
1146 : :
1147 : 60 : result->data = (char *) palloc(result->abytes);
1148 : :
1149 : : /* data */
1150 : 60 : temp = pq_getmsgbytes(&buf, nbytes);
1151 : 60 : memcpy(result->data, temp, nbytes);
1152 : 60 : result->nbytes = nbytes;
1153 : :
1154 : : /* abytes */
1155 : 60 : result->abytes = pq_getmsgint(&buf, 4);
1156 : :
1157 : : /* aitems: might be 0 */
1158 : 60 : result->aitems = pq_getmsgint(&buf, 4);
1159 : :
1160 : : /* nullbitmap */
1161 [ + + ]: 60 : if (result->aitems > 0)
1162 : : {
1163 : 30 : int size = (result->aitems + 7) / 8;
1164 : :
1165 : 30 : result->nullbitmap = (bits8 *) palloc(size);
1166 : 30 : temp = pq_getmsgbytes(&buf, size);
1167 : 30 : memcpy(result->nullbitmap, temp, size);
1168 : : }
1169 : : else
1170 : 30 : result->nullbitmap = NULL;
1171 : :
1172 : : /* nitems */
1173 : 60 : result->nitems = pq_getmsgint(&buf, 4);
1174 : :
1175 : : /* ndims */
1176 : 60 : result->ndims = pq_getmsgint(&buf, 4);
1177 : :
1178 : : /* dims */
1179 : 60 : temp = pq_getmsgbytes(&buf, sizeof(result->dims));
1180 : 60 : memcpy(result->dims, temp, sizeof(result->dims));
1181 : :
1182 : : /* lbs */
1183 : 60 : temp = pq_getmsgbytes(&buf, sizeof(result->lbs));
1184 : 60 : memcpy(result->lbs, temp, sizeof(result->lbs));
1185 : :
1186 : 60 : pq_getmsgend(&buf);
1187 : :
1188 : 60 : PG_RETURN_POINTER(result);
1189 : : }
1190 : :
1191 : : Datum
3428 tgl@sss.pgh.pa.us 1192 : 81 : array_agg_array_finalfn(PG_FUNCTION_ARGS)
1193 : : {
1194 : : Datum result;
1195 : : ArrayBuildStateArr *state;
1196 : :
1197 : : /* cannot be called directly because of internal-type argument */
1198 [ - + ]: 81 : Assert(AggCheckCallContext(fcinfo, NULL));
1199 : :
1200 [ + - ]: 81 : state = PG_ARGISNULL(0) ? NULL : (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
1201 : :
1202 [ - + ]: 81 : if (state == NULL)
3428 tgl@sss.pgh.pa.us 1203 :UBC 0 : 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 : : */
3428 tgl@sss.pgh.pa.us 1211 :CBC 81 : result = makeArrayResultArr(state, CurrentMemoryContext, false);
1212 : :
1213 : 81 : 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
3303 alvherre@alvh.no-ip. 1225 : 108 : array_position(PG_FUNCTION_ARGS)
1226 : : {
1227 : 108 : return array_position_common(fcinfo);
1228 : : }
1229 : :
1230 : : Datum
1231 : 9 : array_position_start(PG_FUNCTION_ARGS)
1232 : : {
1233 : 9 : 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
1244 : 117 : array_position_common(FunctionCallInfo fcinfo)
1245 : : {
1246 : : ArrayType *array;
3315 1247 : 117 : 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 : 117 : bool found = false;
1255 : : TypeCacheEntry *typentry;
1256 : : ArrayMetaState *my_extra;
1257 : : bool null_search;
1258 : : ArrayIterator array_iterator;
1259 : :
1260 [ - + ]: 117 : if (PG_ARGISNULL(0))
3315 alvherre@alvh.no-ip. 1261 :UBC 0 : PG_RETURN_NULL();
1262 : :
3315 alvherre@alvh.no-ip. 1263 :CBC 117 : 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 [ + + ]: 117 : if (ARR_NDIM(array) > 1)
1270 [ + - ]: 3 : 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 */
346 tgl@sss.pgh.pa.us 1275 [ - + ]: 114 : if (ARR_NDIM(array) < 1)
346 tgl@sss.pgh.pa.us 1276 :UBC 0 : PG_RETURN_NULL();
1277 : :
3315 alvherre@alvh.no-ip. 1278 [ + + ]:CBC 114 : if (PG_ARGISNULL(1))
1279 : : {
1280 : : /* fast return when the array doesn't have nulls */
1281 [ + + ]: 6 : if (!array_contains_nulls(array))
1282 : 3 : PG_RETURN_NULL();
1283 : 3 : searched_element = (Datum) 0;
1284 : 3 : null_search = true;
1285 : : }
1286 : : else
1287 : : {
1288 : 108 : searched_element = PG_GETARG_DATUM(1);
1289 : 108 : null_search = false;
1290 : : }
1291 : :
346 tgl@sss.pgh.pa.us 1292 : 111 : element_type = ARR_ELEMTYPE(array);
3303 alvherre@alvh.no-ip. 1293 : 111 : position = (ARR_LBOUND(array))[0] - 1;
1294 : :
1295 : : /* figure out where to start */
3315 1296 [ + + ]: 111 : if (PG_NARGS() == 3)
1297 : : {
1298 [ - + ]: 9 : if (PG_ARGISNULL(2))
3315 alvherre@alvh.no-ip. 1299 [ # # ]:UBC 0 : ereport(ERROR,
1300 : : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
1301 : : errmsg("initial position must not be null")));
1302 : :
3303 alvherre@alvh.no-ip. 1303 :CBC 9 : position_min = PG_GETARG_INT32(2);
1304 : : }
1305 : : else
1306 : 102 : 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 : : */
3315 1313 : 111 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1314 [ + + ]: 111 : if (my_extra == NULL)
1315 : : {
1316 : 42 : fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1317 : : sizeof(ArrayMetaState));
1318 : 42 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1319 : 42 : my_extra->element_type = ~element_type;
1320 : : }
1321 : :
1322 [ + + ]: 111 : if (my_extra->element_type != element_type)
1323 : : {
1324 : 42 : get_typlenbyvalalign(element_type,
1325 : : &my_extra->typlen,
1326 : : &my_extra->typbyval,
1327 : : &my_extra->typalign);
1328 : :
1329 : 42 : typentry = lookup_type_cache(element_type, TYPECACHE_EQ_OPR_FINFO);
1330 : :
1331 [ - + ]: 42 : if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
3315 alvherre@alvh.no-ip. 1332 [ # # ]:UBC 0 : 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 : :
3315 alvherre@alvh.no-ip. 1337 :CBC 42 : my_extra->element_type = element_type;
2683 1338 : 42 : fmgr_info_cxt(typentry->eq_opr_finfo.fn_oid, &my_extra->proc,
1339 : 42 : fcinfo->flinfo->fn_mcxt);
1340 : : }
1341 : :
1342 : : /* Examine each array element until we find a match. */
3315 1343 : 111 : array_iterator = array_create_iterator(array, 0, my_extra);
1344 [ + + ]: 309 : while (array_iterate(array_iterator, &value, &isnull))
1345 : : {
3303 1346 : 288 : position++;
1347 : :
1348 : : /* skip initial elements if caller requested so */
1349 [ + + ]: 288 : if (position < position_min)
3315 1350 : 39 : 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 [ + + + + ]: 249 : if (isnull || null_search)
1357 : : {
1358 [ + + + + ]: 21 : if (isnull && null_search)
1359 : : {
1360 : 3 : found = true;
1361 : 3 : break;
1362 : : }
1363 : : else
1364 : 18 : continue;
1365 : : }
1366 : :
1367 : : /* not nulls, so run the operator */
1368 [ + + ]: 228 : if (DatumGetBool(FunctionCall2Coll(&my_extra->proc, collation,
1369 : : searched_element, value)))
1370 : : {
1371 : 87 : found = true;
1372 : 87 : break;
1373 : : }
1374 : : }
1375 : :
1376 : 111 : array_free_iterator(array_iterator);
1377 : :
1378 : : /* Avoid leaking memory when handed toasted input */
1379 [ + + ]: 111 : PG_FREE_IF_COPY(array, 0);
1380 : :
1381 [ + + ]: 111 : if (!found)
1382 : 21 : PG_RETURN_NULL();
1383 : :
3303 1384 : 90 : 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
1399 : 30 : array_positions(PG_FUNCTION_ARGS)
1400 : : {
1401 : : ArrayType *array;
3315 1402 : 30 : 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;
1411 : : ArrayIterator array_iterator;
1412 : 30 : ArrayBuildState *astate = NULL;
1413 : :
1414 [ + + ]: 30 : if (PG_ARGISNULL(0))
1415 : 6 : PG_RETURN_NULL();
1416 : :
1417 : 24 : 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 [ + + ]: 24 : if (ARR_NDIM(array) > 1)
1424 [ + - ]: 3 : ereport(ERROR,
1425 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1426 : : errmsg("searching for elements in multidimensional arrays is not supported")));
1427 : :
1428 : 21 : astate = initArrayResult(INT4OID, CurrentMemoryContext, false);
1429 : :
1430 : : /* Searching in an empty array is well-defined, though: it always fails */
346 tgl@sss.pgh.pa.us 1431 [ - + ]: 21 : if (ARR_NDIM(array) < 1)
346 tgl@sss.pgh.pa.us 1432 :UBC 0 : PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
1433 : :
3315 alvherre@alvh.no-ip. 1434 [ + + ]:CBC 21 : if (PG_ARGISNULL(1))
1435 : : {
1436 : : /* fast return when the array doesn't have nulls */
1437 [ + + ]: 6 : if (!array_contains_nulls(array))
1438 : 3 : PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
1439 : 3 : searched_element = (Datum) 0;
1440 : 3 : null_search = true;
1441 : : }
1442 : : else
1443 : : {
1444 : 15 : searched_element = PG_GETARG_DATUM(1);
1445 : 15 : null_search = false;
1446 : : }
1447 : :
346 tgl@sss.pgh.pa.us 1448 : 18 : element_type = ARR_ELEMTYPE(array);
1449 : 18 : 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 : : */
3315 alvherre@alvh.no-ip. 1456 : 18 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1457 [ + + ]: 18 : if (my_extra == NULL)
1458 : : {
1459 : 15 : fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1460 : : sizeof(ArrayMetaState));
1461 : 15 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1462 : 15 : my_extra->element_type = ~element_type;
1463 : : }
1464 : :
1465 [ + + ]: 18 : if (my_extra->element_type != element_type)
1466 : : {
1467 : 15 : get_typlenbyvalalign(element_type,
1468 : : &my_extra->typlen,
1469 : : &my_extra->typbyval,
1470 : : &my_extra->typalign);
1471 : :
1472 : 15 : typentry = lookup_type_cache(element_type, TYPECACHE_EQ_OPR_FINFO);
1473 : :
1474 [ - + ]: 15 : if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
3315 alvherre@alvh.no-ip. 1475 [ # # ]:UBC 0 : 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 : :
3315 alvherre@alvh.no-ip. 1480 :CBC 15 : my_extra->element_type = element_type;
2683 1481 : 15 : fmgr_info_cxt(typentry->eq_opr_finfo.fn_oid, &my_extra->proc,
1482 : 15 : fcinfo->flinfo->fn_mcxt);
1483 : : }
1484 : :
1485 : : /*
1486 : : * Accumulate each array position iff the element matches the given
1487 : : * element.
1488 : : */
3315 1489 : 18 : array_iterator = array_create_iterator(array, 0, my_extra);
1490 [ + + ]: 408 : while (array_iterate(array_iterator, &value, &isnull))
1491 : : {
3303 1492 : 390 : 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 : : */
3315 1498 [ + + + + ]: 390 : if (isnull || null_search)
1499 : : {
1500 [ + + + - ]: 36 : if (isnull && null_search)
1501 : : astate =
3303 1502 : 6 : accumArrayResult(astate, Int32GetDatum(position), false,
1503 : : INT4OID, CurrentMemoryContext);
1504 : :
3315 1505 : 36 : continue;
1506 : : }
1507 : :
1508 : : /* not nulls, so run the operator */
1509 [ + + ]: 354 : if (DatumGetBool(FunctionCall2Coll(&my_extra->proc, collation,
1510 : : searched_element, value)))
1511 : : astate =
3303 1512 : 45 : accumArrayResult(astate, Int32GetDatum(position), false,
1513 : : INT4OID, CurrentMemoryContext);
1514 : : }
1515 : :
3315 1516 : 18 : array_free_iterator(array_iterator);
1517 : :
1518 : : /* Avoid leaking memory when handed toasted input */
1519 [ - + ]: 18 : PG_FREE_IF_COPY(array, 0);
1520 : :
1521 : 18 : PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
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 *
373 tgl@sss.pgh.pa.us 1537 : 24 : 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 : 24 : ndim = ARR_NDIM(array);
1557 : 24 : dims = ARR_DIMS(array);
1558 : 24 : lbs = ARR_LBOUND(array);
1559 : :
1560 : 24 : elmlen = typentry->typlen;
1561 : 24 : elmbyval = typentry->typbyval;
1562 : 24 : elmalign = typentry->typalign;
1563 : :
1564 : : /* If the target array is empty, exit fast */
1565 [ + - + - : 24 : if (ndim < 1 || dims[0] < 1 || n < 1)
- + ]
373 tgl@sss.pgh.pa.us 1566 :UBC 0 : return construct_empty_array(elmtyp);
1567 : :
373 tgl@sss.pgh.pa.us 1568 :CBC 24 : deconstruct_array(array, elmtyp, elmlen, elmbyval, elmalign,
1569 : : &elms, &nuls, &nelm);
1570 : :
1571 : 24 : nitem = dims[0]; /* total number of items */
1572 : 24 : nelm /= nitem; /* number of elements per item */
1573 : :
1574 [ - + ]: 24 : 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 : 24 : ielms = elms;
1583 : 24 : inuls = nuls;
1584 [ + + ]: 114 : for (int i = 0; i < n; i++)
1585 : : {
1586 : 90 : int j = (int) pg_prng_uint64_range(&pg_global_prng_state, i, nitem - 1) * nelm;
1587 : 90 : Datum *jelms = elms + j;
1588 : 90 : bool *jnuls = nuls + j;
1589 : :
1590 : : /* Swap i'th and j'th items; advance ielms/inuls to next item */
1591 [ + + ]: 246 : for (int k = 0; k < nelm; k++)
1592 : : {
1593 : 156 : Datum elm = *ielms;
1594 : 156 : bool nul = *inuls;
1595 : :
1596 : 156 : *ielms++ = *jelms;
1597 : 156 : *inuls++ = *jnuls;
1598 : 156 : *jelms++ = elm;
1599 : 156 : *jnuls++ = nul;
1600 : : }
1601 : : }
1602 : :
1603 : : /* Set up dimensions of the result */
1604 : 24 : memcpy(rdims, dims, ndim * sizeof(int));
1605 : 24 : memcpy(rlbs, lbs, ndim * sizeof(int));
1606 : 24 : rdims[0] = n;
1607 [ + + ]: 24 : if (!keep_lb)
1608 : 12 : rlbs[0] = 1;
1609 : :
1610 : 24 : result = construct_md_array(elms, nuls, ndim, rdims, rlbs,
1611 : : elmtyp, elmlen, elmbyval, elmalign);
1612 : :
1613 : 24 : pfree(elms);
1614 : 24 : pfree(nuls);
1615 : :
1616 : 24 : 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
1626 : 12 : array_shuffle(PG_FUNCTION_ARGS)
1627 : : {
1628 : 12 : 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 [ + - - + ]: 12 : if (ARR_NDIM(array) < 1 || ARR_DIMS(array)[0] < 2)
373 tgl@sss.pgh.pa.us 1638 :UBC 0 : PG_RETURN_ARRAYTYPE_P(array);
1639 : :
373 tgl@sss.pgh.pa.us 1640 :CBC 12 : elmtyp = ARR_ELEMTYPE(array);
1641 : 12 : typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
1642 [ - + - - ]: 12 : if (typentry == NULL || typentry->type_id != elmtyp)
1643 : : {
1644 : 12 : typentry = lookup_type_cache(elmtyp, 0);
1645 : 12 : fcinfo->flinfo->fn_extra = (void *) typentry;
1646 : : }
1647 : :
1648 : 12 : result = array_shuffle_n(array, ARR_DIMS(array)[0], true, elmtyp, typentry);
1649 : :
1650 : 12 : 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
1660 : 18 : array_sample(PG_FUNCTION_ARGS)
1661 : : {
1662 : 18 : ArrayType *array = PG_GETARG_ARRAYTYPE_P(0);
1663 : 18 : int n = PG_GETARG_INT32(1);
1664 : : ArrayType *result;
1665 : : Oid elmtyp;
1666 : : TypeCacheEntry *typentry;
1667 : : int nitem;
1668 : :
1669 [ + - ]: 18 : nitem = (ARR_NDIM(array) < 1) ? 0 : ARR_DIMS(array)[0];
1670 : :
1671 [ + + + + ]: 18 : if (n < 0 || n > nitem)
1672 [ + - ]: 6 : ereport(ERROR,
1673 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1674 : : errmsg("sample size must be between 0 and %d", nitem)));
1675 : :
1676 : 12 : elmtyp = ARR_ELEMTYPE(array);
1677 : 12 : typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
1678 [ - + - - ]: 12 : if (typentry == NULL || typentry->type_id != elmtyp)
1679 : : {
1680 : 12 : typentry = lookup_type_cache(elmtyp, 0);
1681 : 12 : fcinfo->flinfo->fn_extra = (void *) typentry;
1682 : : }
1683 : :
1684 : 12 : result = array_shuffle_n(array, n, false, elmtyp, typentry);
1685 : :
1686 : 12 : PG_RETURN_ARRAYTYPE_P(result);
1687 : : }
|