Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * arrayfuncs.c
4 : : * Support functions for arrays.
5 : : *
6 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : *
10 : : * IDENTIFICATION
11 : : * src/backend/utils/adt/arrayfuncs.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : : #include "postgres.h"
16 : :
17 : : #include <ctype.h>
18 : : #include <math.h>
19 : :
20 : : #include "access/transam.h"
21 : : #include "catalog/pg_type.h"
22 : : #include "common/int.h"
23 : : #include "funcapi.h"
24 : : #include "libpq/pqformat.h"
25 : : #include "nodes/nodeFuncs.h"
26 : : #include "nodes/supportnodes.h"
27 : : #include "optimizer/optimizer.h"
28 : : #include "parser/scansup.h"
29 : : #include "port/pg_bitutils.h"
30 : : #include "utils/array.h"
31 : : #include "utils/arrayaccess.h"
32 : : #include "utils/builtins.h"
33 : : #include "utils/datum.h"
34 : : #include "utils/fmgroids.h"
35 : : #include "utils/lsyscache.h"
36 : : #include "utils/memutils.h"
37 : : #include "utils/selfuncs.h"
38 : : #include "utils/typcache.h"
39 : :
40 : :
41 : : /*
42 : : * GUC parameter
43 : : */
44 : : bool Array_nulls = true;
45 : :
46 : : /*
47 : : * Local definitions
48 : : */
49 : : #define ASSGN "="
50 : :
51 : : #define AARR_FREE_IF_COPY(array,n) \
52 : : do { \
53 : : if (!VARATT_IS_EXPANDED_HEADER(array)) \
54 : : PG_FREE_IF_COPY(array, n); \
55 : : } while (0)
56 : :
57 : : /* ReadArrayToken return type */
58 : : typedef enum
59 : : {
60 : : ATOK_LEVEL_START,
61 : : ATOK_LEVEL_END,
62 : : ATOK_DELIM,
63 : : ATOK_ELEM,
64 : : ATOK_ELEM_NULL,
65 : : ATOK_ERROR,
66 : : } ArrayToken;
67 : :
68 : : /* Working state for array_iterate() */
69 : : typedef struct ArrayIteratorData
70 : : {
71 : : /* basic info about the array, set up during array_create_iterator() */
72 : : ArrayType *arr; /* array we're iterating through */
73 : : bits8 *nullbitmap; /* its null bitmap, if any */
74 : : int nitems; /* total number of elements in array */
75 : : int16 typlen; /* element type's length */
76 : : bool typbyval; /* element type's byval property */
77 : : char typalign; /* element type's align property */
78 : : uint8 typalignby; /* typalign mapped to numeric alignment */
79 : :
80 : : /* information about the requested slice size */
81 : : int slice_ndim; /* slice dimension, or 0 if not slicing */
82 : : int slice_len; /* number of elements per slice */
83 : : int *slice_dims; /* slice dims array */
84 : : int *slice_lbound; /* slice lbound array */
85 : : Datum *slice_values; /* workspace of length slice_len */
86 : : bool *slice_nulls; /* workspace of length slice_len */
87 : :
88 : : /* current position information, updated on each iteration */
89 : : char *data_ptr; /* our current position in the array */
90 : : int current_item; /* the item # we're at in the array */
91 : : } ArrayIteratorData;
92 : :
93 : : static bool ReadArrayDimensions(char **srcptr, int *ndim_p,
94 : : int *dim, int *lBound,
95 : : const char *origStr, Node *escontext);
96 : : static bool ReadDimensionInt(char **srcptr, int *result,
97 : : const char *origStr, Node *escontext);
98 : : static bool ReadArrayStr(char **srcptr,
99 : : FmgrInfo *inputproc, Oid typioparam, int32 typmod,
100 : : char typdelim,
101 : : int typlen, bool typbyval, char typalign,
102 : : int *ndim_p, int *dim,
103 : : int *nitems_p,
104 : : Datum **values_p, bool **nulls_p,
105 : : const char *origStr, Node *escontext);
106 : : static ArrayToken ReadArrayToken(char **srcptr, StringInfo elembuf, char typdelim,
107 : : const char *origStr, Node *escontext);
108 : : static void ReadArrayBinary(StringInfo buf, int nitems,
109 : : FmgrInfo *receiveproc, Oid typioparam, int32 typmod,
110 : : int typlen, bool typbyval, char typalign,
111 : : Datum *values, bool *nulls,
112 : : bool *hasnulls, int32 *nbytes);
113 : : static Datum array_get_element_expanded(Datum arraydatum,
114 : : int nSubscripts, int *indx,
115 : : int arraytyplen,
116 : : int elmlen, bool elmbyval, char elmalign,
117 : : bool *isNull);
118 : : static Datum array_set_element_expanded(Datum arraydatum,
119 : : int nSubscripts, int *indx,
120 : : Datum dataValue, bool isNull,
121 : : int arraytyplen,
122 : : int elmlen, bool elmbyval, char elmalign);
123 : : static bool array_get_isnull(const bits8 *nullbitmap, int offset);
124 : : static void array_set_isnull(bits8 *nullbitmap, int offset, bool isNull);
125 : : static Datum ArrayCast(char *value, bool byval, int len);
126 : : static int ArrayCastAndSet(Datum src,
127 : : int typlen, bool typbyval, uint8 typalignby,
128 : : char *dest);
129 : : static char *array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
130 : : int typlen, bool typbyval, char typalign);
131 : : static int array_nelems_size(char *ptr, int offset, bits8 *nullbitmap,
132 : : int nitems, int typlen, bool typbyval, char typalign);
133 : : static int array_copy(char *destptr, int nitems,
134 : : char *srcptr, int offset, bits8 *nullbitmap,
135 : : int typlen, bool typbyval, char typalign);
136 : : static int array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
137 : : int ndim, int *dim, int *lb,
138 : : int *st, int *endp,
139 : : int typlen, bool typbyval, char typalign);
140 : : static void array_extract_slice(ArrayType *newarray,
141 : : int ndim, int *dim, int *lb,
142 : : char *arraydataptr, bits8 *arraynullsptr,
143 : : int *st, int *endp,
144 : : int typlen, bool typbyval, char typalign);
145 : : static void array_insert_slice(ArrayType *destArray, ArrayType *origArray,
146 : : ArrayType *srcArray,
147 : : int ndim, int *dim, int *lb,
148 : : int *st, int *endp,
149 : : int typlen, bool typbyval, char typalign);
150 : : static int array_cmp(FunctionCallInfo fcinfo);
151 : : static ArrayType *create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
152 : : Oid elmtype, int dataoffset);
153 : : static ArrayType *array_fill_internal(ArrayType *dims, ArrayType *lbs,
154 : : Datum value, bool isnull, Oid elmtype,
155 : : FunctionCallInfo fcinfo);
156 : : static ArrayType *array_replace_internal(ArrayType *array,
157 : : Datum search, bool search_isnull,
158 : : Datum replace, bool replace_isnull,
159 : : bool remove, Oid collation,
160 : : FunctionCallInfo fcinfo);
161 : : static int width_bucket_array_float8(Datum operand, ArrayType *thresholds);
162 : : static int width_bucket_array_fixed(Datum operand,
163 : : ArrayType *thresholds,
164 : : Oid collation,
165 : : TypeCacheEntry *typentry);
166 : : static int width_bucket_array_variable(Datum operand,
167 : : ArrayType *thresholds,
168 : : Oid collation,
169 : : TypeCacheEntry *typentry);
170 : :
171 : :
172 : : /*
173 : : * array_in :
174 : : * converts an array from the external format in "string" to
175 : : * its internal format.
176 : : *
177 : : * return value :
178 : : * the internal representation of the input array
179 : : */
180 : : Datum
9406 tgl@sss.pgh.pa.us 181 :CBC 131037 : array_in(PG_FUNCTION_ARGS)
182 : : {
9124 bruce@momjian.us 183 : 131037 : char *string = PG_GETARG_CSTRING(0); /* external form */
3189 tgl@sss.pgh.pa.us 184 : 131037 : Oid element_type = PG_GETARG_OID(1); /* type of an array
185 : : * element */
7456 bruce@momjian.us 186 : 131037 : int32 typmod = PG_GETARG_INT32(2); /* typmod for array elements */
1192 tgl@sss.pgh.pa.us 187 : 131037 : Node *escontext = fcinfo->context;
188 : : int typlen;
189 : : bool typbyval;
190 : : char typalign;
191 : : uint8 typalignby;
192 : : char typdelim;
193 : : Oid typioparam;
194 : : char *p;
195 : : int nitems;
196 : : Datum *values;
197 : : bool *nulls;
198 : : bool hasnulls;
199 : : int32 nbytes;
200 : : int32 dataoffset;
201 : : ArrayType *retval;
202 : : int ndim,
203 : : dim[MAXDIM],
204 : : lBound[MAXDIM];
205 : : ArrayMetaState *my_extra;
206 : :
207 : : /*
208 : : * We arrange to look up info about element type, including its input
209 : : * conversion proc, only once per series of calls, assuming the element
210 : : * type doesn't change underneath us.
211 : : */
8297 212 : 131037 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
213 [ + + ]: 131037 : if (my_extra == NULL)
214 : : {
215 : 45999 : fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
216 : : sizeof(ArrayMetaState));
217 : 45999 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
7517 218 : 45999 : my_extra->element_type = ~element_type;
219 : : }
220 : :
8297 221 [ + + ]: 131037 : if (my_extra->element_type != element_type)
222 : : {
223 : : /*
224 : : * Get info about element type, including its input conversion proc
225 : : */
226 : 46102 : get_type_io_data(element_type, IOFunc_input,
227 : : &my_extra->typlen, &my_extra->typbyval,
228 : : &my_extra->typalign, &my_extra->typdelim,
229 : : &my_extra->typioparam, &my_extra->typiofunc);
230 : 46102 : fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
231 : 46102 : fcinfo->flinfo->fn_mcxt);
232 : 46102 : my_extra->element_type = element_type;
233 : : }
234 : 131037 : typlen = my_extra->typlen;
235 : 131037 : typbyval = my_extra->typbyval;
236 : 131037 : typalign = my_extra->typalign;
41 tgl@sss.pgh.pa.us 237 :GNC 131037 : typalignby = typalign_to_alignby(typalign);
8297 tgl@sss.pgh.pa.us 238 :CBC 131037 : typdelim = my_extra->typdelim;
7952 239 : 131037 : typioparam = my_extra->typioparam;
240 : :
241 : : /*
242 : : * Initialize dim[] and lBound[] for ReadArrayStr, in case there is no
243 : : * explicit dimension info. (If there is, ReadArrayDimensions will
244 : : * overwrite this.)
245 : : */
853 246 [ + + ]: 917259 : for (int i = 0; i < MAXDIM; i++)
247 : : {
248 : 786222 : dim[i] = -1; /* indicates "not yet known" */
249 : 786222 : lBound[i] = 1; /* default lower bound */
250 : : }
251 : :
252 : : /*
253 : : * Start processing the input string.
254 : : *
255 : : * If the input string starts with dimension info, read and use that.
256 : : * Otherwise, we'll determine the dimensions during ReadArrayStr.
257 : : */
258 : 131037 : p = string;
259 [ - + ]: 131037 : if (!ReadArrayDimensions(&p, &ndim, dim, lBound, string, escontext))
853 tgl@sss.pgh.pa.us 260 :UBC 0 : return (Datum) 0;
261 : :
10416 bruce@momjian.us 262 [ + + ]:CBC 131016 : if (ndim == 0)
263 : : {
264 : : /* No array dimensions, so next character should be a left brace */
9252 tgl@sss.pgh.pa.us 265 [ + + ]: 130942 : if (*p != '{')
1192 266 [ + + ]: 7 : ereturn(escontext, (Datum) 0,
267 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
268 : : errmsg("malformed array literal: \"%s\"", string),
269 : : errdetail("Array value must start with \"{\" or dimension information.")));
270 : : }
271 : : else
272 : : {
273 : : /* If array dimensions are given, expect '=' operator */
9366 274 [ - + ]: 74 : if (strncmp(p, ASSGN, strlen(ASSGN)) != 0)
1192 tgl@sss.pgh.pa.us 275 [ # # ]:UBC 0 : ereturn(escontext, (Datum) 0,
276 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
277 : : errmsg("malformed array literal: \"%s\"", string),
278 : : errdetail("Missing \"%s\" after array dimensions.",
279 : : ASSGN)));
10416 bruce@momjian.us 280 :CBC 74 : p += strlen(ASSGN);
281 : : /* Allow whitespace after it */
983 michael@paquier.xyz 282 [ - + ]: 74 : while (scanner_isspace(*p))
10416 bruce@momjian.us 283 :UBC 0 : p++;
284 : :
7892 mail@joeconway.com 285 [ - + ]:CBC 74 : if (*p != '{')
1192 tgl@sss.pgh.pa.us 286 [ # # ]:UBC 0 : ereturn(escontext, (Datum) 0,
287 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
288 : : errmsg("malformed array literal: \"%s\"", string),
289 : : errdetail("Array contents must start with \"{\".")));
290 : : }
291 : :
292 : : /* Parse the value part, in the curly braces: { ... } */
853 tgl@sss.pgh.pa.us 293 [ + + ]:CBC 131009 : if (!ReadArrayStr(&p,
294 : : &my_extra->proc, typioparam, typmod,
295 : : typdelim,
296 : : typlen, typbyval, typalign,
297 : : &ndim,
298 : : dim,
299 : : &nitems,
300 : : &values, &nulls,
301 : : string,
302 : : escontext))
303 : 39 : return (Datum) 0;
304 : :
305 : : /* only whitespace is allowed after the closing brace */
306 [ + + ]: 130895 : while (*p)
307 : : {
308 [ + - ]: 6 : if (!scanner_isspace(*p++))
1192 309 [ + - ]: 6 : ereturn(escontext, (Datum) 0,
310 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
311 : : errmsg("malformed array literal: \"%s\"", string),
312 : : errdetail("Junk after closing right brace.")));
313 : : }
314 : :
315 : : /* Empty array? */
10416 bruce@momjian.us 316 [ + + ]: 130889 : if (nitems == 0)
7423 tgl@sss.pgh.pa.us 317 : 2500 : PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
318 : :
319 : : /*
320 : : * Check for nulls, compute total data space needed
321 : : */
853 322 : 128389 : hasnulls = false;
323 : 128389 : nbytes = 0;
324 [ + + ]: 757310 : for (int i = 0; i < nitems; i++)
325 : : {
326 [ + + ]: 628921 : if (nulls[i])
327 : 412 : hasnulls = true;
328 : : else
329 : : {
330 : : /* let's just make sure data is not toasted */
331 [ + + ]: 628509 : if (typlen == -1)
332 : 358995 : values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
333 [ + + + + : 628509 : nbytes = att_addlength_datum(nbytes, typlen, values[i]);
- + - - -
- - - - +
- - ]
41 tgl@sss.pgh.pa.us 334 :GNC 628509 : nbytes = att_nominal_alignby(nbytes, typalignby);
335 : : /* check for overflow of total request */
853 tgl@sss.pgh.pa.us 336 [ - + ]:CBC 628509 : if (!AllocSizeIsValid(nbytes))
853 tgl@sss.pgh.pa.us 337 [ # # ]:UBC 0 : ereturn(escontext, (Datum) 0,
338 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
339 : : errmsg("array size exceeds the maximum allowed (%zu)",
340 : : MaxAllocSize)));
341 : : }
342 : : }
7423 tgl@sss.pgh.pa.us 343 [ + + ]:CBC 128389 : if (hasnulls)
344 : : {
345 : 372 : dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
346 : 372 : nbytes += dataoffset;
347 : : }
348 : : else
349 : : {
350 : 128017 : dataoffset = 0; /* marker for no null bitmap */
351 : 128017 : nbytes += ARR_OVERHEAD_NONULLS(ndim);
352 : : }
353 : :
354 : : /*
355 : : * Construct the final array datum
356 : : */
6547 357 : 128389 : retval = (ArrayType *) palloc0(nbytes);
6956 358 : 128389 : SET_VARSIZE(retval, nbytes);
9367 359 : 128389 : retval->ndim = ndim;
7423 360 : 128389 : retval->dataoffset = dataoffset;
361 : :
362 : : /*
363 : : * This comes from the array's pg_type.typelem (which points to the base
364 : : * data type's pg_type.oid) and stores system oids in user tables. This
365 : : * oid must be preserved by binary upgrades.
366 : : */
8602 367 : 128389 : retval->elemtype = element_type;
8103 neilc@samurai.com 368 : 128389 : memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
369 : 128389 : memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
370 : :
7423 tgl@sss.pgh.pa.us 371 : 128389 : CopyArrayEls(retval,
372 : : values, nulls, nitems,
373 : : typlen, typbyval, typalign,
374 : : true);
375 : :
853 376 : 128389 : pfree(values);
377 : 128389 : pfree(nulls);
378 : :
9372 379 : 128389 : PG_RETURN_ARRAYTYPE_P(retval);
380 : : }
381 : :
382 : : /*
383 : : * ReadArrayDimensions
384 : : * parses the array dimensions part of the input and converts the values
385 : : * to internal format.
386 : : *
387 : : * On entry, *srcptr points to the string to parse. It is advanced to point
388 : : * after whitespace (if any) and dimension info (if any).
389 : : *
390 : : * *ndim_p, dim[], and lBound[] are output variables. They are filled with the
391 : : * number of dimensions (<= MAXDIM), the lengths of each dimension, and the
392 : : * lower subscript bounds, respectively. If no dimension info appears,
393 : : * *ndim_p will be set to zero, and dim[] and lBound[] are unchanged.
394 : : *
395 : : * 'origStr' is the original input string, used only in error messages.
396 : : * If *escontext points to an ErrorSaveContext, details of any error are
397 : : * reported there.
398 : : *
399 : : * Result:
400 : : * true for success, false for failure (if escontext is provided).
401 : : *
402 : : * Note that dim[] and lBound[] are allocated by the caller, and must have
403 : : * MAXDIM elements.
404 : : */
405 : : static bool
853 406 : 131037 : ReadArrayDimensions(char **srcptr, int *ndim_p, int *dim, int *lBound,
407 : : const char *origStr, Node *escontext)
408 : : {
409 : 131037 : char *p = *srcptr;
410 : : int ndim;
411 : :
412 : : /*
413 : : * Dimension info takes the form of one or more [n] or [m:n] items. This
414 : : * loop iterates once per dimension item.
415 : : */
416 : 131037 : ndim = 0;
417 : : for (;;)
10416 bruce@momjian.us 418 : 102 : {
419 : : char *q;
420 : : int ub;
421 : : int i;
422 : :
423 : : /*
424 : : * Note: we currently allow whitespace between, but not within,
425 : : * dimension items.
426 : : */
853 tgl@sss.pgh.pa.us 427 [ + + ]: 131145 : while (scanner_isspace(*p))
428 : 6 : p++;
429 [ + + ]: 131139 : if (*p != '[')
430 : 131016 : break; /* no more dimension items */
431 : 123 : p++;
432 [ - + ]: 123 : if (ndim >= MAXDIM)
853 tgl@sss.pgh.pa.us 433 [ # # ]:UBC 0 : ereturn(escontext, false,
434 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
435 : : errmsg("number of array dimensions exceeds the maximum allowed (%d)",
436 : : MAXDIM)));
437 : :
853 tgl@sss.pgh.pa.us 438 :CBC 123 : q = p;
439 [ - + ]: 123 : if (!ReadDimensionInt(&p, &i, origStr, escontext))
853 tgl@sss.pgh.pa.us 440 :UBC 0 : return false;
853 tgl@sss.pgh.pa.us 441 [ + + ]:CBC 117 : if (p == q) /* no digits? */
442 [ + - ]: 3 : ereturn(escontext, false,
443 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
444 : : errmsg("malformed array literal: \"%s\"", origStr),
445 : : errdetail("\"[\" must introduce explicitly-specified array dimensions.")));
446 : :
447 [ + + ]: 114 : if (*p == ':')
448 : : {
449 : : /* [m:n] format */
450 : 108 : lBound[ndim] = i;
451 : 108 : p++;
452 : 108 : q = p;
453 [ - + ]: 108 : if (!ReadDimensionInt(&p, &ub, origStr, escontext))
853 tgl@sss.pgh.pa.us 454 :UBC 0 : return false;
853 tgl@sss.pgh.pa.us 455 [ + + ]:CBC 108 : if (p == q) /* no digits? */
456 [ + - ]: 3 : ereturn(escontext, false,
457 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
458 : : errmsg("malformed array literal: \"%s\"", origStr),
459 : : errdetail("Missing array dimension value.")));
460 : : }
461 : : else
462 : : {
463 : : /* [n] format */
464 : 6 : lBound[ndim] = 1;
465 : 6 : ub = i;
466 : : }
467 [ - + ]: 111 : if (*p != ']')
853 tgl@sss.pgh.pa.us 468 [ # # ]:UBC 0 : ereturn(escontext, false,
469 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
470 : : errmsg("malformed array literal: \"%s\"", origStr),
471 : : errdetail("Missing \"%s\" after array dimensions.",
472 : : "]")));
853 tgl@sss.pgh.pa.us 473 :CBC 111 : p++;
474 : :
475 : : /*
476 : : * Note: we could accept ub = lb-1 to represent a zero-length
477 : : * dimension. However, that would result in an empty array, for which
478 : : * we don't keep any dimension data, so that e.g. [1:0] and [101:100]
479 : : * would be equivalent. Given the lack of field demand, there seems
480 : : * little point in allowing such cases.
481 : : */
482 [ + + ]: 111 : if (ub < lBound[ndim])
483 [ + - ]: 6 : ereturn(escontext, false,
484 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
485 : : errmsg("upper bound cannot be less than lower bound")));
486 : :
487 : : /* Upper bound of INT_MAX must be disallowed, cf ArrayCheckBounds() */
488 [ + + ]: 105 : if (ub == INT_MAX)
489 [ + - ]: 3 : ereturn(escontext, false,
490 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
491 : : errmsg("array upper bound is too large: %d", ub)));
492 : :
493 : : /* Compute "ub - lBound[ndim] + 1", detecting overflow */
494 [ + - - + ]: 204 : if (pg_sub_s32_overflow(ub, lBound[ndim], &ub) ||
495 : 102 : pg_add_s32_overflow(ub, 1, &ub))
853 tgl@sss.pgh.pa.us 496 [ # # ]:UBC 0 : ereturn(escontext, false,
497 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
498 : : errmsg("array size exceeds the maximum allowed (%zu)",
499 : : MaxArraySize)));
500 : :
853 tgl@sss.pgh.pa.us 501 :CBC 102 : dim[ndim] = ub;
502 : 102 : ndim++;
503 : : }
504 : :
505 : 131016 : *srcptr = p;
506 : 131016 : *ndim_p = ndim;
507 : 131016 : return true;
508 : : }
509 : :
510 : : /*
511 : : * ReadDimensionInt
512 : : * parse an integer, for the array dimensions
513 : : *
514 : : * On entry, *srcptr points to the string to parse. It is advanced past the
515 : : * digits of the integer. If there are no digits, returns true and leaves
516 : : * *srcptr unchanged.
517 : : *
518 : : * Result:
519 : : * true for success, false for failure (if escontext is provided).
520 : : * On success, the parsed integer is returned in *result.
521 : : */
522 : : static bool
523 : 231 : ReadDimensionInt(char **srcptr, int *result,
524 : : const char *origStr, Node *escontext)
525 : : {
526 : 231 : char *p = *srcptr;
527 : : long l;
528 : :
529 : : /* don't accept leading whitespace */
530 [ + + + + : 231 : if (!isdigit((unsigned char) *p) && *p != '-' && *p != '+')
+ - ]
531 : : {
532 : 6 : *result = 0;
533 : 6 : return true;
534 : : }
535 : :
536 : 225 : errno = 0;
537 : 225 : l = strtol(p, srcptr, 10);
538 : :
539 [ + - + + : 225 : if (errno == ERANGE || l > PG_INT32_MAX || l < PG_INT32_MIN)
+ + ]
540 [ + - ]: 6 : ereturn(escontext, false,
541 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
542 : : errmsg("array bound is out of integer range")));
543 : :
544 : 219 : *result = (int) l;
545 : 219 : return true;
546 : : }
547 : :
548 : : /*
549 : : * ReadArrayStr :
550 : : * parses the array string pointed to by *srcptr and converts the values
551 : : * to internal format. Determines the array dimensions as it goes.
552 : : *
553 : : * On entry, *srcptr points to the string to parse (it must point to a '{').
554 : : * On successful return, it is advanced to point past the closing '}'.
555 : : *
556 : : * If dimensions were specified explicitly, they are passed in *ndim_p and
557 : : * dim[]. This function will check that the array values match the specified
558 : : * dimensions. If dimensions were not given, caller must pass *ndim_p == 0
559 : : * and initialize all elements of dim[] to -1. Then this function will
560 : : * deduce the dimensions from the structure of the input and store them in
561 : : * *ndim_p and the dim[] array.
562 : : *
563 : : * Element type information:
564 : : * inputproc: type-specific input procedure for element datatype.
565 : : * typioparam, typmod: auxiliary values to pass to inputproc.
566 : : * typdelim: the value delimiter (type-specific).
567 : : * typlen, typbyval, typalign: storage parameters of element datatype.
568 : : *
569 : : * Outputs:
570 : : * *ndim_p, dim: dimensions deduced from the input structure.
571 : : * *nitems_p: total number of elements.
572 : : * *values_p[]: palloc'd array, filled with converted data values.
573 : : * *nulls_p[]: palloc'd array, filled with is-null markers.
574 : : *
575 : : * 'origStr' is the original input string, used only in error messages.
576 : : * If *escontext points to an ErrorSaveContext, details of any error are
577 : : * reported there.
578 : : *
579 : : * Result:
580 : : * true for success, false for failure (if escontext is provided).
581 : : */
582 : : static bool
583 : 131009 : ReadArrayStr(char **srcptr,
584 : : FmgrInfo *inputproc,
585 : : Oid typioparam,
586 : : int32 typmod,
587 : : char typdelim,
588 : : int typlen,
589 : : bool typbyval,
590 : : char typalign,
591 : : int *ndim_p,
592 : : int *dim,
593 : : int *nitems_p,
594 : : Datum **values_p,
595 : : bool **nulls_p,
596 : : const char *origStr,
597 : : Node *escontext)
598 : : {
599 : 131009 : int ndim = *ndim_p;
600 : 131009 : bool dimensions_specified = (ndim != 0);
601 : : int maxitems;
602 : : Datum *values;
603 : : bool *nulls;
604 : : StringInfoData elembuf;
605 : : int nest_level;
606 : : int nitems;
607 : : bool ndim_frozen;
608 : : bool expect_delim;
609 : : int nelems[MAXDIM];
610 : :
611 : : /* Allocate some starting output workspace; we'll enlarge as needed */
612 : 131009 : maxitems = 16;
613 : 131009 : values = palloc_array(Datum, maxitems);
614 : 131009 : nulls = palloc_array(bool, maxitems);
615 : :
616 : : /* Allocate workspace to hold (string representation of) one element */
617 : 131009 : initStringInfo(&elembuf);
618 : :
619 : : /* Loop below assumes first token is ATOK_LEVEL_START */
620 [ - + ]: 131009 : Assert(**srcptr == '{');
621 : :
622 : : /* Parse tokens until we reach the matching right brace */
623 : 131009 : nest_level = 0;
624 : 131009 : nitems = 0;
625 : 131009 : ndim_frozen = dimensions_specified;
626 : 131009 : expect_delim = false;
627 : : do
628 : : {
629 : : ArrayToken tok;
630 : :
631 : 1393358 : tok = ReadArrayToken(srcptr, &elembuf, typdelim, origStr, escontext);
632 : :
633 [ + + + + : 1393343 : switch (tok)
+ - ]
634 : : {
635 : 131916 : case ATOK_LEVEL_START:
636 : : /* Can't write left brace where delim is expected */
637 [ + + ]: 131916 : if (expect_delim)
1192 638 [ + - ]: 3 : ereturn(escontext, false,
639 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
640 : : errmsg("malformed array literal: \"%s\"", origStr),
641 : : errdetail("Unexpected \"%c\" character.", '{')));
642 : :
643 : : /* Initialize element counting in the new level */
853 644 [ + + ]: 131913 : if (nest_level >= MAXDIM)
645 [ + - ]: 1 : ereturn(escontext, false,
646 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
647 : : errmsg("number of array dimensions exceeds the maximum allowed (%d)",
648 : : MAXDIM)));
649 : :
650 : 131912 : nelems[nest_level] = 0;
651 : 131912 : nest_level++;
652 [ + + ]: 131912 : if (nest_level > ndim)
653 : : {
654 : : /* Can't increase ndim once it's frozen */
655 [ + + ]: 131233 : if (ndim_frozen)
656 : 6 : goto dimension_error;
657 : 131227 : ndim = nest_level;
658 : : }
659 : 131906 : break;
660 : :
661 : 131780 : case ATOK_LEVEL_END:
662 : : /* Can't get here with nest_level == 0 */
663 [ - + ]: 131780 : Assert(nest_level > 0);
664 : :
665 : : /*
666 : : * We allow a right brace to terminate an empty sub-array,
667 : : * otherwise it must occur where we expect a delimiter.
668 : : */
669 [ + + + + ]: 131780 : if (nelems[nest_level - 1] > 0 && !expect_delim)
670 [ + - ]: 12 : ereturn(escontext, false,
671 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
672 : : errmsg("malformed array literal: \"%s\"", origStr),
673 : : errdetail("Unexpected \"%c\" character.",
674 : : '}')));
675 : 131768 : nest_level--;
676 : : /* Nested sub-arrays count as elements of outer level */
677 [ + + ]: 131768 : if (nest_level > 0)
678 : 870 : nelems[nest_level - 1]++;
679 : :
680 : : /*
681 : : * Note: if we had dimensionality info, then dim[nest_level]
682 : : * is initially non-negative, and we'll check each sub-array's
683 : : * length against that.
684 : : */
685 [ + + ]: 131768 : if (dim[nest_level] < 0)
686 : : {
687 : : /* Save length of first sub-array of this level */
688 : 131095 : dim[nest_level] = nelems[nest_level];
689 : : }
690 [ + + ]: 673 : else if (nelems[nest_level] != dim[nest_level])
691 : : {
692 : : /* Subsequent sub-arrays must have same length */
693 : 16 : goto dimension_error;
694 : : }
695 : :
696 : : /*
697 : : * Must have a delim or another right brace following, unless
698 : : * we have reached nest_level 0, where this won't matter.
699 : : */
700 : 131752 : expect_delim = true;
701 : 131752 : break;
702 : :
703 : 500601 : case ATOK_DELIM:
704 [ + + ]: 500601 : if (!expect_delim)
705 [ + - ]: 3 : ereturn(escontext, false,
706 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
707 : : errmsg("malformed array literal: \"%s\"", origStr),
708 : : errdetail("Unexpected \"%c\" character.",
709 : : typdelim)));
710 : 500598 : expect_delim = false;
711 : 500598 : break;
712 : :
713 : 629043 : case ATOK_ELEM:
714 : : case ATOK_ELEM_NULL:
715 : : /* Can't get here with nest_level == 0 */
716 [ - + ]: 629043 : Assert(nest_level > 0);
717 : :
718 : : /* Disallow consecutive ELEM tokens */
719 [ + + ]: 629043 : if (expect_delim)
720 [ + - ]: 3 : ereturn(escontext, false,
721 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
722 : : errmsg("malformed array literal: \"%s\"", origStr),
723 : : errdetail("Unexpected array element.")));
724 : :
725 : : /* Enlarge the values/nulls arrays if needed */
726 [ + + ]: 629040 : if (nitems >= maxitems)
727 : : {
728 [ - + ]: 3208 : if (maxitems >= MaxArraySize)
1192 tgl@sss.pgh.pa.us 729 [ # # ]:UBC 0 : ereturn(escontext, false,
730 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
731 : : errmsg("array size exceeds the maximum allowed (%zu)",
732 : : MaxArraySize)));
853 tgl@sss.pgh.pa.us 733 [ + - ]:CBC 3208 : maxitems = Min(maxitems * 2, MaxArraySize);
734 : 3208 : values = repalloc_array(values, Datum, maxitems);
735 : 3208 : nulls = repalloc_array(nulls, bool, maxitems);
736 : : }
737 : :
738 : : /* Read the element's value, or check that NULL is allowed */
739 [ + + ]: 629040 : if (!InputFunctionCallSafe(inputproc,
740 : : (tok == ATOK_ELEM_NULL) ? NULL : elembuf.data,
741 : : typioparam, typmod,
742 : : escontext,
743 [ + + ]: 629040 : &values[nitems]))
744 : 36 : return false;
745 : 628994 : nulls[nitems] = (tok == ATOK_ELEM_NULL);
746 : 628994 : nitems++;
747 : :
748 : : /*
749 : : * Once we have found an element, the number of dimensions can
750 : : * no longer increase, and subsequent elements must all be at
751 : : * the same nesting depth.
752 : : */
753 : 628994 : ndim_frozen = true;
754 [ + + ]: 628994 : if (nest_level != ndim)
755 : 6 : goto dimension_error;
756 : : /* Count the new element */
757 : 628988 : nelems[nest_level - 1]++;
758 : :
759 : : /* Must have a delim or a right brace following */
760 : 628988 : expect_delim = true;
761 : 628988 : break;
762 : :
763 : 3 : case ATOK_ERROR:
764 : 3 : return false;
765 : : }
766 [ + + ]: 1393244 : } while (nest_level > 0);
767 : :
768 : : /* Clean up and return results */
769 : 130895 : pfree(elembuf.data);
770 : :
771 : 130895 : *ndim_p = ndim;
772 : 130895 : *nitems_p = nitems;
773 : 130895 : *values_p = values;
774 : 130895 : *nulls_p = nulls;
775 : 130895 : return true;
776 : :
777 : 28 : dimension_error:
778 [ + + ]: 28 : if (dimensions_specified)
779 [ + - ]: 3 : ereturn(escontext, false,
780 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
781 : : errmsg("malformed array literal: \"%s\"", origStr),
782 : : errdetail("Specified array dimensions do not match array contents.")));
783 : : else
784 [ + - ]: 25 : ereturn(escontext, false,
785 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
786 : : errmsg("malformed array literal: \"%s\"", origStr),
787 : : errdetail("Multidimensional arrays must have sub-arrays with matching dimensions.")));
788 : : }
789 : :
790 : : /*
791 : : * ReadArrayToken
792 : : * read one token from an array value string
793 : : *
794 : : * Starts scanning from *srcptr. On non-error return, *srcptr is
795 : : * advanced past the token.
796 : : *
797 : : * If the token is ATOK_ELEM, the de-escaped string is returned in elembuf.
798 : : */
799 : : static ArrayToken
800 : 1393358 : ReadArrayToken(char **srcptr, StringInfo elembuf, char typdelim,
801 : : const char *origStr, Node *escontext)
802 : : {
803 : 1393358 : char *p = *srcptr;
804 : : int dstlen;
805 : : bool has_escapes;
806 : :
807 : 1393358 : resetStringInfo(elembuf);
808 : :
809 : : /* Identify token type. Loop advances over leading whitespace. */
810 : : for (;;)
811 : : {
812 [ - + + + : 1407467 : switch (*p)
+ ]
813 : : {
853 tgl@sss.pgh.pa.us 814 :UBC 0 : case '\0':
815 : 0 : goto ending_error;
853 tgl@sss.pgh.pa.us 816 :CBC 131916 : case '{':
817 : 131916 : *srcptr = p + 1;
818 : 131916 : return ATOK_LEVEL_START;
819 : 131780 : case '}':
820 : 131780 : *srcptr = p + 1;
821 : 131780 : return ATOK_LEVEL_END;
822 : 283340 : case '"':
823 : 283340 : p++;
824 : 283340 : goto quoted_element;
825 : 860431 : default:
826 [ + + ]: 860431 : if (*p == typdelim)
827 : : {
828 : 500601 : *srcptr = p + 1;
829 : 500601 : return ATOK_DELIM;
830 : : }
831 [ + + ]: 359830 : if (scanner_isspace(*p))
832 : : {
833 : 14109 : p++;
834 : 14109 : continue;
835 : : }
836 : 345721 : goto unquoted_element;
837 : : }
838 : : }
839 : :
840 : 283340 : quoted_element:
841 : : for (;;)
842 : : {
843 [ - + + + ]: 2159487 : switch (*p)
844 : : {
853 tgl@sss.pgh.pa.us 845 :UBC 0 : case '\0':
846 : 0 : goto ending_error;
853 tgl@sss.pgh.pa.us 847 :CBC 637 : case '\\':
848 : : /* Skip backslash, copy next character as-is. */
849 : 637 : p++;
850 [ - + ]: 637 : if (*p == '\0')
853 tgl@sss.pgh.pa.us 851 :UBC 0 : goto ending_error;
853 tgl@sss.pgh.pa.us 852 :CBC 637 : appendStringInfoChar(elembuf, *p++);
853 : 637 : break;
854 : 283340 : case '"':
855 : :
856 : : /*
857 : : * If next non-whitespace isn't typdelim or a brace, complain
858 : : * about incorrect quoting. While we could leave such cases
859 : : * to be detected as incorrect token sequences, the resulting
860 : : * message wouldn't be as helpful. (We could also give the
861 : : * incorrect-quoting error when next is '{', but treating that
862 : : * as a token sequence error seems better.)
863 : : */
864 [ + - ]: 283358 : while (*(++p) != '\0')
865 : : {
866 [ + + + + : 283358 : if (*p == typdelim || *p == '}' || *p == '{')
+ + ]
867 : : {
868 : 283331 : *srcptr = p;
869 : 283331 : return ATOK_ELEM;
870 : : }
871 [ + + ]: 27 : if (!scanner_isspace(*p))
872 [ + - ]: 9 : ereturn(escontext, ATOK_ERROR,
873 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
874 : : errmsg("malformed array literal: \"%s\"", origStr),
875 : : errdetail("Incorrectly quoted array element.")));
876 : : }
853 tgl@sss.pgh.pa.us 877 :UBC 0 : goto ending_error;
853 tgl@sss.pgh.pa.us 878 :CBC 1875510 : default:
879 : 1875510 : appendStringInfoChar(elembuf, *p++);
880 : 1875510 : break;
881 : : }
882 : : }
883 : :
884 : 345721 : unquoted_element:
885 : :
886 : : /*
887 : : * We don't include trailing whitespace in the result. dstlen tracks how
888 : : * much of the output string is known to not be trailing whitespace.
889 : : */
890 : 345721 : dstlen = 0;
891 : 345721 : has_escapes = false;
892 : : for (;;)
893 : : {
894 [ + + + + : 2022628 : switch (*p)
+ ]
895 : : {
896 : 3 : case '\0':
897 : 3 : goto ending_error;
898 : 3 : case '{':
899 [ + - ]: 3 : ereturn(escontext, ATOK_ERROR,
900 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
901 : : errmsg("malformed array literal: \"%s\"", origStr),
902 : : errdetail("Unexpected \"%c\" character.",
903 : : '{')));
904 : 3 : case '"':
905 : : /* Must double-quote all or none of an element. */
906 [ + - ]: 3 : ereturn(escontext, ATOK_ERROR,
907 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
908 : : errmsg("malformed array literal: \"%s\"", origStr),
909 : : errdetail("Incorrectly quoted array element.")));
910 : 9 : case '\\':
911 : : /* Skip backslash, copy next character as-is. */
912 : 9 : p++;
913 [ - + ]: 9 : if (*p == '\0')
853 tgl@sss.pgh.pa.us 914 :UBC 0 : goto ending_error;
853 tgl@sss.pgh.pa.us 915 :CBC 9 : appendStringInfoChar(elembuf, *p++);
916 : 9 : dstlen = elembuf->len; /* treat it as non-whitespace */
917 : 9 : has_escapes = true;
918 : 9 : break;
919 : 2022610 : default:
920 : : /* End of elem? */
921 [ + + + + ]: 2022610 : if (*p == typdelim || *p == '}')
922 : : {
923 : : /* hack: truncate the output string to dstlen */
924 : 345712 : elembuf->data[dstlen] = '\0';
925 : 345712 : elembuf->len = dstlen;
926 : 345712 : *srcptr = p;
927 : : /* Check if it's unquoted "NULL" */
928 [ + - + + : 691415 : if (Array_nulls && !has_escapes &&
+ + ]
929 : 345703 : pg_strcasecmp(elembuf->data, "NULL") == 0)
930 : 412 : return ATOK_ELEM_NULL;
931 : : else
932 : 345300 : return ATOK_ELEM;
933 : : }
934 : 1676898 : appendStringInfoChar(elembuf, *p);
935 [ + + ]: 1676898 : if (!scanner_isspace(*p))
936 : 1676492 : dstlen = elembuf->len;
937 : 1676898 : p++;
938 : 1676898 : break;
939 : : }
940 : : }
941 : :
942 : 3 : ending_error:
943 [ - + ]: 3 : ereturn(escontext, ATOK_ERROR,
944 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
945 : : errmsg("malformed array literal: \"%s\"", origStr),
946 : : errdetail("Unexpected end of input.")));
947 : : }
948 : :
949 : : /*
950 : : * Copy data into an array object from a temporary array of Datums.
951 : : *
952 : : * array: array object (with header fields already filled in)
953 : : * values: array of Datums to be copied
954 : : * nulls: array of is-null flags (can be NULL if no nulls)
955 : : * nitems: number of Datums to be copied
956 : : * typbyval, typlen, typalign: info about element datatype
957 : : * freedata: if true and element type is pass-by-ref, pfree data values
958 : : * referenced by Datums after copying them.
959 : : *
960 : : * If the input data is of varlena type, the caller must have ensured that
961 : : * the values are not toasted. (Doing it here doesn't work since the
962 : : * caller has already allocated space for the array...)
963 : : */
964 : : void
7423 965 : 921624 : CopyArrayEls(ArrayType *array,
966 : : const Datum *values,
967 : : const bool *nulls,
968 : : int nitems,
969 : : int typlen,
970 : : bool typbyval,
971 : : char typalign,
972 : : bool freedata)
973 : : {
974 [ + + ]: 921624 : char *p = ARR_DATA_PTR(array);
975 [ + + ]: 921624 : bits8 *bitmap = ARR_NULLBITMAP(array);
976 : 921624 : int bitval = 0;
977 : 921624 : int bitmask = 1;
41 tgl@sss.pgh.pa.us 978 :GNC 921624 : uint8 typalignby = typalign_to_alignby(typalign);
979 : : int i;
980 : :
9372 tgl@sss.pgh.pa.us 981 [ + + ]:CBC 921624 : if (typbyval)
982 : 617707 : freedata = false;
983 : :
10416 bruce@momjian.us 984 [ + + ]: 6725285 : for (i = 0; i < nitems; i++)
985 : : {
7423 tgl@sss.pgh.pa.us 986 [ + + + + ]: 5803661 : if (nulls && nulls[i])
987 : : {
7418 bruce@momjian.us 988 [ - + ]: 16942 : if (!bitmap) /* shouldn't happen */
7423 tgl@sss.pgh.pa.us 989 [ # # ]:UBC 0 : elog(ERROR, "null array element where not supported");
990 : : /* bitmap bit stays 0 */
991 : : }
992 : : else
993 : : {
7423 tgl@sss.pgh.pa.us 994 :CBC 5786719 : bitval |= bitmask;
41 tgl@sss.pgh.pa.us 995 :GNC 5786719 : p += ArrayCastAndSet(values[i], typlen, typbyval, typalignby, p);
7423 tgl@sss.pgh.pa.us 996 [ + + ]:CBC 5786719 : if (freedata)
997 : 375385 : pfree(DatumGetPointer(values[i]));
998 : : }
999 [ + + ]: 5803661 : if (bitmap)
1000 : : {
1001 : 393718 : bitmask <<= 1;
1002 [ + + ]: 393718 : if (bitmask == 0x100)
1003 : : {
1004 : 47598 : *bitmap++ = bitval;
1005 : 47598 : bitval = 0;
1006 : 47598 : bitmask = 1;
1007 : : }
1008 : : }
1009 : : }
1010 : :
1011 [ + + + + ]: 921624 : if (bitmap && bitmask != 1)
1012 : 9188 : *bitmap = bitval;
10841 scrappy@hub.org 1013 : 921624 : }
1014 : :
1015 : : /*
1016 : : * array_out :
1017 : : * takes the internal representation of an array and returns a string
1018 : : * containing the array in its external format.
1019 : : */
1020 : : Datum
9406 tgl@sss.pgh.pa.us 1021 : 498889 : array_out(PG_FUNCTION_ARGS)
1022 : : {
3100 1023 : 498889 : AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
3958 1024 [ + + ]: 498889 : Oid element_type = AARR_ELEMTYPE(v);
1025 : : int typlen;
1026 : : bool typbyval;
1027 : : char typalign;
1028 : : char typdelim;
1029 : : char *p,
1030 : : *tmp,
1031 : : *retval,
1032 : : **values,
1033 : : dims_str[(MAXDIM * 33) + 2];
1034 : :
1035 : : /*
1036 : : * 33 per dim since we assume 15 digits per number + ':' +'[]'
1037 : : *
1038 : : * +2 allows for assignment operator + trailing null
1039 : : */
1040 : : bool *needquotes,
7892 mail@joeconway.com 1041 : 498889 : needdims = false;
1042 : : size_t overall_length;
1043 : : int nitems,
1044 : : i,
1045 : : j,
1046 : : k,
1047 : : indx[MAXDIM];
1048 : : int ndim,
1049 : : *dims,
1050 : : *lb;
1051 : : array_iter iter;
1052 : : ArrayMetaState *my_extra;
1053 : :
1054 : : /*
1055 : : * We arrange to look up info about element type, including its output
1056 : : * conversion proc, only once per series of calls, assuming the element
1057 : : * type doesn't change underneath us.
1058 : : */
8297 tgl@sss.pgh.pa.us 1059 : 498889 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1060 [ + + ]: 498889 : if (my_extra == NULL)
1061 : : {
1062 : 14428 : fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1063 : : sizeof(ArrayMetaState));
1064 : 14428 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
7423 1065 : 14428 : my_extra->element_type = ~element_type;
1066 : : }
1067 : :
8297 1068 [ + + ]: 498889 : if (my_extra->element_type != element_type)
1069 : : {
1070 : : /*
1071 : : * Get info about element type, including its output conversion proc
1072 : : */
1073 : 16763 : get_type_io_data(element_type, IOFunc_output,
1074 : : &my_extra->typlen, &my_extra->typbyval,
1075 : : &my_extra->typalign, &my_extra->typdelim,
1076 : : &my_extra->typioparam, &my_extra->typiofunc);
1077 : 16763 : fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1078 : 16763 : fcinfo->flinfo->fn_mcxt);
1079 : 16763 : my_extra->element_type = element_type;
1080 : : }
1081 : 498889 : typlen = my_extra->typlen;
1082 : 498889 : typbyval = my_extra->typbyval;
1083 : 498889 : typalign = my_extra->typalign;
1084 : 498889 : typdelim = my_extra->typdelim;
1085 : :
3958 1086 [ + + ]: 498889 : ndim = AARR_NDIM(v);
1087 [ + + ]: 498889 : dims = AARR_DIMS(v);
1088 [ + + ]: 498889 : lb = AARR_LBOUND(v);
7850 neilc@samurai.com 1089 : 498889 : nitems = ArrayGetNItems(ndim, dims);
1090 : :
10416 bruce@momjian.us 1091 [ + + ]: 498889 : if (nitems == 0)
1092 : : {
8872 tgl@sss.pgh.pa.us 1093 : 1757 : retval = pstrdup("{}");
9406 1094 : 1757 : PG_RETURN_CSTRING(retval);
1095 : : }
1096 : :
1097 : : /*
1098 : : * we will need to add explicit dimensions if any dimension has a lower
1099 : : * bound other than one
1100 : : */
7892 mail@joeconway.com 1101 [ + + ]: 994638 : for (i = 0; i < ndim; i++)
1102 : : {
1103 [ + + ]: 497660 : if (lb[i] != 1)
1104 : : {
1105 : 154 : needdims = true;
1106 : 154 : break;
1107 : : }
1108 : : }
1109 : :
1110 : : /*
1111 : : * Convert all values to string form, count total space needed (including
1112 : : * any overhead such as escaping backslashes), and detect whether each
1113 : : * item needs double quotes.
1114 : : */
8872 tgl@sss.pgh.pa.us 1115 : 497132 : values = (char **) palloc(nitems * sizeof(char *));
1116 : 497132 : needquotes = (bool *) palloc(nitems * sizeof(bool));
2729 1117 : 497132 : overall_length = 0;
1118 : :
41 tgl@sss.pgh.pa.us 1119 :GNC 497132 : array_iter_setup(&iter, v, typlen, typbyval, typalign);
1120 : :
10416 bruce@momjian.us 1121 [ + + ]:CBC 1759295 : for (i = 0; i < nitems; i++)
1122 : : {
1123 : : Datum itemvalue;
1124 : : bool isnull;
1125 : : bool needquote;
1126 : :
1127 : : /* Get source element, checking for NULL */
41 tgl@sss.pgh.pa.us 1128 :GNC 1262163 : itemvalue = array_iter_next(&iter, &isnull, i);
1129 : :
3958 tgl@sss.pgh.pa.us 1130 [ + + ]:CBC 1262163 : if (isnull)
1131 : : {
7423 1132 : 1196 : values[i] = pstrdup("NULL");
1133 : 1196 : overall_length += 4;
7850 neilc@samurai.com 1134 : 1196 : needquote = false;
1135 : : }
1136 : : else
1137 : : {
7285 tgl@sss.pgh.pa.us 1138 : 1260967 : values[i] = OutputFunctionCall(&my_extra->proc, itemvalue);
1139 : :
1140 : : /* count data plus backslashes; detect chars needing quotes */
7423 1141 [ + + ]: 1260967 : if (values[i][0] == '\0')
3189 1142 : 270 : needquote = true; /* force quotes for empty string */
7423 1143 [ + + ]: 1260697 : else if (pg_strcasecmp(values[i], "NULL") == 0)
3189 1144 : 10 : needquote = true; /* force quotes for literal NULL */
1145 : : else
7423 1146 : 1260687 : needquote = false;
1147 : :
1148 [ + + ]: 17065593 : for (tmp = values[i]; *tmp != '\0'; tmp++)
1149 : : {
1150 : 15804626 : char ch = *tmp;
1151 : :
10057 bruce@momjian.us 1152 : 15804626 : overall_length += 1;
7423 tgl@sss.pgh.pa.us 1153 [ + + + + ]: 15804626 : if (ch == '"' || ch == '\\')
1154 : : {
1155 : 2166 : needquote = true;
1156 : 2166 : overall_length += 1;
1157 : : }
1158 [ + + + + : 31579113 : else if (ch == '{' || ch == '}' || ch == typdelim ||
+ + + + ]
983 michael@paquier.xyz 1159 : 15776653 : scanner_isspace(ch))
7423 tgl@sss.pgh.pa.us 1160 : 104420 : needquote = true;
1161 : : }
1162 : : }
1163 : :
7850 neilc@samurai.com 1164 : 1262163 : needquotes[i] = needquote;
1165 : :
1166 : : /* Count the pair of double quotes, if needed */
1167 [ + + ]: 1262163 : if (needquote)
8872 tgl@sss.pgh.pa.us 1168 : 15100 : overall_length += 2;
1169 : : /* and the comma (or other typdelim delimiter) */
10222 bruce@momjian.us 1170 : 1262163 : overall_length += 1;
1171 : : }
1172 : :
1173 : : /*
1174 : : * The very last array element doesn't have a typdelim delimiter after it,
1175 : : * but that's OK; that space is needed for the trailing '\0'.
1176 : : *
1177 : : * Now count total number of curly brace pairs in output string.
1178 : : */
7850 neilc@samurai.com 1179 [ + + ]: 994825 : for (i = j = 0, k = 1; i < ndim; i++)
1180 : : {
2729 tgl@sss.pgh.pa.us 1181 : 497693 : j += k, k *= dims[i];
1182 : : }
1183 : 497132 : overall_length += 2 * j;
1184 : :
1185 : : /* Format explicit dimensions if required */
7850 neilc@samurai.com 1186 : 497132 : dims_str[0] = '\0';
7892 mail@joeconway.com 1187 [ + + ]: 497132 : if (needdims)
1188 : : {
7868 bruce@momjian.us 1189 : 154 : char *ptr = dims_str;
1190 : :
7892 mail@joeconway.com 1191 [ + + ]: 342 : for (i = 0; i < ndim; i++)
1192 : : {
7850 neilc@samurai.com 1193 : 188 : sprintf(ptr, "[%d:%d]", lb[i], lb[i] + dims[i] - 1);
7892 mail@joeconway.com 1194 : 188 : ptr += strlen(ptr);
1195 : : }
1196 : 154 : *ptr++ = *ASSGN;
1197 : 154 : *ptr = '\0';
2729 tgl@sss.pgh.pa.us 1198 : 154 : overall_length += ptr - dims_str;
1199 : : }
1200 : :
1201 : : /* Now construct the output string */
1202 : 497132 : retval = (char *) palloc(overall_length);
8872 1203 : 497132 : p = retval;
1204 : :
1205 : : #define APPENDSTR(str) (strcpy(p, (str)), p += strlen(p))
1206 : : #define APPENDCHAR(ch) (*p++ = (ch), *p = '\0')
1207 : :
7892 mail@joeconway.com 1208 [ + + ]: 497132 : if (needdims)
1209 : 154 : APPENDSTR(dims_str);
8872 tgl@sss.pgh.pa.us 1210 : 497132 : APPENDCHAR('{');
7850 neilc@samurai.com 1211 [ + + ]: 994825 : for (i = 0; i < ndim; i++)
1212 : 497693 : indx[i] = 0;
10416 bruce@momjian.us 1213 : 497132 : j = 0;
1214 : 497132 : k = 0;
1215 : : do
1216 : : {
1217 [ + + ]: 1266755 : for (i = j; i < ndim - 1; i++)
8872 tgl@sss.pgh.pa.us 1218 : 4592 : APPENDCHAR('{');
1219 : :
1220 [ + + ]: 1262163 : if (needquotes[k])
1221 : : {
1222 : 15100 : APPENDCHAR('"');
10057 bruce@momjian.us 1223 [ + + ]: 531635 : for (tmp = values[k]; *tmp; tmp++)
1224 : : {
8593 1225 : 516535 : char ch = *tmp;
1226 : :
8872 tgl@sss.pgh.pa.us 1227 [ + + + + ]: 516535 : if (ch == '"' || ch == '\\')
1228 : 2166 : *p++ = '\\';
1229 : 516535 : *p++ = ch;
1230 : : }
1231 : 15100 : *p = '\0';
1232 : 15100 : APPENDCHAR('"');
1233 : : }
1234 : : else
1235 : 1247063 : APPENDSTR(values[k]);
10416 bruce@momjian.us 1236 : 1262163 : pfree(values[k++]);
1237 : :
1238 [ + + ]: 1763887 : for (i = ndim - 1; i >= 0; i--)
1239 : : {
2729 tgl@sss.pgh.pa.us 1240 [ + + ]: 1266755 : if (++(indx[i]) < dims[i])
1241 : : {
8872 1242 : 765031 : APPENDCHAR(typdelim);
10416 bruce@momjian.us 1243 : 765031 : break;
1244 : : }
1245 : : else
1246 : : {
2729 tgl@sss.pgh.pa.us 1247 : 501724 : indx[i] = 0;
8872 1248 : 501724 : APPENDCHAR('}');
1249 : : }
1250 : : }
10416 bruce@momjian.us 1251 : 1262163 : j = i;
1252 [ + + ]: 1262163 : } while (j != -1);
1253 : :
1254 : : #undef APPENDSTR
1255 : : #undef APPENDCHAR
1256 : :
1257 : : /* Assert that we calculated the string length accurately */
2729 tgl@sss.pgh.pa.us 1258 [ - + ]: 497132 : Assert(overall_length == (p - retval + 1));
1259 : :
10416 bruce@momjian.us 1260 : 497132 : pfree(values);
8872 tgl@sss.pgh.pa.us 1261 : 497132 : pfree(needquotes);
1262 : :
9406 1263 : 497132 : PG_RETURN_CSTRING(retval);
1264 : : }
1265 : :
1266 : : /*
1267 : : * array_recv :
1268 : : * converts an array from the external binary format to
1269 : : * its internal format.
1270 : : *
1271 : : * return value :
1272 : : * the internal representation of the input array
1273 : : */
1274 : : Datum
8347 1275 : 31 : array_recv(PG_FUNCTION_ARGS)
1276 : : {
8346 1277 : 31 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
1278 : 31 : Oid spec_element_type = PG_GETARG_OID(1); /* type of an array
1279 : : * element */
7456 bruce@momjian.us 1280 : 31 : int32 typmod = PG_GETARG_INT32(2); /* typmod for array elements */
1281 : : Oid element_type;
1282 : : int typlen;
1283 : : bool typbyval;
1284 : : char typalign;
1285 : : Oid typioparam;
1286 : : int i,
1287 : : nitems;
1288 : : Datum *dataPtr;
1289 : : bool *nullsPtr;
1290 : : bool hasnulls;
1291 : : int32 nbytes;
1292 : : int32 dataoffset;
1293 : : ArrayType *retval;
1294 : : int ndim,
1295 : : flags,
1296 : : dim[MAXDIM],
1297 : : lBound[MAXDIM];
1298 : : ArrayMetaState *my_extra;
1299 : :
1300 : : /* Get the array header information */
8346 tgl@sss.pgh.pa.us 1301 : 31 : ndim = pq_getmsgint(buf, 4);
8267 1302 [ - + ]: 31 : if (ndim < 0) /* we do allow zero-dimension arrays */
8267 tgl@sss.pgh.pa.us 1303 [ # # ]:UBC 0 : ereport(ERROR,
1304 : : (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1305 : : errmsg("invalid number of dimensions: %d", ndim)));
8267 tgl@sss.pgh.pa.us 1306 [ - + ]:CBC 31 : if (ndim > MAXDIM)
8267 tgl@sss.pgh.pa.us 1307 [ # # ]:UBC 0 : ereport(ERROR,
1308 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1309 : : errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
1310 : : ndim, MAXDIM)));
1311 : :
8346 tgl@sss.pgh.pa.us 1312 :CBC 31 : flags = pq_getmsgint(buf, 4);
7423 1313 [ - + - - ]: 31 : if (flags != 0 && flags != 1)
8267 tgl@sss.pgh.pa.us 1314 [ # # ]:UBC 0 : ereport(ERROR,
1315 : : (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1316 : : errmsg("invalid array flags")));
1317 : :
1318 : : /* Check element type recorded in the data */
8346 tgl@sss.pgh.pa.us 1319 :CBC 31 : element_type = pq_getmsgint(buf, sizeof(Oid));
1320 : :
1321 : : /*
1322 : : * From a security standpoint, it doesn't matter whether the input's
1323 : : * element type matches what we expect: the element type's receive
1324 : : * function has to be robust enough to cope with invalid data. However,
1325 : : * from a user-friendliness standpoint, it's nicer to complain about type
1326 : : * mismatches than to throw "improper binary format" errors. But there's
1327 : : * a problem: only built-in types have OIDs that are stable enough to
1328 : : * believe that a mismatch is a real issue. So complain only if both OIDs
1329 : : * are in the built-in range. Otherwise, carry on with the element type
1330 : : * we "should" be getting.
1331 : : */
1332 [ - + ]: 31 : if (element_type != spec_element_type)
1333 : : {
2063 tgl@sss.pgh.pa.us 1334 [ # # # # ]:UBC 0 : if (element_type < FirstGenbkiObjectId &&
1335 : : spec_element_type < FirstGenbkiObjectId)
1336 [ # # ]: 0 : ereport(ERROR,
1337 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
1338 : : errmsg("binary data has array element type %u (%s) instead of expected %u (%s)",
1339 : : element_type,
1340 : : format_type_extended(element_type, -1,
1341 : : FORMAT_TYPE_ALLOW_INVALID),
1342 : : spec_element_type,
1343 : : format_type_extended(spec_element_type, -1,
1344 : : FORMAT_TYPE_ALLOW_INVALID))));
1345 : 0 : element_type = spec_element_type;
1346 : : }
1347 : :
8346 tgl@sss.pgh.pa.us 1348 [ + + ]:CBC 62 : for (i = 0; i < ndim; i++)
1349 : : {
1350 : 31 : dim[i] = pq_getmsgint(buf, 4);
1351 : 31 : lBound[i] = pq_getmsgint(buf, 4);
1352 : : }
1353 : :
1354 : : /* This checks for overflow of array dimensions */
1355 : 31 : nitems = ArrayGetNItems(ndim, dim);
1770 1356 : 31 : ArrayCheckBounds(ndim, dim, lBound);
1357 : :
1358 : : /*
1359 : : * We arrange to look up info about element type, including its receive
1360 : : * conversion proc, only once per series of calls, assuming the element
1361 : : * type doesn't change underneath us.
1362 : : */
8297 1363 : 31 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1364 [ + + ]: 31 : if (my_extra == NULL)
1365 : : {
1366 : 28 : fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1367 : : sizeof(ArrayMetaState));
1368 : 28 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
7517 1369 : 28 : my_extra->element_type = ~element_type;
1370 : : }
1371 : :
8297 1372 [ + + ]: 31 : if (my_extra->element_type != element_type)
1373 : : {
1374 : : /* Get info about element type, including its receive proc */
1375 : 28 : get_type_io_data(element_type, IOFunc_receive,
1376 : : &my_extra->typlen, &my_extra->typbyval,
1377 : : &my_extra->typalign, &my_extra->typdelim,
1378 : : &my_extra->typioparam, &my_extra->typiofunc);
1379 [ - + ]: 28 : if (!OidIsValid(my_extra->typiofunc))
8267 tgl@sss.pgh.pa.us 1380 [ # # ]:UBC 0 : ereport(ERROR,
1381 : : (errcode(ERRCODE_UNDEFINED_FUNCTION),
1382 : : errmsg("no binary input function available for type %s",
1383 : : format_type_be(element_type))));
8297 tgl@sss.pgh.pa.us 1384 :CBC 28 : fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1385 : 28 : fcinfo->flinfo->fn_mcxt);
1386 : 28 : my_extra->element_type = element_type;
1387 : : }
1388 : :
7517 1389 [ - + ]: 31 : if (nitems == 0)
1390 : : {
1391 : : /* Return empty array ... but not till we've validated element_type */
7423 tgl@sss.pgh.pa.us 1392 :UBC 0 : PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
1393 : : }
1394 : :
8297 tgl@sss.pgh.pa.us 1395 :CBC 31 : typlen = my_extra->typlen;
1396 : 31 : typbyval = my_extra->typbyval;
1397 : 31 : typalign = my_extra->typalign;
7952 1398 : 31 : typioparam = my_extra->typioparam;
1399 : :
7423 1400 : 31 : dataPtr = (Datum *) palloc(nitems * sizeof(Datum));
1401 : 31 : nullsPtr = (bool *) palloc(nitems * sizeof(bool));
1402 : 31 : ReadArrayBinary(buf, nitems,
1403 : : &my_extra->proc, typioparam, typmod,
1404 : : typlen, typbyval, typalign,
1405 : : dataPtr, nullsPtr,
1406 : : &hasnulls, &nbytes);
1407 [ - + ]: 31 : if (hasnulls)
1408 : : {
7423 tgl@sss.pgh.pa.us 1409 :UBC 0 : dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
1410 : 0 : nbytes += dataoffset;
1411 : : }
1412 : : else
1413 : : {
7423 tgl@sss.pgh.pa.us 1414 :CBC 31 : dataoffset = 0; /* marker for no null bitmap */
1415 : 31 : nbytes += ARR_OVERHEAD_NONULLS(ndim);
1416 : : }
5436 1417 : 31 : retval = (ArrayType *) palloc0(nbytes);
6956 1418 : 31 : SET_VARSIZE(retval, nbytes);
8346 1419 : 31 : retval->ndim = ndim;
7423 1420 : 31 : retval->dataoffset = dataoffset;
8346 1421 : 31 : retval->elemtype = element_type;
8103 neilc@samurai.com 1422 : 31 : memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
1423 : 31 : memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
1424 : :
7423 tgl@sss.pgh.pa.us 1425 : 31 : CopyArrayEls(retval,
1426 : : dataPtr, nullsPtr, nitems,
1427 : : typlen, typbyval, typalign,
1428 : : true);
1429 : :
8346 1430 : 31 : pfree(dataPtr);
7423 1431 : 31 : pfree(nullsPtr);
1432 : :
8346 1433 : 31 : PG_RETURN_ARRAYTYPE_P(retval);
1434 : : }
1435 : :
1436 : : /*
1437 : : * ReadArrayBinary:
1438 : : * collect the data elements of an array being read in binary style.
1439 : : *
1440 : : * Inputs:
1441 : : * buf: the data buffer to read from.
1442 : : * nitems: total number of array elements (already read).
1443 : : * receiveproc: type-specific receive procedure for element datatype.
1444 : : * typioparam, typmod: auxiliary values to pass to receiveproc.
1445 : : * typlen, typbyval, typalign: storage parameters of element datatype.
1446 : : *
1447 : : * Outputs:
1448 : : * values[]: filled with converted data values.
1449 : : * nulls[]: filled with is-null markers.
1450 : : * *hasnulls: set true iff there are any null elements.
1451 : : * *nbytes: set to total size of data area needed (including alignment
1452 : : * padding but not including array header overhead).
1453 : : *
1454 : : * Note that values[] and nulls[] are allocated by the caller, and must have
1455 : : * nitems elements.
1456 : : */
1457 : : static void
1458 : 31 : ReadArrayBinary(StringInfo buf,
1459 : : int nitems,
1460 : : FmgrInfo *receiveproc,
1461 : : Oid typioparam,
1462 : : int32 typmod,
1463 : : int typlen,
1464 : : bool typbyval,
1465 : : char typalign,
1466 : : Datum *values,
1467 : : bool *nulls,
1468 : : bool *hasnulls,
1469 : : int32 *nbytes)
1470 : : {
1471 : : int i;
1472 : : bool hasnull;
1473 : : int32 totbytes;
41 tgl@sss.pgh.pa.us 1474 :GNC 31 : uint8 typalignby = typalign_to_alignby(typalign);
1475 : :
8346 tgl@sss.pgh.pa.us 1476 [ + + ]:CBC 124 : for (i = 0; i < nitems; i++)
1477 : : {
1478 : : int itemlen;
1479 : : StringInfoData elem_buf;
1480 : :
1481 : : /* Get and check the item length */
1482 : 93 : itemlen = pq_getmsgint(buf, 4);
7423 1483 [ + - - + ]: 93 : if (itemlen < -1 || itemlen > (buf->len - buf->cursor))
8267 tgl@sss.pgh.pa.us 1484 [ # # ]:UBC 0 : ereport(ERROR,
1485 : : (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1486 : : errmsg("insufficient data left in message")));
1487 : :
7423 tgl@sss.pgh.pa.us 1488 [ - + ]:CBC 93 : if (itemlen == -1)
1489 : : {
1490 : : /* -1 length means NULL */
7285 tgl@sss.pgh.pa.us 1491 :UBC 0 : values[i] = ReceiveFunctionCall(receiveproc, NULL,
1492 : : typioparam, typmod);
7423 1493 : 0 : nulls[i] = true;
1494 : 0 : continue;
1495 : : }
1496 : :
1497 : : /*
1498 : : * Rather than copying data around, we just initialize a StringInfo
1499 : : * pointing to the correct portion of the message buffer.
1500 : : */
871 drowley@postgresql.o 1501 :CBC 93 : initReadOnlyStringInfo(&elem_buf, &buf->data[buf->cursor], itemlen);
1502 : :
8346 tgl@sss.pgh.pa.us 1503 : 93 : buf->cursor += itemlen;
1504 : :
1505 : : /* Now call the element's receiveproc */
7285 1506 : 93 : values[i] = ReceiveFunctionCall(receiveproc, &elem_buf,
1507 : : typioparam, typmod);
7423 1508 : 93 : nulls[i] = false;
1509 : :
1510 : : /* Trouble if it didn't eat the whole buffer */
8346 1511 [ - + ]: 93 : if (elem_buf.cursor != itemlen)
8267 tgl@sss.pgh.pa.us 1512 [ # # ]:UBC 0 : ereport(ERROR,
1513 : : (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1514 : : errmsg("improper binary format in array element %d",
1515 : : i + 1)));
1516 : : }
1517 : :
1518 : : /*
1519 : : * Check for nulls, compute total data space needed
1520 : : */
7423 tgl@sss.pgh.pa.us 1521 :CBC 31 : hasnull = false;
1522 : 31 : totbytes = 0;
1523 [ + + ]: 124 : for (i = 0; i < nitems; i++)
1524 : : {
1525 [ - + ]: 93 : if (nulls[i])
7423 tgl@sss.pgh.pa.us 1526 :UBC 0 : hasnull = true;
1527 : : else
1528 : : {
1529 : : /* let's just make sure data is not toasted */
8346 tgl@sss.pgh.pa.us 1530 [ + + ]:CBC 93 : if (typlen == -1)
1531 : 54 : values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
6918 1532 [ + + + - : 93 : totbytes = att_addlength_datum(totbytes, typlen, values[i]);
- - - - -
- - - - +
- - ]
41 tgl@sss.pgh.pa.us 1533 :GNC 93 : totbytes = att_nominal_alignby(totbytes, typalignby);
1534 : : /* check for overflow of total request */
7423 tgl@sss.pgh.pa.us 1535 [ - + ]:CBC 93 : if (!AllocSizeIsValid(totbytes))
7423 tgl@sss.pgh.pa.us 1536 [ # # ]:UBC 0 : ereport(ERROR,
1537 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1538 : : errmsg("array size exceeds the maximum allowed (%zu)",
1539 : : MaxAllocSize)));
1540 : : }
1541 : : }
7423 tgl@sss.pgh.pa.us 1542 :CBC 31 : *hasnulls = hasnull;
1543 : 31 : *nbytes = totbytes;
8346 1544 : 31 : }
1545 : :
1546 : :
1547 : : /*
1548 : : * array_send :
1549 : : * takes the internal representation of an array and returns a bytea
1550 : : * containing the array in its external binary format.
1551 : : */
1552 : : Datum
8347 1553 : 23 : array_send(PG_FUNCTION_ARGS)
1554 : : {
3100 1555 : 23 : AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
3958 1556 [ - + ]: 23 : Oid element_type = AARR_ELEMTYPE(v);
1557 : : int typlen;
1558 : : bool typbyval;
1559 : : char typalign;
1560 : : int nitems,
1561 : : i;
1562 : : int ndim,
1563 : : *dim,
1564 : : *lb;
1565 : : StringInfoData buf;
1566 : : array_iter iter;
1567 : : ArrayMetaState *my_extra;
1568 : :
1569 : : /*
1570 : : * We arrange to look up info about element type, including its send
1571 : : * conversion proc, only once per series of calls, assuming the element
1572 : : * type doesn't change underneath us.
1573 : : */
8297 1574 : 23 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1575 [ + + ]: 23 : if (my_extra == NULL)
1576 : : {
1577 : 20 : fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1578 : : sizeof(ArrayMetaState));
1579 : 20 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
7423 1580 : 20 : my_extra->element_type = ~element_type;
1581 : : }
1582 : :
8297 1583 [ + + ]: 23 : if (my_extra->element_type != element_type)
1584 : : {
1585 : : /* Get info about element type, including its send proc */
1586 : 20 : get_type_io_data(element_type, IOFunc_send,
1587 : : &my_extra->typlen, &my_extra->typbyval,
1588 : : &my_extra->typalign, &my_extra->typdelim,
1589 : : &my_extra->typioparam, &my_extra->typiofunc);
1590 [ - + ]: 20 : if (!OidIsValid(my_extra->typiofunc))
8267 tgl@sss.pgh.pa.us 1591 [ # # ]:UBC 0 : ereport(ERROR,
1592 : : (errcode(ERRCODE_UNDEFINED_FUNCTION),
1593 : : errmsg("no binary output function available for type %s",
1594 : : format_type_be(element_type))));
8297 tgl@sss.pgh.pa.us 1595 :CBC 20 : fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1596 : 20 : fcinfo->flinfo->fn_mcxt);
1597 : 20 : my_extra->element_type = element_type;
1598 : : }
1599 : 23 : typlen = my_extra->typlen;
1600 : 23 : typbyval = my_extra->typbyval;
1601 : 23 : typalign = my_extra->typalign;
1602 : :
3958 1603 [ - + ]: 23 : ndim = AARR_NDIM(v);
1604 [ - + ]: 23 : dim = AARR_DIMS(v);
1605 [ - + ]: 23 : lb = AARR_LBOUND(v);
8346 1606 : 23 : nitems = ArrayGetNItems(ndim, dim);
1607 : :
1608 : 23 : pq_begintypsend(&buf);
1609 : :
1610 : : /* Send the array header information */
3077 andres@anarazel.de 1611 : 23 : pq_sendint32(&buf, ndim);
1612 [ - + - - ]: 23 : pq_sendint32(&buf, AARR_HASNULL(v) ? 1 : 0);
1613 : 23 : pq_sendint32(&buf, element_type);
8346 tgl@sss.pgh.pa.us 1614 [ + + ]: 46 : for (i = 0; i < ndim; i++)
1615 : : {
3077 andres@anarazel.de 1616 : 23 : pq_sendint32(&buf, dim[i]);
1617 : 23 : pq_sendint32(&buf, lb[i]);
1618 : : }
1619 : :
1620 : : /* Send the array elements using the element's own sendproc */
41 tgl@sss.pgh.pa.us 1621 :GNC 23 : array_iter_setup(&iter, v, typlen, typbyval, typalign);
1622 : :
8346 tgl@sss.pgh.pa.us 1623 [ + + ]:CBC 92 : for (i = 0; i < nitems; i++)
1624 : : {
1625 : : Datum itemvalue;
1626 : : bool isnull;
1627 : :
1628 : : /* Get source element, checking for NULL */
41 tgl@sss.pgh.pa.us 1629 :GNC 69 : itemvalue = array_iter_next(&iter, &isnull, i);
1630 : :
3958 tgl@sss.pgh.pa.us 1631 [ - + ]:CBC 69 : if (isnull)
1632 : : {
1633 : : /* -1 length means a NULL */
3077 andres@anarazel.de 1634 :UBC 0 : pq_sendint32(&buf, -1);
1635 : : }
1636 : : else
1637 : : {
1638 : : bytea *outputbytes;
1639 : :
7285 tgl@sss.pgh.pa.us 1640 :CBC 69 : outputbytes = SendFunctionCall(&my_extra->proc, itemvalue);
3077 andres@anarazel.de 1641 : 69 : pq_sendint32(&buf, VARSIZE(outputbytes) - VARHDRSZ);
7423 tgl@sss.pgh.pa.us 1642 : 69 : pq_sendbytes(&buf, VARDATA(outputbytes),
1643 : 69 : VARSIZE(outputbytes) - VARHDRSZ);
1644 : 69 : pfree(outputbytes);
1645 : : }
1646 : : }
1647 : :
8346 1648 : 23 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
1649 : : }
1650 : :
1651 : : /*
1652 : : * array_ndims :
1653 : : * returns the number of dimensions of the array pointed to by "v"
1654 : : */
1655 : : Datum
6340 peter_e@gmx.net 1656 : 1244 : array_ndims(PG_FUNCTION_ARGS)
1657 : : {
3100 tgl@sss.pgh.pa.us 1658 : 1244 : AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
1659 : :
1660 : : /* Sanity check: does it look like an array at all? */
3958 1661 [ - + - - : 1244 : if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
+ + - + -
- - + ]
6340 peter_e@gmx.net 1662 : 6 : PG_RETURN_NULL();
1663 : :
3958 tgl@sss.pgh.pa.us 1664 [ - + ]: 1238 : PG_RETURN_INT32(AARR_NDIM(v));
1665 : : }
1666 : :
1667 : : /*
1668 : : * array_dims :
1669 : : * returns the dimensions of the array pointed to by "v", as a "text"
1670 : : */
1671 : : Datum
9406 1672 : 4750 : array_dims(PG_FUNCTION_ARGS)
1673 : : {
3100 1674 : 4750 : AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
1675 : : char *p;
1676 : : int i;
1677 : : int *dimv,
1678 : : *lb;
1679 : :
1680 : : /*
1681 : : * 33 since we assume 15 digits per number + ':' +'[]'
1682 : : *
1683 : : * +1 for trailing null
1684 : : */
1685 : : char buf[MAXDIM * 33 + 1];
1686 : :
1687 : : /* Sanity check: does it look like an array at all? */
3958 1688 [ - + - - : 4750 : if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
+ + - + -
- - + ]
6564 1689 : 34 : PG_RETURN_NULL();
1690 : :
3958 1691 [ - + ]: 4716 : dimv = AARR_DIMS(v);
1692 [ - + ]: 4716 : lb = AARR_LBOUND(v);
1693 : :
6564 1694 : 4716 : p = buf;
3958 1695 [ - + + + ]: 9486 : for (i = 0; i < AARR_NDIM(v); i++)
1696 : : {
10416 bruce@momjian.us 1697 : 4770 : sprintf(p, "[%d:%d]", lb[i], dimv[i] + lb[i] - 1);
1698 : 4770 : p += strlen(p);
1699 : : }
1700 : :
6564 tgl@sss.pgh.pa.us 1701 : 4716 : PG_RETURN_TEXT_P(cstring_to_text(buf));
1702 : : }
1703 : :
1704 : : /*
1705 : : * array_lower :
1706 : : * returns the lower dimension, of the DIM requested, for
1707 : : * the array pointed to by "v", as an int4
1708 : : */
1709 : : Datum
8528 bruce@momjian.us 1710 : 12960 : array_lower(PG_FUNCTION_ARGS)
1711 : : {
3100 tgl@sss.pgh.pa.us 1712 : 12960 : AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
8528 bruce@momjian.us 1713 : 12960 : int reqdim = PG_GETARG_INT32(1);
1714 : : int *lb;
1715 : : int result;
1716 : :
1717 : : /* Sanity check: does it look like an array at all? */
3958 tgl@sss.pgh.pa.us 1718 [ + + + - : 12960 : if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
+ - + + -
+ - + ]
8528 bruce@momjian.us 1719 :UBC 0 : PG_RETURN_NULL();
1720 : :
1721 : : /* Sanity check: was the requested dim valid */
3958 tgl@sss.pgh.pa.us 1722 [ + - + + :CBC 12960 : if (reqdim <= 0 || reqdim > AARR_NDIM(v))
- + ]
8528 bruce@momjian.us 1723 :UBC 0 : PG_RETURN_NULL();
1724 : :
3958 tgl@sss.pgh.pa.us 1725 [ + + ]:CBC 12960 : lb = AARR_LBOUND(v);
8528 bruce@momjian.us 1726 : 12960 : result = lb[reqdim - 1];
1727 : :
1728 : 12960 : PG_RETURN_INT32(result);
1729 : : }
1730 : :
1731 : : /*
1732 : : * array_upper :
1733 : : * returns the upper dimension, of the DIM requested, for
1734 : : * the array pointed to by "v", as an int4
1735 : : */
1736 : : Datum
1737 : 13305 : array_upper(PG_FUNCTION_ARGS)
1738 : : {
3100 tgl@sss.pgh.pa.us 1739 : 13305 : AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
8528 bruce@momjian.us 1740 : 13305 : int reqdim = PG_GETARG_INT32(1);
1741 : : int *dimv,
1742 : : *lb;
1743 : : int result;
1744 : :
1745 : : /* Sanity check: does it look like an array at all? */
3958 tgl@sss.pgh.pa.us 1746 [ + + + - : 13305 : if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
+ + + + -
+ - + ]
8528 bruce@momjian.us 1747 : 15 : PG_RETURN_NULL();
1748 : :
1749 : : /* Sanity check: was the requested dim valid */
3958 tgl@sss.pgh.pa.us 1750 [ + - + + : 13290 : if (reqdim <= 0 || reqdim > AARR_NDIM(v))
- + ]
8528 bruce@momjian.us 1751 :UBC 0 : PG_RETURN_NULL();
1752 : :
3958 tgl@sss.pgh.pa.us 1753 [ + + ]:CBC 13290 : lb = AARR_LBOUND(v);
1754 [ + + ]: 13290 : dimv = AARR_DIMS(v);
1755 : :
8528 bruce@momjian.us 1756 : 13290 : result = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
1757 : :
1758 : 13290 : PG_RETURN_INT32(result);
1759 : : }
1760 : :
1761 : : /*
1762 : : * array_length :
1763 : : * returns the length, of the dimension requested, for
1764 : : * the array pointed to by "v", as an int4
1765 : : */
1766 : : Datum
6332 peter_e@gmx.net 1767 : 56888 : array_length(PG_FUNCTION_ARGS)
1768 : : {
3100 tgl@sss.pgh.pa.us 1769 : 56888 : AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
6332 peter_e@gmx.net 1770 : 56888 : int reqdim = PG_GETARG_INT32(1);
1771 : : int *dimv;
1772 : : int result;
1773 : :
1774 : : /* Sanity check: does it look like an array at all? */
3958 tgl@sss.pgh.pa.us 1775 [ + + + - : 56888 : if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
+ - + + -
+ - + ]
6332 peter_e@gmx.net 1776 :UBC 0 : PG_RETURN_NULL();
1777 : :
1778 : : /* Sanity check: was the requested dim valid */
3958 tgl@sss.pgh.pa.us 1779 [ + + + + :CBC 56888 : if (reqdim <= 0 || reqdim > AARR_NDIM(v))
+ + ]
6332 peter_e@gmx.net 1780 : 6 : PG_RETURN_NULL();
1781 : :
3958 tgl@sss.pgh.pa.us 1782 [ + + ]: 56882 : dimv = AARR_DIMS(v);
1783 : :
6332 peter_e@gmx.net 1784 : 56882 : result = dimv[reqdim - 1];
1785 : :
1786 : 56882 : PG_RETURN_INT32(result);
1787 : : }
1788 : :
1789 : : /*
1790 : : * array_cardinality:
1791 : : * returns the total number of elements in an array
1792 : : */
1793 : : Datum
4436 rhaas@postgresql.org 1794 : 1266 : array_cardinality(PG_FUNCTION_ARGS)
1795 : : {
3100 tgl@sss.pgh.pa.us 1796 : 1266 : AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
1797 : :
3958 1798 [ - + - + ]: 1266 : PG_RETURN_INT32(ArrayGetNItems(AARR_NDIM(v), AARR_DIMS(v)));
1799 : : }
1800 : :
1801 : :
1802 : : /*
1803 : : * array_get_element :
1804 : : * This routine takes an array datum and a subscript array and returns
1805 : : * the referenced item as a Datum. Note that for a pass-by-reference
1806 : : * datatype, the returned Datum is a pointer into the array object.
1807 : : *
1808 : : * This handles both ordinary varlena arrays and fixed-length arrays.
1809 : : *
1810 : : * Inputs:
1811 : : * arraydatum: the array object (mustn't be NULL)
1812 : : * nSubscripts: number of subscripts supplied
1813 : : * indx[]: the subscript values
1814 : : * arraytyplen: pg_type.typlen for the array type
1815 : : * elmlen: pg_type.typlen for the array's element type
1816 : : * elmbyval: pg_type.typbyval for the array's element type
1817 : : * elmalign: pg_type.typalign for the array's element type
1818 : : *
1819 : : * Outputs:
1820 : : * The return value is the element Datum.
1821 : : * *isNull is set to indicate whether the element is NULL.
1822 : : */
1823 : : Datum
4045 1824 : 478921 : array_get_element(Datum arraydatum,
1825 : : int nSubscripts,
1826 : : int *indx,
1827 : : int arraytyplen,
1828 : : int elmlen,
1829 : : bool elmbyval,
1830 : : char elmalign,
1831 : : bool *isNull)
1832 : : {
1833 : : int i,
1834 : : ndim,
1835 : : *dim,
1836 : : *lb,
1837 : : offset,
1838 : : fixedDim[1],
1839 : : fixedLb[1];
1840 : : char *arraydataptr,
1841 : : *retptr;
1842 : : bits8 *arraynullsptr;
1843 : :
7423 1844 [ + + ]: 478921 : if (arraytyplen > 0)
1845 : : {
1846 : : /*
1847 : : * fixed-length arrays -- these are assumed to be 1-d, 0-based
1848 : : */
9366 1849 : 220315 : ndim = 1;
7423 1850 : 220315 : fixedDim[0] = arraytyplen / elmlen;
9366 1851 : 220315 : fixedLb[0] = 0;
1852 : 220315 : dim = fixedDim;
1853 : 220315 : lb = fixedLb;
4045 1854 : 220315 : arraydataptr = (char *) DatumGetPointer(arraydatum);
7423 1855 : 220315 : arraynullsptr = NULL;
1856 : : }
3958 1857 [ + + + + ]: 258606 : else if (VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(arraydatum)))
1858 : : {
1859 : : /* expanded array: let's do this in a separate function */
1860 : 2507 : return array_get_element_expanded(arraydatum,
1861 : : nSubscripts,
1862 : : indx,
1863 : : arraytyplen,
1864 : : elmlen,
1865 : : elmbyval,
1866 : : elmalign,
1867 : : isNull);
1868 : : }
1869 : : else
1870 : : {
1871 : : /* detoast array if necessary, producing normal varlena input */
1872 : 256099 : ArrayType *array = DatumGetArrayTypeP(arraydatum);
1873 : :
9366 1874 : 256099 : ndim = ARR_NDIM(array);
1875 : 256099 : dim = ARR_DIMS(array);
1876 : 256099 : lb = ARR_LBOUND(array);
1877 [ + + ]: 256099 : arraydataptr = ARR_DATA_PTR(array);
7423 1878 [ + + ]: 256099 : arraynullsptr = ARR_NULLBITMAP(array);
1879 : : }
1880 : :
1881 : : /*
1882 : : * Return NULL for invalid subscript
1883 : : */
9366 1884 [ + + + - : 476414 : if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
- + ]
1885 : : {
7423 1886 : 48 : *isNull = true;
1887 : 48 : return (Datum) 0;
1888 : : }
9366 1889 [ + + ]: 926164 : for (i = 0; i < ndim; i++)
1890 : : {
1891 [ + + + + ]: 476414 : if (indx[i] < lb[i] || indx[i] >= (dim[i] + lb[i]))
1892 : : {
7423 1893 : 26616 : *isNull = true;
1894 : 26616 : return (Datum) 0;
1895 : : }
1896 : : }
1897 : :
1898 : : /*
1899 : : * Calculate the element number
1900 : : */
9367 1901 : 449750 : offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
1902 : :
1903 : : /*
1904 : : * Check for NULL array element
1905 : : */
7423 1906 [ + + ]: 449750 : if (array_get_isnull(arraynullsptr, offset))
1907 : : {
1908 : 36 : *isNull = true;
1909 : 36 : return (Datum) 0;
1910 : : }
1911 : :
1912 : : /*
1913 : : * OK, get the element
1914 : : */
8780 peter_e@gmx.net 1915 : 449714 : *isNull = false;
7423 tgl@sss.pgh.pa.us 1916 : 449714 : retptr = array_seek(arraydataptr, 0, arraynullsptr, offset,
1917 : : elmlen, elmbyval, elmalign);
9367 1918 : 449714 : return ArrayCast(retptr, elmbyval, elmlen);
1919 : : }
1920 : :
1921 : : /*
1922 : : * Implementation of array_get_element() for an expanded array
1923 : : */
1924 : : static Datum
3958 1925 : 2507 : array_get_element_expanded(Datum arraydatum,
1926 : : int nSubscripts, int *indx,
1927 : : int arraytyplen,
1928 : : int elmlen, bool elmbyval, char elmalign,
1929 : : bool *isNull)
1930 : : {
1931 : : ExpandedArrayHeader *eah;
1932 : : int i,
1933 : : ndim,
1934 : : *dim,
1935 : : *lb,
1936 : : offset;
1937 : : Datum *dvalues;
1938 : : bool *dnulls;
1939 : :
1940 : 2507 : eah = (ExpandedArrayHeader *) DatumGetEOHP(arraydatum);
1941 [ - + ]: 2507 : Assert(eah->ea_magic == EA_MAGIC);
1942 : :
1943 : : /* sanity-check caller's info against object */
1944 [ - + ]: 2507 : Assert(arraytyplen == -1);
1945 [ - + ]: 2507 : Assert(elmlen == eah->typlen);
1946 [ - + ]: 2507 : Assert(elmbyval == eah->typbyval);
1947 [ - + ]: 2507 : Assert(elmalign == eah->typalign);
1948 : :
1949 : 2507 : ndim = eah->ndims;
1950 : 2507 : dim = eah->dims;
1951 : 2507 : lb = eah->lbound;
1952 : :
1953 : : /*
1954 : : * Return NULL for invalid subscript
1955 : : */
1956 [ + + + - : 2507 : if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
- + ]
1957 : : {
1958 : 3 : *isNull = true;
1959 : 3 : return (Datum) 0;
1960 : : }
1961 [ + + ]: 5008 : for (i = 0; i < ndim; i++)
1962 : : {
1963 [ + - - + ]: 2504 : if (indx[i] < lb[i] || indx[i] >= (dim[i] + lb[i]))
1964 : : {
3958 tgl@sss.pgh.pa.us 1965 :UBC 0 : *isNull = true;
1966 : 0 : return (Datum) 0;
1967 : : }
1968 : : }
1969 : :
1970 : : /*
1971 : : * Calculate the element number
1972 : : */
3958 tgl@sss.pgh.pa.us 1973 :CBC 2504 : offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
1974 : :
1975 : : /*
1976 : : * Deconstruct array if we didn't already. Note that we apply this even
1977 : : * if the input is nominally read-only: it should be safe enough.
1978 : : */
1979 : 2504 : deconstruct_expanded_array(eah);
1980 : :
1981 : 2504 : dvalues = eah->dvalues;
1982 : 2504 : dnulls = eah->dnulls;
1983 : :
1984 : : /*
1985 : : * Check for NULL array element
1986 : : */
1987 [ - + - - ]: 2504 : if (dnulls && dnulls[offset])
1988 : : {
3958 tgl@sss.pgh.pa.us 1989 :UBC 0 : *isNull = true;
1990 : 0 : return (Datum) 0;
1991 : : }
1992 : :
1993 : : /*
1994 : : * OK, get the element. It's OK to return a pass-by-ref value as a
1995 : : * pointer into the expanded array, for the same reason that regular
1996 : : * array_get_element can return a pointer into flat arrays: the value is
1997 : : * assumed not to change for as long as the Datum reference can exist.
1998 : : */
3958 tgl@sss.pgh.pa.us 1999 :CBC 2504 : *isNull = false;
2000 : 2504 : return dvalues[offset];
2001 : : }
2002 : :
2003 : : /*
2004 : : * array_get_slice :
2005 : : * This routine takes an array and a range of indices (upperIndx and
2006 : : * lowerIndx), creates a new array structure for the referred elements
2007 : : * and returns a pointer to it.
2008 : : *
2009 : : * This handles both ordinary varlena arrays and fixed-length arrays.
2010 : : *
2011 : : * Inputs:
2012 : : * arraydatum: the array object (mustn't be NULL)
2013 : : * nSubscripts: number of subscripts supplied (must be same for upper/lower)
2014 : : * upperIndx[]: the upper subscript values
2015 : : * lowerIndx[]: the lower subscript values
2016 : : * upperProvided[]: true for provided upper subscript values
2017 : : * lowerProvided[]: true for provided lower subscript values
2018 : : * arraytyplen: pg_type.typlen for the array type
2019 : : * elmlen: pg_type.typlen for the array's element type
2020 : : * elmbyval: pg_type.typbyval for the array's element type
2021 : : * elmalign: pg_type.typalign for the array's element type
2022 : : *
2023 : : * Outputs:
2024 : : * The return value is the new array Datum (it's never NULL)
2025 : : *
2026 : : * Omitted upper and lower subscript values are replaced by the corresponding
2027 : : * array bound.
2028 : : *
2029 : : * NOTE: we assume it is OK to scribble on the provided subscript arrays
2030 : : * lowerIndx[] and upperIndx[]; also, these arrays must be of size MAXDIM
2031 : : * even when nSubscripts is less. These are generally just temporaries.
2032 : : */
2033 : : Datum
4045 2034 : 204 : array_get_slice(Datum arraydatum,
2035 : : int nSubscripts,
2036 : : int *upperIndx,
2037 : : int *lowerIndx,
2038 : : bool *upperProvided,
2039 : : bool *lowerProvided,
2040 : : int arraytyplen,
2041 : : int elmlen,
2042 : : bool elmbyval,
2043 : : char elmalign)
2044 : : {
2045 : : ArrayType *array;
2046 : : ArrayType *newarray;
2047 : : int i,
2048 : : ndim,
2049 : : *dim,
2050 : : *lb,
2051 : : *newlb;
2052 : : int fixedDim[1],
2053 : : fixedLb[1];
2054 : : Oid elemtype;
2055 : : char *arraydataptr;
2056 : : bits8 *arraynullsptr;
2057 : : int32 dataoffset;
2058 : : int bytes,
2059 : : span[MAXDIM];
2060 : :
7423 2061 [ + + ]: 204 : if (arraytyplen > 0)
2062 : : {
2063 : : /*
2064 : : * fixed-length arrays -- currently, cannot slice these because parser
2065 : : * labels output as being of the fixed-length array type! Code below
2066 : : * shows how we could support it if the parser were changed to label
2067 : : * output as a suitable varlena array type.
2068 : : */
8267 2069 [ + - ]: 12 : ereport(ERROR,
2070 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2071 : : errmsg("slices of fixed-length arrays not implemented")));
2072 : :
2073 : : /*
2074 : : * fixed-length arrays -- these are assumed to be 1-d, 0-based
2075 : : *
2076 : : * XXX where would we get the correct ELEMTYPE from?
2077 : : */
2078 : : ndim = 1;
2079 : : fixedDim[0] = arraytyplen / elmlen;
2080 : : fixedLb[0] = 0;
2081 : : dim = fixedDim;
2082 : : lb = fixedLb;
2083 : : elemtype = InvalidOid; /* XXX */
2084 : : arraydataptr = (char *) DatumGetPointer(arraydatum);
2085 : : arraynullsptr = NULL;
2086 : : }
2087 : : else
2088 : : {
2089 : : /* detoast input array if necessary */
4045 2090 : 192 : array = DatumGetArrayTypeP(arraydatum);
2091 : :
9366 2092 : 192 : ndim = ARR_NDIM(array);
2093 : 192 : dim = ARR_DIMS(array);
2094 : 192 : lb = ARR_LBOUND(array);
7423 2095 : 192 : elemtype = ARR_ELEMTYPE(array);
9366 2096 [ + + ]: 192 : arraydataptr = ARR_DATA_PTR(array);
7423 2097 [ + + ]: 192 : arraynullsptr = ARR_NULLBITMAP(array);
2098 : : }
2099 : :
2100 : : /*
2101 : : * Check provided subscripts. A slice exceeding the current array limits
2102 : : * is silently truncated to the array limits. If we end up with an empty
2103 : : * slice, return an empty array.
2104 : : */
8761 2105 [ + + + - : 192 : if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
- + ]
4045 2106 : 48 : return PointerGetDatum(construct_empty_array(elemtype));
2107 : :
8761 2108 [ + + ]: 330 : for (i = 0; i < nSubscripts; i++)
2109 : : {
3736 2110 [ + + + + ]: 192 : if (!lowerProvided[i] || lowerIndx[i] < lb[i])
9366 2111 : 63 : lowerIndx[i] = lb[i];
3736 2112 [ + + + + ]: 192 : if (!upperProvided[i] || upperIndx[i] >= (dim[i] + lb[i]))
9366 2113 : 36 : upperIndx[i] = dim[i] + lb[i] - 1;
10416 bruce@momjian.us 2114 [ + + ]: 192 : if (lowerIndx[i] > upperIndx[i])
4045 tgl@sss.pgh.pa.us 2115 : 6 : return PointerGetDatum(construct_empty_array(elemtype));
2116 : : }
2117 : : /* fill any missing subscript positions with full array range */
8761 2118 [ + + ]: 162 : for (; i < ndim; i++)
2119 : : {
2120 : 24 : lowerIndx[i] = lb[i];
2121 : 24 : upperIndx[i] = dim[i] + lb[i] - 1;
2122 [ - + ]: 24 : if (lowerIndx[i] > upperIndx[i])
4045 tgl@sss.pgh.pa.us 2123 :UBC 0 : return PointerGetDatum(construct_empty_array(elemtype));
2124 : : }
2125 : :
8761 tgl@sss.pgh.pa.us 2126 :CBC 138 : mda_get_range(ndim, span, lowerIndx, upperIndx);
2127 : :
7423 2128 : 138 : bytes = array_slice_size(arraydataptr, arraynullsptr,
2129 : : ndim, dim, lb,
2130 : : lowerIndx, upperIndx,
2131 : : elmlen, elmbyval, elmalign);
2132 : :
2133 : : /*
2134 : : * Currently, we put a null bitmap in the result if the source has one;
2135 : : * could be smarter ...
2136 : : */
2137 [ + + ]: 138 : if (arraynullsptr)
2138 : : {
2139 : 18 : dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, ArrayGetNItems(ndim, span));
2140 : 18 : bytes += dataoffset;
2141 : : }
2142 : : else
2143 : : {
2144 : 120 : dataoffset = 0; /* marker for no null bitmap */
2145 : 120 : bytes += ARR_OVERHEAD_NONULLS(ndim);
2146 : : }
2147 : :
5436 2148 : 138 : newarray = (ArrayType *) palloc0(bytes);
6956 2149 : 138 : SET_VARSIZE(newarray, bytes);
9366 2150 : 138 : newarray->ndim = ndim;
7423 2151 : 138 : newarray->dataoffset = dataoffset;
2152 : 138 : newarray->elemtype = elemtype;
9366 2153 : 138 : memcpy(ARR_DIMS(newarray), span, ndim * sizeof(int));
2154 : :
2155 : : /*
2156 : : * Lower bounds of the new array are set to 1. Formerly (before 7.3) we
2157 : : * copied the given lowerIndx values ... but that seems confusing.
2158 : : */
8779 2159 : 138 : newlb = ARR_LBOUND(newarray);
2160 [ + + ]: 348 : for (i = 0; i < ndim; i++)
2161 : 210 : newlb[i] = 1;
2162 : :
7423 2163 : 138 : array_extract_slice(newarray,
2164 : : ndim, dim, lb,
2165 : : arraydataptr, arraynullsptr,
2166 : : lowerIndx, upperIndx,
2167 : : elmlen, elmbyval, elmalign);
2168 : :
4045 2169 : 138 : return PointerGetDatum(newarray);
2170 : : }
2171 : :
2172 : : /*
2173 : : * array_set_element :
2174 : : * This routine sets the value of one array element (specified by
2175 : : * a subscript array) to a new value specified by "dataValue".
2176 : : *
2177 : : * This handles both ordinary varlena arrays and fixed-length arrays.
2178 : : *
2179 : : * Inputs:
2180 : : * arraydatum: the initial array object (mustn't be NULL)
2181 : : * nSubscripts: number of subscripts supplied
2182 : : * indx[]: the subscript values
2183 : : * dataValue: the datum to be inserted at the given position
2184 : : * isNull: whether dataValue is NULL
2185 : : * arraytyplen: pg_type.typlen for the array type
2186 : : * elmlen: pg_type.typlen for the array's element type
2187 : : * elmbyval: pg_type.typbyval for the array's element type
2188 : : * elmalign: pg_type.typalign for the array's element type
2189 : : *
2190 : : * Result:
2191 : : * A new array is returned, just like the old except for the one
2192 : : * modified entry. The original array object is not changed,
2193 : : * unless what is passed is a read-write reference to an expanded
2194 : : * array object; in that case the expanded array is updated in-place.
2195 : : *
2196 : : * For one-dimensional arrays only, we allow the array to be extended
2197 : : * by assigning to a position outside the existing subscript range; any
2198 : : * positions between the existing elements and the new one are set to NULLs.
2199 : : * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
2200 : : *
2201 : : * NOTE: For assignments, we throw an error for invalid subscripts etc,
2202 : : * rather than returning a NULL as the fetch operations do.
2203 : : */
2204 : : Datum
2205 : 2089 : array_set_element(Datum arraydatum,
2206 : : int nSubscripts,
2207 : : int *indx,
2208 : : Datum dataValue,
2209 : : bool isNull,
2210 : : int arraytyplen,
2211 : : int elmlen,
2212 : : bool elmbyval,
2213 : : char elmalign)
2214 : : {
2215 : : ArrayType *array;
2216 : : ArrayType *newarray;
2217 : : int i,
2218 : : ndim,
2219 : : dim[MAXDIM],
2220 : : lb[MAXDIM],
2221 : : offset;
2222 : : char *elt_ptr;
2223 : : bool newhasnulls;
2224 : : bits8 *oldnullbitmap;
2225 : : int oldnitems,
2226 : : newnitems,
2227 : : olddatasize,
2228 : : newsize,
2229 : : olditemlen,
2230 : : newitemlen,
2231 : : overheadlen,
2232 : : oldoverheadlen,
2233 : : addedbefore,
2234 : : addedafter,
2235 : : lenbefore,
2236 : : lenafter;
41 tgl@sss.pgh.pa.us 2237 :GNC 2089 : uint8 elmalignby = typalign_to_alignby(elmalign);
2238 : :
7423 tgl@sss.pgh.pa.us 2239 [ + + ]:CBC 2089 : if (arraytyplen > 0)
2240 : : {
2241 : : /*
2242 : : * fixed-length arrays -- these are assumed to be 1-d, 0-based. We
2243 : : * cannot extend them, either.
2244 : : */
2245 : : char *resultarray;
2246 : :
9367 2247 [ - + ]: 9 : if (nSubscripts != 1)
8267 tgl@sss.pgh.pa.us 2248 [ # # ]:UBC 0 : ereport(ERROR,
2249 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2250 : : errmsg("wrong number of array subscripts")));
2251 : :
1770 tgl@sss.pgh.pa.us 2252 [ + - + + ]:CBC 9 : if (indx[0] < 0 || indx[0] >= arraytyplen / elmlen)
8267 2253 [ + - ]: 3 : ereport(ERROR,
2254 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2255 : : errmsg("array subscript out of range")));
2256 : :
7423 2257 [ - + ]: 6 : if (isNull)
7423 tgl@sss.pgh.pa.us 2258 [ # # ]:UBC 0 : ereport(ERROR,
2259 : : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
2260 : : errmsg("cannot assign null value to an element of a fixed-length array")));
2261 : :
4045 tgl@sss.pgh.pa.us 2262 :CBC 6 : resultarray = (char *) palloc(arraytyplen);
2263 : 6 : memcpy(resultarray, DatumGetPointer(arraydatum), arraytyplen);
103 peter@eisentraut.org 2264 :GNC 6 : elt_ptr = resultarray + indx[0] * elmlen;
41 tgl@sss.pgh.pa.us 2265 : 6 : ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalignby, elt_ptr);
4045 tgl@sss.pgh.pa.us 2266 :CBC 6 : return PointerGetDatum(resultarray);
2267 : : }
2268 : :
7423 2269 [ + - - + ]: 2080 : if (nSubscripts <= 0 || nSubscripts > MAXDIM)
7423 tgl@sss.pgh.pa.us 2270 [ # # ]:UBC 0 : ereport(ERROR,
2271 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2272 : : errmsg("wrong number of array subscripts")));
2273 : :
2274 : : /* make sure item to be inserted is not toasted */
7423 tgl@sss.pgh.pa.us 2275 [ + + + + ]:CBC 2080 : if (elmlen == -1 && !isNull)
9362 2276 : 1313 : dataValue = PointerGetDatum(PG_DETOAST_DATUM(dataValue));
2277 : :
3958 2278 [ + + + - ]: 2080 : if (VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(arraydatum)))
2279 : : {
2280 : : /* expanded array: let's do this in a separate function */
2281 : 1008 : return array_set_element_expanded(arraydatum,
2282 : : nSubscripts,
2283 : : indx,
2284 : : dataValue,
2285 : : isNull,
2286 : : arraytyplen,
2287 : : elmlen,
2288 : : elmbyval,
2289 : : elmalign);
2290 : : }
2291 : :
2292 : : /* detoast input array if necessary */
4045 2293 : 1072 : array = DatumGetArrayTypeP(arraydatum);
2294 : :
9367 2295 : 1072 : ndim = ARR_NDIM(array);
2296 : :
2297 : : /*
2298 : : * if number of dims is zero, i.e. an empty array, create an array with
2299 : : * nSubscripts dimensions, and set the lower bounds to the supplied
2300 : : * subscripts
2301 : : */
8297 2302 [ + + ]: 1072 : if (ndim == 0)
2303 : : {
8259 bruce@momjian.us 2304 : 174 : Oid elmtype = ARR_ELEMTYPE(array);
2305 : :
8297 tgl@sss.pgh.pa.us 2306 [ + + ]: 349 : for (i = 0; i < nSubscripts; i++)
2307 : : {
2308 : 175 : dim[i] = 1;
2309 : 175 : lb[i] = indx[i];
2310 : : }
2311 : :
4045 2312 : 174 : return PointerGetDatum(construct_md_array(&dataValue, &isNull,
2313 : : nSubscripts, dim, lb,
2314 : : elmtype,
2315 : : elmlen, elmbyval, elmalign));
2316 : : }
2317 : :
7423 2318 [ + + ]: 898 : if (ndim != nSubscripts)
8267 2319 [ + - ]: 3 : ereport(ERROR,
2320 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2321 : : errmsg("wrong number of array subscripts")));
2322 : :
2323 : : /* copy dim/lb since we may modify them */
9366 2324 : 895 : memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
2325 : 895 : memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
2326 : :
7107 2327 [ + + + + ]: 895 : newhasnulls = (ARR_HASNULL(array) || isNull);
2328 : 895 : addedbefore = addedafter = 0;
2329 : :
2330 : : /*
2331 : : * Check subscripts. We assume the existing subscripts passed
2332 : : * ArrayCheckBounds, so that dim[i] + lb[i] can be computed without
2333 : : * overflow. But we must beware of other overflows in our calculations of
2334 : : * new dim[] values.
2335 : : */
2336 [ + + ]: 895 : if (ndim == 1)
2337 : : {
2338 [ + + ]: 892 : if (indx[0] < lb[0])
2339 : : {
2340 : : /* addedbefore = lb[0] - indx[0]; */
2341 : : /* dim[0] += addedbefore; */
860 2342 [ + - - + ]: 24 : if (pg_sub_s32_overflow(lb[0], indx[0], &addedbefore) ||
2343 : 12 : pg_add_s32_overflow(dim[0], addedbefore, &dim[0]))
860 tgl@sss.pgh.pa.us 2344 [ # # ]:UBC 0 : ereport(ERROR,
2345 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2346 : : errmsg("array size exceeds the maximum allowed (%zu)",
2347 : : MaxArraySize)));
7107 tgl@sss.pgh.pa.us 2348 :CBC 12 : lb[0] = indx[0];
2349 [ + + ]: 12 : if (addedbefore > 1)
3189 2350 : 6 : newhasnulls = true; /* will insert nulls */
2351 : : }
7107 2352 [ + + ]: 892 : if (indx[0] >= (dim[0] + lb[0]))
2353 : : {
2354 : : /* addedafter = indx[0] - (dim[0] + lb[0]) + 1; */
2355 : : /* dim[0] += addedafter; */
860 2356 [ + + + - ]: 1413 : if (pg_sub_s32_overflow(indx[0], dim[0] + lb[0], &addedafter) ||
2357 [ - + ]: 1410 : pg_add_s32_overflow(addedafter, 1, &addedafter) ||
2358 : 705 : pg_add_s32_overflow(dim[0], addedafter, &dim[0]))
2359 [ + - ]: 3 : ereport(ERROR,
2360 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2361 : : errmsg("array size exceeds the maximum allowed (%zu)",
2362 : : MaxArraySize)));
7107 2363 [ + + ]: 705 : if (addedafter > 1)
3189 2364 : 18 : newhasnulls = true; /* will insert nulls */
2365 : : }
2366 : : }
2367 : : else
2368 : : {
2369 : : /*
2370 : : * XXX currently we do not support extending multi-dimensional arrays
2371 : : * during assignment
2372 : : */
7107 2373 [ + + ]: 9 : for (i = 0; i < ndim; i++)
2374 : : {
2375 [ + - ]: 6 : if (indx[i] < lb[i] ||
2376 [ - + ]: 6 : indx[i] >= (dim[i] + lb[i]))
8267 tgl@sss.pgh.pa.us 2377 [ # # ]:UBC 0 : ereport(ERROR,
2378 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2379 : : errmsg("array subscript out of range")));
2380 : : }
2381 : : }
2382 : :
2383 : : /* This checks for overflow of the array dimensions */
1770 tgl@sss.pgh.pa.us 2384 :CBC 892 : newnitems = ArrayGetNItems(ndim, dim);
2385 : 892 : ArrayCheckBounds(ndim, dim, lb);
2386 : :
2387 : : /*
2388 : : * Compute sizes of items and areas to copy
2389 : : */
7107 2390 [ + + ]: 892 : if (newhasnulls)
2391 : 67 : overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, newnitems);
2392 : : else
7423 2393 : 825 : overheadlen = ARR_OVERHEAD_NONULLS(ndim);
2394 : 892 : oldnitems = ArrayGetNItems(ndim, ARR_DIMS(array));
2395 [ + + ]: 892 : oldnullbitmap = ARR_NULLBITMAP(array);
2396 [ + + ]: 892 : oldoverheadlen = ARR_DATA_OFFSET(array);
2397 : 892 : olddatasize = ARR_SIZE(array) - oldoverheadlen;
7107 2398 [ + + ]: 892 : if (addedbefore)
2399 : : {
7423 2400 : 12 : offset = 0;
9366 2401 : 12 : lenbefore = 0;
2402 : 12 : olditemlen = 0;
2403 : 12 : lenafter = olddatasize;
2404 : : }
7107 2405 [ + + ]: 880 : else if (addedafter)
2406 : : {
7423 2407 : 705 : offset = oldnitems;
9366 2408 : 705 : lenbefore = olddatasize;
2409 : 705 : olditemlen = 0;
2410 : 705 : lenafter = 0;
2411 : : }
2412 : : else
2413 : : {
2414 : 175 : offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
7423 2415 [ + + ]: 175 : elt_ptr = array_seek(ARR_DATA_PTR(array), 0, oldnullbitmap, offset,
2416 : : elmlen, elmbyval, elmalign);
9366 2417 [ + + ]: 175 : lenbefore = (int) (elt_ptr - ARR_DATA_PTR(array));
7423 2418 [ + + ]: 175 : if (array_get_isnull(oldnullbitmap, offset))
2419 : 12 : olditemlen = 0;
2420 : : else
2421 : : {
6918 2422 [ + + + - : 163 : olditemlen = att_addlength_pointer(0, elmlen, elt_ptr);
- - - - -
- - - - +
- - ]
41 tgl@sss.pgh.pa.us 2423 :GNC 163 : olditemlen = att_nominal_alignby(olditemlen, elmalignby);
2424 : : }
103 peter@eisentraut.org 2425 : 175 : lenafter = olddatasize - lenbefore - olditemlen;
2426 : : }
2427 : :
7423 tgl@sss.pgh.pa.us 2428 [ + + ]:CBC 892 : if (isNull)
2429 : 10 : newitemlen = 0;
2430 : : else
2431 : : {
6918 2432 [ + + + - : 882 : newitemlen = att_addlength_datum(0, elmlen, dataValue);
- - - - -
- - - - +
- - ]
41 tgl@sss.pgh.pa.us 2433 :GNC 882 : newitemlen = att_nominal_alignby(newitemlen, elmalignby);
2434 : : }
2435 : :
9366 tgl@sss.pgh.pa.us 2436 :CBC 892 : newsize = overheadlen + lenbefore + newitemlen + lenafter;
2437 : :
2438 : : /*
2439 : : * OK, create the new array and fill in header/dimensions
2440 : : */
5436 2441 : 892 : newarray = (ArrayType *) palloc0(newsize);
6956 2442 : 892 : SET_VARSIZE(newarray, newsize);
9366 2443 : 892 : newarray->ndim = ndim;
7423 2444 [ + + ]: 892 : newarray->dataoffset = newhasnulls ? overheadlen : 0;
8602 2445 : 892 : newarray->elemtype = ARR_ELEMTYPE(array);
9366 2446 : 892 : memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
2447 : 892 : memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
2448 : :
2449 : : /*
2450 : : * Fill in data
2451 : : */
2452 : 892 : memcpy((char *) newarray + overheadlen,
2453 : : (char *) array + oldoverheadlen,
2454 : : lenbefore);
7423 2455 [ + + ]: 892 : if (!isNull)
41 tgl@sss.pgh.pa.us 2456 :GNC 882 : ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalignby,
7423 tgl@sss.pgh.pa.us 2457 :CBC 882 : (char *) newarray + overheadlen + lenbefore);
9366 2458 : 892 : memcpy((char *) newarray + overheadlen + lenbefore + newitemlen,
7423 2459 : 892 : (char *) array + oldoverheadlen + lenbefore + olditemlen,
2460 : : lenafter);
2461 : :
2462 : : /*
2463 : : * Fill in nulls bitmap if needed
2464 : : *
2465 : : * Note: it's possible we just replaced the last NULL with a non-NULL, and
2466 : : * could get rid of the bitmap. Seems not worth testing for though.
2467 : : */
2468 [ + + ]: 892 : if (newhasnulls)
2469 : : {
7418 bruce@momjian.us 2470 [ + - ]: 67 : bits8 *newnullbitmap = ARR_NULLBITMAP(newarray);
2471 : :
2472 : : /* palloc0 above already marked any inserted positions as nulls */
2473 : : /* Fix the inserted value */
7107 tgl@sss.pgh.pa.us 2474 [ + + ]: 67 : if (addedafter)
2475 : 28 : array_set_isnull(newnullbitmap, newnitems - 1, isNull);
2476 : : else
2477 : 39 : array_set_isnull(newnullbitmap, offset, isNull);
2478 : : /* Fix the copied range(s) */
2479 [ + + ]: 67 : if (addedbefore)
2480 : 12 : array_bitmap_copy(newnullbitmap, addedbefore,
2481 : : oldnullbitmap, 0,
2482 : : oldnitems);
2483 : : else
2484 : : {
7423 2485 : 55 : array_bitmap_copy(newnullbitmap, 0,
2486 : : oldnullbitmap, 0,
2487 : : offset);
7107 2488 [ + + ]: 55 : if (addedafter == 0)
7418 bruce@momjian.us 2489 : 27 : array_bitmap_copy(newnullbitmap, offset + 1,
2490 : : oldnullbitmap, offset + 1,
7423 tgl@sss.pgh.pa.us 2491 : 27 : oldnitems - offset - 1);
2492 : : }
2493 : : }
2494 : :
4045 2495 : 892 : return PointerGetDatum(newarray);
2496 : : }
2497 : :
2498 : : /*
2499 : : * Implementation of array_set_element() for an expanded array
2500 : : *
2501 : : * Note: as with any operation on a read/write expanded object, we must
2502 : : * take pains not to leave the object in a corrupt state if we fail partway
2503 : : * through.
2504 : : */
2505 : : static Datum
3958 2506 : 1008 : array_set_element_expanded(Datum arraydatum,
2507 : : int nSubscripts, int *indx,
2508 : : Datum dataValue, bool isNull,
2509 : : int arraytyplen,
2510 : : int elmlen, bool elmbyval, char elmalign)
2511 : : {
2512 : : ExpandedArrayHeader *eah;
2513 : : Datum *dvalues;
2514 : : bool *dnulls;
2515 : : int i,
2516 : : ndim,
2517 : : dim[MAXDIM],
2518 : : lb[MAXDIM],
2519 : : offset;
2520 : : bool dimschanged,
2521 : : newhasnulls;
2522 : : int addedbefore,
2523 : : addedafter;
2524 : : char *oldValue;
2525 : :
2526 : : /* Convert to R/W object if not so already */
2527 : 1008 : eah = DatumGetExpandedArray(arraydatum);
2528 : :
2529 : : /* Sanity-check caller's info against object; we don't use it otherwise */
2530 [ - + ]: 1008 : Assert(arraytyplen == -1);
2531 [ - + ]: 1008 : Assert(elmlen == eah->typlen);
2532 [ - + ]: 1008 : Assert(elmbyval == eah->typbyval);
2533 [ - + ]: 1008 : Assert(elmalign == eah->typalign);
2534 : :
2535 : : /*
2536 : : * Copy dimension info into local storage. This allows us to modify the
2537 : : * dimensions if needed, while not messing up the expanded value if we
2538 : : * fail partway through.
2539 : : */
2540 : 1008 : ndim = eah->ndims;
2541 [ + - - + ]: 1008 : Assert(ndim >= 0 && ndim <= MAXDIM);
2542 : 1008 : memcpy(dim, eah->dims, ndim * sizeof(int));
2543 : 1008 : memcpy(lb, eah->lbound, ndim * sizeof(int));
2544 : 1008 : dimschanged = false;
2545 : :
2546 : : /*
2547 : : * if number of dims is zero, i.e. an empty array, create an array with
2548 : : * nSubscripts dimensions, and set the lower bounds to the supplied
2549 : : * subscripts.
2550 : : */
2551 [ + + ]: 1008 : if (ndim == 0)
2552 : : {
2553 : : /*
2554 : : * Allocate adequate space for new dimension info. This is harmless
2555 : : * if we fail later.
2556 : : */
2557 [ + - - + ]: 208 : Assert(nSubscripts > 0 && nSubscripts <= MAXDIM);
2558 : 208 : eah->dims = (int *) MemoryContextAllocZero(eah->hdr.eoh_context,
2559 : : nSubscripts * sizeof(int));
2560 : 208 : eah->lbound = (int *) MemoryContextAllocZero(eah->hdr.eoh_context,
2561 : : nSubscripts * sizeof(int));
2562 : :
2563 : : /* Update local copies of dimension info */
2564 : 208 : ndim = nSubscripts;
2565 [ + + ]: 416 : for (i = 0; i < nSubscripts; i++)
2566 : : {
2567 : 208 : dim[i] = 0;
2568 : 208 : lb[i] = indx[i];
2569 : : }
2570 : 208 : dimschanged = true;
2571 : : }
2572 [ - + ]: 800 : else if (ndim != nSubscripts)
3958 tgl@sss.pgh.pa.us 2573 [ # # ]:UBC 0 : ereport(ERROR,
2574 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2575 : : errmsg("wrong number of array subscripts")));
2576 : :
2577 : : /*
2578 : : * Deconstruct array if we didn't already. (Someday maybe add a special
2579 : : * case path for fixed-length, no-nulls cases, where we can overwrite an
2580 : : * element in place without ever deconstructing. But today is not that
2581 : : * day.)
2582 : : */
3958 tgl@sss.pgh.pa.us 2583 :CBC 1008 : deconstruct_expanded_array(eah);
2584 : :
2585 : : /*
2586 : : * Copy new element into array's context, if needed (we assume it's
2587 : : * already detoasted, so no junk should be created). Doing this before
2588 : : * we've made any significant changes ensures that our behavior is sane
2589 : : * even when the source is a reference to some element of this same array.
2590 : : * If we fail further down, this memory is leaked, but that's reasonably
2591 : : * harmless.
2592 : : */
2593 [ + + + - ]: 1008 : if (!eah->typbyval && !isNull)
2594 : : {
2595 : 449 : MemoryContext oldcxt = MemoryContextSwitchTo(eah->hdr.eoh_context);
2596 : :
2597 : 449 : dataValue = datumCopy(dataValue, false, eah->typlen);
2598 : 449 : MemoryContextSwitchTo(oldcxt);
2599 : : }
2600 : :
2601 : 1008 : dvalues = eah->dvalues;
2602 : 1008 : dnulls = eah->dnulls;
2603 : :
2604 [ + - - + ]: 1008 : newhasnulls = ((dnulls != NULL) || isNull);
2605 : 1008 : addedbefore = addedafter = 0;
2606 : :
2607 : : /*
2608 : : * Check subscripts (this logic must match array_set_element). We assume
2609 : : * the existing subscripts passed ArrayCheckBounds, so that dim[i] + lb[i]
2610 : : * can be computed without overflow. But we must beware of other
2611 : : * overflows in our calculations of new dim[] values.
2612 : : */
2613 [ + - ]: 1008 : if (ndim == 1)
2614 : : {
2615 [ + + ]: 1008 : if (indx[0] < lb[0])
2616 : : {
2617 : : /* addedbefore = lb[0] - indx[0]; */
2618 : : /* dim[0] += addedbefore; */
860 2619 [ + - - + ]: 80 : if (pg_sub_s32_overflow(lb[0], indx[0], &addedbefore) ||
2620 : 40 : pg_add_s32_overflow(dim[0], addedbefore, &dim[0]))
860 tgl@sss.pgh.pa.us 2621 [ # # ]:UBC 0 : ereport(ERROR,
2622 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2623 : : errmsg("array size exceeds the maximum allowed (%zu)",
2624 : : MaxArraySize)));
3958 tgl@sss.pgh.pa.us 2625 :CBC 40 : lb[0] = indx[0];
2626 : 40 : dimschanged = true;
2627 [ - + ]: 40 : if (addedbefore > 1)
3189 tgl@sss.pgh.pa.us 2628 :UBC 0 : newhasnulls = true; /* will insert nulls */
2629 : : }
3958 tgl@sss.pgh.pa.us 2630 [ + + ]:CBC 1008 : if (indx[0] >= (dim[0] + lb[0]))
2631 : : {
2632 : : /* addedafter = indx[0] - (dim[0] + lb[0]) + 1; */
2633 : : /* dim[0] += addedafter; */
860 2634 [ + + + - ]: 1883 : if (pg_sub_s32_overflow(indx[0], dim[0] + lb[0], &addedafter) ||
2635 [ - + ]: 1880 : pg_add_s32_overflow(addedafter, 1, &addedafter) ||
2636 : 940 : pg_add_s32_overflow(dim[0], addedafter, &dim[0]))
2637 [ + - ]: 3 : ereport(ERROR,
2638 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2639 : : errmsg("array size exceeds the maximum allowed (%zu)",
2640 : : MaxArraySize)));
3958 2641 : 940 : dimschanged = true;
2642 [ - + ]: 940 : if (addedafter > 1)
3189 tgl@sss.pgh.pa.us 2643 :UBC 0 : newhasnulls = true; /* will insert nulls */
2644 : : }
2645 : : }
2646 : : else
2647 : : {
2648 : : /*
2649 : : * XXX currently we do not support extending multi-dimensional arrays
2650 : : * during assignment
2651 : : */
3958 2652 [ # # ]: 0 : for (i = 0; i < ndim; i++)
2653 : : {
2654 [ # # ]: 0 : if (indx[i] < lb[i] ||
2655 [ # # ]: 0 : indx[i] >= (dim[i] + lb[i]))
2656 [ # # ]: 0 : ereport(ERROR,
2657 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2658 : : errmsg("array subscript out of range")));
2659 : : }
2660 : : }
2661 : :
2662 : : /* Check for overflow of the array dimensions */
1770 tgl@sss.pgh.pa.us 2663 [ + + ]:CBC 1005 : if (dimschanged)
2664 : : {
2665 : 980 : (void) ArrayGetNItems(ndim, dim);
2666 : 980 : ArrayCheckBounds(ndim, dim, lb);
2667 : : }
2668 : :
2669 : : /* Now we can calculate linear offset of target item in array */
3958 2670 : 1005 : offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
2671 : :
2672 : : /* Physically enlarge existing dvalues/dnulls arrays if needed */
2673 [ + + ]: 1005 : if (dim[0] > eah->dvalueslen)
2674 : : {
2675 : : /* We want some extra space if we're enlarging */
2676 : 974 : int newlen = dim[0] + dim[0] / 8;
2677 : :
2678 : 974 : newlen = Max(newlen, dim[0]); /* integer overflow guard */
2679 : 974 : eah->dvalues = dvalues = (Datum *)
2680 : 974 : repalloc(dvalues, newlen * sizeof(Datum));
2681 [ - + ]: 974 : if (dnulls)
3958 tgl@sss.pgh.pa.us 2682 :UBC 0 : eah->dnulls = dnulls = (bool *)
2683 : 0 : repalloc(dnulls, newlen * sizeof(bool));
3958 tgl@sss.pgh.pa.us 2684 :CBC 974 : eah->dvalueslen = newlen;
2685 : : }
2686 : :
2687 : : /*
2688 : : * If we need a nulls bitmap and don't already have one, create it, being
2689 : : * sure to mark all existing entries as not null.
2690 : : */
2691 [ - + - - ]: 1005 : if (newhasnulls && dnulls == NULL)
3958 tgl@sss.pgh.pa.us 2692 :UBC 0 : eah->dnulls = dnulls = (bool *)
2693 : 0 : MemoryContextAllocZero(eah->hdr.eoh_context,
2694 : 0 : eah->dvalueslen * sizeof(bool));
2695 : :
2696 : : /*
2697 : : * We now have all the needed space allocated, so we're ready to make
2698 : : * irreversible changes. Be very wary of allowing failure below here.
2699 : : */
2700 : :
2701 : : /* Flattened value will no longer represent array accurately */
3958 tgl@sss.pgh.pa.us 2702 :CBC 1005 : eah->fvalue = NULL;
2703 : : /* And we don't know the flattened size either */
2704 : 1005 : eah->flat_size = 0;
2705 : :
2706 : : /* Update dimensionality info if needed */
2707 [ + + ]: 1005 : if (dimschanged)
2708 : : {
2709 : 980 : eah->ndims = ndim;
2710 : 980 : memcpy(eah->dims, dim, ndim * sizeof(int));
2711 : 980 : memcpy(eah->lbound, lb, ndim * sizeof(int));
2712 : : }
2713 : :
2714 : : /* Reposition items if needed, and fill addedbefore items with nulls */
2715 [ + + ]: 1005 : if (addedbefore > 0)
2716 : : {
2717 : 40 : memmove(dvalues + addedbefore, dvalues, eah->nelems * sizeof(Datum));
2718 [ + + ]: 80 : for (i = 0; i < addedbefore; i++)
2719 : 40 : dvalues[i] = (Datum) 0;
2720 [ - + ]: 40 : if (dnulls)
2721 : : {
3958 tgl@sss.pgh.pa.us 2722 :UBC 0 : memmove(dnulls + addedbefore, dnulls, eah->nelems * sizeof(bool));
2723 [ # # ]: 0 : for (i = 0; i < addedbefore; i++)
2724 : 0 : dnulls[i] = true;
2725 : : }
3958 tgl@sss.pgh.pa.us 2726 :CBC 40 : eah->nelems += addedbefore;
2727 : : }
2728 : :
2729 : : /* fill addedafter items with nulls */
2730 [ + + ]: 1005 : if (addedafter > 0)
2731 : : {
2732 [ + + ]: 1880 : for (i = 0; i < addedafter; i++)
2733 : 940 : dvalues[eah->nelems + i] = (Datum) 0;
2734 [ - + ]: 940 : if (dnulls)
2735 : : {
3958 tgl@sss.pgh.pa.us 2736 [ # # ]:UBC 0 : for (i = 0; i < addedafter; i++)
2737 : 0 : dnulls[eah->nelems + i] = true;
2738 : : }
3958 tgl@sss.pgh.pa.us 2739 :CBC 940 : eah->nelems += addedafter;
2740 : : }
2741 : :
2742 : : /* Grab old element value for pfree'ing, if needed. */
2743 [ + + - + : 1005 : if (!eah->typbyval && (dnulls == NULL || !dnulls[offset]))
- - ]
2744 : 449 : oldValue = (char *) DatumGetPointer(dvalues[offset]);
2745 : : else
2746 : 556 : oldValue = NULL;
2747 : :
2748 : : /* And finally we can insert the new element. */
2749 : 1005 : dvalues[offset] = dataValue;
2750 [ - + ]: 1005 : if (dnulls)
3958 tgl@sss.pgh.pa.us 2751 :UBC 0 : dnulls[offset] = isNull;
2752 : :
2753 : : /*
2754 : : * Free old element if needed; this keeps repeated element replacements
2755 : : * from bloating the array's storage. If the pfree somehow fails, it
2756 : : * won't corrupt the array.
2757 : : */
3958 tgl@sss.pgh.pa.us 2758 [ + + ]:CBC 1005 : if (oldValue)
2759 : : {
2760 : : /* Don't try to pfree a part of the original flat array */
2761 [ + - - + ]: 1 : if (oldValue < eah->fstartptr || oldValue >= eah->fendptr)
3958 tgl@sss.pgh.pa.us 2762 :UBC 0 : pfree(oldValue);
2763 : : }
2764 : :
2765 : : /* Done, return standard TOAST pointer for object */
3958 tgl@sss.pgh.pa.us 2766 :CBC 1005 : return EOHPGetRWDatum(&eah->hdr);
2767 : : }
2768 : :
2769 : : /*
2770 : : * array_set_slice :
2771 : : * This routine sets the value of a range of array locations (specified
2772 : : * by upper and lower subscript values) to new values passed as
2773 : : * another array.
2774 : : *
2775 : : * This handles both ordinary varlena arrays and fixed-length arrays.
2776 : : *
2777 : : * Inputs:
2778 : : * arraydatum: the initial array object (mustn't be NULL)
2779 : : * nSubscripts: number of subscripts supplied (must be same for upper/lower)
2780 : : * upperIndx[]: the upper subscript values
2781 : : * lowerIndx[]: the lower subscript values
2782 : : * upperProvided[]: true for provided upper subscript values
2783 : : * lowerProvided[]: true for provided lower subscript values
2784 : : * srcArrayDatum: the source for the inserted values
2785 : : * isNull: indicates whether srcArrayDatum is NULL
2786 : : * arraytyplen: pg_type.typlen for the array type
2787 : : * elmlen: pg_type.typlen for the array's element type
2788 : : * elmbyval: pg_type.typbyval for the array's element type
2789 : : * elmalign: pg_type.typalign for the array's element type
2790 : : *
2791 : : * Result:
2792 : : * A new array is returned, just like the old except for the
2793 : : * modified range. The original array object is not changed.
2794 : : *
2795 : : * Omitted upper and lower subscript values are replaced by the corresponding
2796 : : * array bound.
2797 : : *
2798 : : * For one-dimensional arrays only, we allow the array to be extended
2799 : : * by assigning to positions outside the existing subscript range; any
2800 : : * positions between the existing elements and the new ones are set to NULLs.
2801 : : * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
2802 : : *
2803 : : * NOTE: we assume it is OK to scribble on the provided index arrays
2804 : : * lowerIndx[] and upperIndx[]; also, these arrays must be of size MAXDIM
2805 : : * even when nSubscripts is less. These are generally just temporaries.
2806 : : *
2807 : : * NOTE: For assignments, we throw an error for silly subscripts etc,
2808 : : * rather than returning a NULL or empty array as the fetch operations do.
2809 : : */
2810 : : Datum
4045 2811 : 131 : array_set_slice(Datum arraydatum,
2812 : : int nSubscripts,
2813 : : int *upperIndx,
2814 : : int *lowerIndx,
2815 : : bool *upperProvided,
2816 : : bool *lowerProvided,
2817 : : Datum srcArrayDatum,
2818 : : bool isNull,
2819 : : int arraytyplen,
2820 : : int elmlen,
2821 : : bool elmbyval,
2822 : : char elmalign)
2823 : : {
2824 : : ArrayType *array;
2825 : : ArrayType *srcArray;
2826 : : ArrayType *newarray;
2827 : : int i,
2828 : : ndim,
2829 : : dim[MAXDIM],
2830 : : lb[MAXDIM],
2831 : : span[MAXDIM];
2832 : : bool newhasnulls;
2833 : : int nitems,
2834 : : nsrcitems,
2835 : : olddatasize,
2836 : : newsize,
2837 : : olditemsize,
2838 : : newitemsize,
2839 : : overheadlen,
2840 : : oldoverheadlen,
2841 : : addedbefore,
2842 : : addedafter,
2843 : : lenbefore,
2844 : : lenafter,
2845 : : itemsbefore,
2846 : : itemsafter,
2847 : : nolditems;
2848 : :
2849 : : /* Currently, assignment from a NULL source array is a no-op */
7423 2850 [ - + ]: 131 : if (isNull)
4045 tgl@sss.pgh.pa.us 2851 :UBC 0 : return arraydatum;
2852 : :
7423 tgl@sss.pgh.pa.us 2853 [ - + ]:CBC 131 : if (arraytyplen > 0)
2854 : : {
2855 : : /*
2856 : : * fixed-length arrays -- not got round to doing this...
2857 : : */
8267 tgl@sss.pgh.pa.us 2858 [ # # ]:UBC 0 : ereport(ERROR,
2859 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2860 : : errmsg("updates on slices of fixed-length arrays not implemented")));
2861 : : }
2862 : :
2863 : : /* detoast arrays if necessary */
4045 tgl@sss.pgh.pa.us 2864 :CBC 131 : array = DatumGetArrayTypeP(arraydatum);
2865 : 131 : srcArray = DatumGetArrayTypeP(srcArrayDatum);
2866 : :
2867 : : /* note: we assume srcArray contains no toasted elements */
2868 : :
9367 2869 : 131 : ndim = ARR_NDIM(array);
2870 : :
2871 : : /*
2872 : : * if number of dims is zero, i.e. an empty array, create an array with
2873 : : * nSubscripts dimensions, and set the upper and lower bounds to the
2874 : : * supplied subscripts
2875 : : */
8297 2876 [ + + ]: 131 : if (ndim == 0)
2877 : : {
2878 : : Datum *dvalues;
2879 : : bool *dnulls;
2880 : : int nelems;
8259 bruce@momjian.us 2881 : 28 : Oid elmtype = ARR_ELEMTYPE(array);
2882 : :
8297 tgl@sss.pgh.pa.us 2883 : 28 : deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
2884 : : &dvalues, &dnulls, &nelems);
2885 : :
2886 [ + + ]: 56 : for (i = 0; i < nSubscripts; i++)
2887 : : {
3736 2888 [ + + - + ]: 37 : if (!upperProvided[i] || !lowerProvided[i])
2889 [ + - ]: 3 : ereport(ERROR,
2890 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2891 : : errmsg("array slice subscript must provide both boundaries"),
2892 : : errdetail("When assigning to a slice of an empty array value,"
2893 : : " slice boundaries must be fully specified.")));
2894 : :
2895 : : /* compute "upperIndx[i] - lowerIndx[i] + 1", detecting overflow */
600 nathan@postgresql.or 2896 [ + + + + ]: 65 : if (pg_sub_s32_overflow(upperIndx[i], lowerIndx[i], &dim[i]) ||
2897 : 31 : pg_add_s32_overflow(dim[i], 1, &dim[i]))
2898 [ + - ]: 6 : ereport(ERROR,
2899 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2900 : : errmsg("array size exceeds the maximum allowed (%zu)",
2901 : : MaxArraySize)));
2902 : :
8297 tgl@sss.pgh.pa.us 2903 : 28 : lb[i] = lowerIndx[i];
2904 : : }
2905 : :
2906 : : /* complain if too few source items; we ignore extras, however */
7950 2907 [ - + ]: 19 : if (nelems < ArrayGetNItems(nSubscripts, dim))
7950 tgl@sss.pgh.pa.us 2908 [ # # ]:UBC 0 : ereport(ERROR,
2909 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2910 : : errmsg("source array too small")));
2911 : :
4045 tgl@sss.pgh.pa.us 2912 :CBC 19 : return PointerGetDatum(construct_md_array(dvalues, dnulls, nSubscripts,
2913 : : dim, lb, elmtype,
2914 : : elmlen, elmbyval, elmalign));
2915 : : }
2916 : :
8761 2917 [ + - + - : 103 : if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
- + ]
8267 tgl@sss.pgh.pa.us 2918 [ # # ]:UBC 0 : ereport(ERROR,
2919 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2920 : : errmsg("wrong number of array subscripts")));
2921 : :
2922 : : /* copy dim/lb since we may modify them */
9366 tgl@sss.pgh.pa.us 2923 :CBC 103 : memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
2924 : 103 : memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
2925 : :
7107 2926 [ + + - + ]: 103 : newhasnulls = (ARR_HASNULL(array) || ARR_HASNULL(srcArray));
2927 : 103 : addedbefore = addedafter = 0;
2928 : :
2929 : : /*
2930 : : * Check subscripts. We assume the existing subscripts passed
2931 : : * ArrayCheckBounds, so that dim[i] + lb[i] can be computed without
2932 : : * overflow. But we must beware of other overflows in our calculations of
2933 : : * new dim[] values.
2934 : : */
2935 [ + + ]: 103 : if (ndim == 1)
2936 : : {
2937 [ - + ]: 88 : Assert(nSubscripts == 1);
3736 2938 [ + + ]: 88 : if (!lowerProvided[0])
2939 : 18 : lowerIndx[0] = lb[0];
2940 [ + + ]: 88 : if (!upperProvided[0])
2941 : 21 : upperIndx[0] = dim[0] + lb[0] - 1;
7107 2942 [ - + ]: 88 : if (lowerIndx[0] > upperIndx[0])
8267 tgl@sss.pgh.pa.us 2943 [ # # ]:UBC 0 : ereport(ERROR,
2944 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2945 : : errmsg("upper bound cannot be less than lower bound")));
7107 tgl@sss.pgh.pa.us 2946 [ + + ]:CBC 88 : if (lowerIndx[0] < lb[0])
2947 : : {
2948 : : /* addedbefore = lb[0] - lowerIndx[0]; */
2949 : : /* dim[0] += addedbefore; */
860 2950 [ + - - + ]: 48 : if (pg_sub_s32_overflow(lb[0], lowerIndx[0], &addedbefore) ||
2951 : 24 : pg_add_s32_overflow(dim[0], addedbefore, &dim[0]))
860 tgl@sss.pgh.pa.us 2952 [ # # ]:UBC 0 : ereport(ERROR,
2953 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2954 : : errmsg("array size exceeds the maximum allowed (%zu)",
2955 : : MaxArraySize)));
7107 tgl@sss.pgh.pa.us 2956 :CBC 24 : lb[0] = lowerIndx[0];
860 2957 [ + + ]: 24 : if (addedbefore > 1)
2958 : 18 : newhasnulls = true; /* will insert nulls */
2959 : : }
7107 2960 [ + + ]: 88 : if (upperIndx[0] >= (dim[0] + lb[0]))
2961 : : {
2962 : : /* addedafter = upperIndx[0] - (dim[0] + lb[0]) + 1; */
2963 : : /* dim[0] += addedafter; */
860 2964 [ + + + - ]: 59 : if (pg_sub_s32_overflow(upperIndx[0], dim[0] + lb[0], &addedafter) ||
2965 [ - + ]: 56 : pg_add_s32_overflow(addedafter, 1, &addedafter) ||
2966 : 28 : pg_add_s32_overflow(dim[0], addedafter, &dim[0]))
2967 [ + - ]: 3 : ereport(ERROR,
2968 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2969 : : errmsg("array size exceeds the maximum allowed (%zu)",
2970 : : MaxArraySize)));
2971 [ + + ]: 28 : if (addedafter > 1)
3189 2972 : 18 : newhasnulls = true; /* will insert nulls */
2973 : : }
2974 : : }
2975 : : else
2976 : : {
2977 : : /*
2978 : : * XXX currently we do not support extending multi-dimensional arrays
2979 : : * during assignment
2980 : : */
7107 2981 [ + + ]: 51 : for (i = 0; i < nSubscripts; i++)
2982 : : {
3736 2983 [ + + ]: 36 : if (!lowerProvided[i])
2984 : 6 : lowerIndx[i] = lb[i];
2985 [ + + ]: 36 : if (!upperProvided[i])
2986 : 12 : upperIndx[i] = dim[i] + lb[i] - 1;
7107 2987 [ - + ]: 36 : if (lowerIndx[i] > upperIndx[i])
7107 tgl@sss.pgh.pa.us 2988 [ # # ]:UBC 0 : ereport(ERROR,
2989 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2990 : : errmsg("upper bound cannot be less than lower bound")));
7107 tgl@sss.pgh.pa.us 2991 [ + - ]:CBC 36 : if (lowerIndx[i] < lb[i] ||
2992 [ - + ]: 36 : upperIndx[i] >= (dim[i] + lb[i]))
8267 tgl@sss.pgh.pa.us 2993 [ # # ]:UBC 0 : ereport(ERROR,
2994 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2995 : : errmsg("array subscript out of range")));
2996 : : }
2997 : : /* fill any missing subscript positions with full array range */
7107 tgl@sss.pgh.pa.us 2998 [ - + ]:CBC 15 : for (; i < ndim; i++)
2999 : : {
7107 tgl@sss.pgh.pa.us 3000 :UBC 0 : lowerIndx[i] = lb[i];
3001 : 0 : upperIndx[i] = dim[i] + lb[i] - 1;
3002 [ # # ]: 0 : if (lowerIndx[i] > upperIndx[i])
8267 3003 [ # # ]: 0 : ereport(ERROR,
3004 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
3005 : : errmsg("upper bound cannot be less than lower bound")));
3006 : : }
3007 : : }
3008 : :
3009 : : /* Do this mainly to check for overflow */
7423 tgl@sss.pgh.pa.us 3010 :CBC 100 : nitems = ArrayGetNItems(ndim, dim);
1770 3011 : 100 : ArrayCheckBounds(ndim, dim, lb);
3012 : :
3013 : : /*
3014 : : * Make sure source array has enough entries. Note we ignore the shape of
3015 : : * the source array and just read entries serially.
3016 : : */
9367 3017 : 100 : mda_get_range(ndim, span, lowerIndx, upperIndx);
9366 3018 : 100 : nsrcitems = ArrayGetNItems(ndim, span);
3019 [ + + ]: 100 : if (nsrcitems > ArrayGetNItems(ARR_NDIM(srcArray), ARR_DIMS(srcArray)))
8267 3020 [ + - ]: 3 : ereport(ERROR,
3021 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
3022 : : errmsg("source array too small")));
3023 : :
3024 : : /*
3025 : : * Compute space occupied by new entries, space occupied by replaced
3026 : : * entries, and required space for new array.
3027 : : */
7107 3028 [ + + ]: 97 : if (newhasnulls)
7423 3029 : 48 : overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
3030 : : else
3031 : 49 : overheadlen = ARR_OVERHEAD_NONULLS(ndim);
3032 [ + + ]: 97 : newitemsize = array_nelems_size(ARR_DATA_PTR(srcArray), 0,
3033 [ + + ]: 97 : ARR_NULLBITMAP(srcArray), nsrcitems,
3034 : : elmlen, elmbyval, elmalign);
3035 [ + + ]: 97 : oldoverheadlen = ARR_DATA_OFFSET(array);
3036 : 97 : olddatasize = ARR_SIZE(array) - oldoverheadlen;
9366 3037 [ + + ]: 97 : if (ndim > 1)
3038 : : {
3039 : : /*
3040 : : * here we do not need to cope with extension of the array; it would
3041 : : * be a lot more complicated if we had to do so...
3042 : : */
7423 3043 [ - + ]: 15 : olditemsize = array_slice_size(ARR_DATA_PTR(array),
3044 [ - + ]: 15 : ARR_NULLBITMAP(array),
3045 : : ndim, dim, lb,
3046 : : lowerIndx, upperIndx,
3047 : : elmlen, elmbyval, elmalign);
3189 3048 : 15 : lenbefore = lenafter = 0; /* keep compiler quiet */
7423 3049 : 15 : itemsbefore = itemsafter = nolditems = 0;
3050 : : }
3051 : : else
3052 : : {
3053 : : /*
3054 : : * here we must allow for possibility of slice larger than orig array
3055 : : * and/or not adjacent to orig array subscripts
3056 : : */
9124 bruce@momjian.us 3057 : 82 : int oldlb = ARR_LBOUND(array)[0];
3058 : 82 : int oldub = oldlb + ARR_DIMS(array)[0] - 1;
8791 3059 : 82 : int slicelb = Max(oldlb, lowerIndx[0]);
3060 : 82 : int sliceub = Min(oldub, upperIndx[0]);
9124 3061 [ + + ]: 82 : char *oldarraydata = ARR_DATA_PTR(array);
7423 tgl@sss.pgh.pa.us 3062 [ + + ]: 82 : bits8 *oldarraybitmap = ARR_NULLBITMAP(array);
3063 : :
3064 : : /* count/size of old array entries that will go before the slice */
7107 3065 : 82 : itemsbefore = Min(slicelb, oldub + 1) - oldlb;
7423 3066 : 82 : lenbefore = array_nelems_size(oldarraydata, 0, oldarraybitmap,
3067 : : itemsbefore,
3068 : : elmlen, elmbyval, elmalign);
3069 : : /* count/size of old array entries that will be replaced by slice */
9366 3070 [ + + ]: 82 : if (slicelb > sliceub)
3071 : : {
7423 3072 : 27 : nolditems = 0;
9366 3073 : 27 : olditemsize = 0;
3074 : : }
3075 : : else
3076 : : {
7423 3077 : 55 : nolditems = sliceub - slicelb + 1;
9366 3078 : 55 : olditemsize = array_nelems_size(oldarraydata + lenbefore,
3079 : : itemsbefore, oldarraybitmap,
3080 : : nolditems,
3081 : : elmlen, elmbyval, elmalign);
3082 : : }
3083 : : /* count/size of old array entries that will go after the slice */
5536 3084 : 82 : itemsafter = oldub + 1 - Max(sliceub + 1, oldlb);
9366 3085 : 82 : lenafter = olddatasize - lenbefore - olditemsize;
3086 : : }
3087 : :
3088 : 97 : newsize = overheadlen + olddatasize - olditemsize + newitemsize;
3089 : :
5436 3090 : 97 : newarray = (ArrayType *) palloc0(newsize);
6956 3091 : 97 : SET_VARSIZE(newarray, newsize);
9366 3092 : 97 : newarray->ndim = ndim;
7423 3093 [ + + ]: 97 : newarray->dataoffset = newhasnulls ? overheadlen : 0;
8602 3094 : 97 : newarray->elemtype = ARR_ELEMTYPE(array);
9366 3095 : 97 : memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
3096 : 97 : memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
3097 : :
3098 [ + + ]: 97 : if (ndim > 1)
3099 : : {
3100 : : /*
3101 : : * here we do not need to cope with extension of the array; it would
3102 : : * be a lot more complicated if we had to do so...
3103 : : */
7423 3104 : 15 : array_insert_slice(newarray, array, srcArray,
3105 : : ndim, dim, lb,
3106 : : lowerIndx, upperIndx,
3107 : : elmlen, elmbyval, elmalign);
3108 : : }
3109 : : else
3110 : : {
3111 : : /* fill in data */
9366 3112 : 82 : memcpy((char *) newarray + overheadlen,
3113 : : (char *) array + oldoverheadlen,
3114 : : lenbefore);
3115 : 164 : memcpy((char *) newarray + overheadlen + lenbefore,
3116 [ + + ]: 82 : ARR_DATA_PTR(srcArray),
3117 : : newitemsize);
3118 : 82 : memcpy((char *) newarray + overheadlen + lenbefore + newitemsize,
7423 3119 : 82 : (char *) array + oldoverheadlen + lenbefore + olditemsize,
3120 : : lenafter);
3121 : : /* fill in nulls bitmap if needed */
3122 [ + + ]: 82 : if (newhasnulls)
3123 : : {
7418 bruce@momjian.us 3124 [ + - ]: 48 : bits8 *newnullbitmap = ARR_NULLBITMAP(newarray);
3125 [ + - ]: 48 : bits8 *oldnullbitmap = ARR_NULLBITMAP(array);
3126 : :
3127 : : /* palloc0 above already marked any inserted positions as nulls */
7107 tgl@sss.pgh.pa.us 3128 : 48 : array_bitmap_copy(newnullbitmap, addedbefore,
3129 : : oldnullbitmap, 0,
3130 : : itemsbefore);
3131 : 48 : array_bitmap_copy(newnullbitmap, lowerIndx[0] - lb[0],
7423 3132 [ + + ]: 48 : ARR_NULLBITMAP(srcArray), 0,
3133 : : nsrcitems);
7107 3134 : 48 : array_bitmap_copy(newnullbitmap, addedbefore + itemsbefore + nolditems,
3135 : : oldnullbitmap, itemsbefore + nolditems,
3136 : : itemsafter);
3137 : : }
3138 : : }
3139 : :
4045 3140 : 97 : return PointerGetDatum(newarray);
3141 : : }
3142 : :
3143 : : /*
3144 : : * array_ref : backwards compatibility wrapper for array_get_element
3145 : : *
3146 : : * This only works for detoasted/flattened varlena arrays, since the array
3147 : : * argument is declared as "ArrayType *". However there's enough code like
3148 : : * that to justify preserving this API.
3149 : : */
3150 : : Datum
3151 : 22640 : array_ref(ArrayType *array, int nSubscripts, int *indx,
3152 : : int arraytyplen, int elmlen, bool elmbyval, char elmalign,
3153 : : bool *isNull)
3154 : : {
3155 : 22640 : return array_get_element(PointerGetDatum(array), nSubscripts, indx,
3156 : : arraytyplen, elmlen, elmbyval, elmalign,
3157 : : isNull);
3158 : : }
3159 : :
3160 : : /*
3161 : : * array_set : backwards compatibility wrapper for array_set_element
3162 : : *
3163 : : * This only works for detoasted/flattened varlena arrays, since the array
3164 : : * argument and result are declared as "ArrayType *". However there's enough
3165 : : * code like that to justify preserving this API.
3166 : : */
3167 : : ArrayType *
3168 : 573 : array_set(ArrayType *array, int nSubscripts, int *indx,
3169 : : Datum dataValue, bool isNull,
3170 : : int arraytyplen, int elmlen, bool elmbyval, char elmalign)
3171 : : {
3172 : 573 : return DatumGetArrayTypeP(array_set_element(PointerGetDatum(array),
3173 : : nSubscripts, indx,
3174 : : dataValue, isNull,
3175 : : arraytyplen,
3176 : : elmlen, elmbyval, elmalign));
3177 : : }
3178 : :
3179 : : /*
3180 : : * array_map()
3181 : : *
3182 : : * Map an array through an arbitrary expression. Return a new array with
3183 : : * the same dimensions and each source element transformed by the given,
3184 : : * already-compiled expression. Each source element is placed in the
3185 : : * innermost_caseval/innermost_casenull fields of the ExprState.
3186 : : *
3187 : : * Parameters are:
3188 : : * * arrayd: Datum representing array argument.
3189 : : * * exprstate: ExprState representing the per-element transformation.
3190 : : * * econtext: context for expression evaluation.
3191 : : * * retType: OID of element type of output array. This must be the same as,
3192 : : * or binary-compatible with, the result type of the expression. It might
3193 : : * be different from the input array's element type.
3194 : : * * amstate: workspace for array_map. Must be zeroed by caller before
3195 : : * first call, and not touched after that.
3196 : : *
3197 : : * It is legitimate to pass a freshly-zeroed ArrayMapState on each call,
3198 : : * but better performance can be had if the state can be preserved across
3199 : : * a series of calls.
3200 : : *
3201 : : * NB: caller must assure that input array is not NULL. NULL elements in
3202 : : * the array are OK however.
3203 : : * NB: caller should be running in econtext's per-tuple memory context.
3204 : : */
3205 : : Datum
3088 3206 : 20523 : array_map(Datum arrayd,
3207 : : ExprState *exprstate, ExprContext *econtext,
3208 : : Oid retType, ArrayMapState *amstate)
3209 : : {
3210 : 20523 : AnyArrayType *v = DatumGetAnyArrayP(arrayd);
3211 : : ArrayType *result;
3212 : : Datum *values;
3213 : : bool *nulls;
3214 : : int *dim;
3215 : : int ndim;
3216 : : int nitems;
3217 : : int i;
7423 3218 : 20523 : int32 nbytes = 0;
3219 : : int32 dataoffset;
3220 : : bool hasnulls;
3221 : : Oid inpType;
3222 : : int inp_typlen;
3223 : : bool inp_typbyval;
3224 : : char inp_typalign;
3225 : : int typlen;
3226 : : bool typbyval;
3227 : : char typalign;
3228 : : uint8 typalignby;
3229 : : array_iter iter;
3230 : : ArrayMetaState *inp_extra;
3231 : : ArrayMetaState *ret_extra;
3088 3232 : 20523 : Datum *transform_source = exprstate->innermost_caseval;
3233 : 20523 : bool *transform_source_isnull = exprstate->innermost_casenull;
3234 : :
3958 3235 [ - + ]: 20523 : inpType = AARR_ELEMTYPE(v);
3236 [ - + ]: 20523 : ndim = AARR_NDIM(v);
3237 [ - + ]: 20523 : dim = AARR_DIMS(v);
9367 3238 : 20523 : nitems = ArrayGetNItems(ndim, dim);
3239 : :
3240 : : /* Check for empty array */
9791 bruce@momjian.us 3241 [ + + ]: 20523 : if (nitems <= 0)
3242 : : {
3243 : : /* Return empty array */
3088 tgl@sss.pgh.pa.us 3244 : 6 : return PointerGetDatum(construct_empty_array(retType));
3245 : : }
3246 : :
3247 : : /*
3248 : : * We arrange to look up info about input and return element types only
3249 : : * once per series of calls, assuming the element type doesn't change
3250 : : * underneath us.
3251 : : */
7661 3252 : 20517 : inp_extra = &amstate->inp_extra;
3253 : 20517 : ret_extra = &amstate->ret_extra;
3254 : :
8297 3255 [ + + ]: 20517 : if (inp_extra->element_type != inpType)
3256 : : {
3257 : 217 : get_typlenbyvalalign(inpType,
3258 : : &inp_extra->typlen,
3259 : : &inp_extra->typbyval,
3260 : : &inp_extra->typalign);
3261 : 217 : inp_extra->element_type = inpType;
3262 : : }
3263 : 20517 : inp_typlen = inp_extra->typlen;
3264 : 20517 : inp_typbyval = inp_extra->typbyval;
3265 : 20517 : inp_typalign = inp_extra->typalign;
3266 : :
3267 [ + + ]: 20517 : if (ret_extra->element_type != retType)
3268 : : {
3269 : 217 : get_typlenbyvalalign(retType,
3270 : : &ret_extra->typlen,
3271 : : &ret_extra->typbyval,
3272 : : &ret_extra->typalign);
3273 : 217 : ret_extra->element_type = retType;
3274 : : }
3275 : 20517 : typlen = ret_extra->typlen;
3276 : 20517 : typbyval = ret_extra->typbyval;
3277 : 20517 : typalign = ret_extra->typalign;
41 tgl@sss.pgh.pa.us 3278 :GNC 20517 : typalignby = typalign_to_alignby(typalign);
3279 : :
3280 : : /* Allocate temporary arrays for new values */
9372 tgl@sss.pgh.pa.us 3281 :CBC 20517 : values = (Datum *) palloc(nitems * sizeof(Datum));
7423 3282 : 20517 : nulls = (bool *) palloc(nitems * sizeof(bool));
3283 : :
3284 : : /* Loop over source data */
41 tgl@sss.pgh.pa.us 3285 :GNC 20517 : array_iter_setup(&iter, v, inp_typlen, inp_typbyval, inp_typalign);
7423 tgl@sss.pgh.pa.us 3286 :CBC 20517 : hasnulls = false;
3287 : :
9791 bruce@momjian.us 3288 [ + + ]: 231350 : for (i = 0; i < nitems; i++)
3289 : : {
3290 : : /* Get source element, checking for NULL */
3088 tgl@sss.pgh.pa.us 3291 : 210849 : *transform_source =
41 tgl@sss.pgh.pa.us 3292 :GNC 210849 : array_iter_next(&iter, transform_source_isnull, i);
3293 : :
3294 : : /* Apply the given expression to source element */
3088 tgl@sss.pgh.pa.us 3295 :CBC 210849 : values[i] = ExecEvalExpr(exprstate, econtext, &nulls[i]);
3296 : :
3297 [ + + ]: 210833 : if (nulls[i])
7423 3298 : 6 : hasnulls = true;
3299 : : else
3300 : : {
3301 : : /* Ensure data is not toasted */
3302 [ + + ]: 210827 : if (typlen == -1)
3303 : 214 : values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
3304 : : /* Update total result size */
6918 3305 [ + + + - : 210827 : nbytes = att_addlength_datum(nbytes, typlen, values[i]);
- - - - -
- - - - +
- - ]
41 tgl@sss.pgh.pa.us 3306 :GNC 210827 : nbytes = att_nominal_alignby(nbytes, typalignby);
3307 : : /* check for overflow of total request */
7423 tgl@sss.pgh.pa.us 3308 [ - + ]:CBC 210827 : if (!AllocSizeIsValid(nbytes))
7423 tgl@sss.pgh.pa.us 3309 [ # # ]:UBC 0 : ereport(ERROR,
3310 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3311 : : errmsg("array size exceeds the maximum allowed (%zu)",
3312 : : MaxAllocSize)));
3313 : : }
3314 : : }
3315 : :
3316 : : /* Allocate and fill the result array */
7423 tgl@sss.pgh.pa.us 3317 [ + + ]:CBC 20501 : if (hasnulls)
3318 : : {
3319 : 3 : dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
3320 : 3 : nbytes += dataoffset;
3321 : : }
3322 : : else
3323 : : {
3324 : 20498 : dataoffset = 0; /* marker for no null bitmap */
3325 : 20498 : nbytes += ARR_OVERHEAD_NONULLS(ndim);
3326 : : }
5436 3327 : 20501 : result = (ArrayType *) palloc0(nbytes);
6956 3328 : 20501 : SET_VARSIZE(result, nbytes);
9372 3329 : 20501 : result->ndim = ndim;
7423 3330 : 20501 : result->dataoffset = dataoffset;
8602 3331 : 20501 : result->elemtype = retType;
3958 3332 [ - + ]: 20501 : memcpy(ARR_DIMS(result), AARR_DIMS(v), ndim * sizeof(int));
3333 [ - + ]: 20501 : memcpy(ARR_LBOUND(result), AARR_LBOUND(v), ndim * sizeof(int));
3334 : :
7423 3335 : 20501 : CopyArrayEls(result,
3336 : : values, nulls, nitems,
3337 : : typlen, typbyval, typalign,
3338 : : false);
3339 : :
3340 : : /*
3341 : : * Note: do not risk trying to pfree the results of the called expression
3342 : : */
9372 3343 : 20501 : pfree(values);
7423 3344 : 20501 : pfree(nulls);
3345 : :
3088 3346 : 20501 : return PointerGetDatum(result);
3347 : : }
3348 : :
3349 : : /*
3350 : : * construct_array --- simple method for constructing an array object
3351 : : *
3352 : : * elems: array of Datum items to become the array contents
3353 : : * (NULL element values are not supported).
3354 : : * nelems: number of items
3355 : : * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
3356 : : *
3357 : : * A palloc'd 1-D array object is constructed and returned. Note that
3358 : : * elem values will be copied into the object even if pass-by-ref type.
3359 : : * Also note the result will be 0-D not 1-D if nelems = 0.
3360 : : *
3361 : : * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
3362 : : * from the system catalogs, given the elmtype. However, the caller is
3363 : : * in a better position to cache this info across multiple uses, or even
3364 : : * to hard-wire values if the element type is hard-wired.
3365 : : */
3366 : : ArrayType *
9372 3367 : 176083 : construct_array(Datum *elems, int nelems,
3368 : : Oid elmtype,
3369 : : int elmlen, bool elmbyval, char elmalign)
3370 : : {
3371 : : int dims[1];
3372 : : int lbs[1];
3373 : :
8377 3374 : 176083 : dims[0] = nelems;
3375 : 176083 : lbs[0] = 1;
3376 : :
7423 3377 : 176083 : return construct_md_array(elems, NULL, 1, dims, lbs,
3378 : : elmtype, elmlen, elmbyval, elmalign);
3379 : : }
3380 : :
3381 : : /*
3382 : : * Like construct_array(), where elmtype must be a built-in type, and
3383 : : * elmlen/elmbyval/elmalign is looked up from hardcoded data. This is often
3384 : : * useful when manipulating arrays from/for system catalogs.
3385 : : */
3386 : : ArrayType *
1353 peter@eisentraut.org 3387 : 125578 : construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
3388 : : {
3389 : : int elmlen;
3390 : : bool elmbyval;
3391 : : char elmalign;
3392 : :
3393 [ + + + + : 125578 : switch (elmtype)
+ + + + +
+ + + - ]
3394 : : {
3395 : 1522 : case CHAROID:
3396 : 1522 : elmlen = 1;
3397 : 1522 : elmbyval = true;
3398 : 1522 : elmalign = TYPALIGN_CHAR;
3399 : 1522 : break;
3400 : :
3401 : 4127 : case CSTRINGOID:
3402 : 4127 : elmlen = -2;
3403 : 4127 : elmbyval = false;
3404 : 4127 : elmalign = TYPALIGN_CHAR;
3405 : 4127 : break;
3406 : :
3407 : 71532 : case FLOAT4OID:
3408 : 71532 : elmlen = sizeof(float4);
3409 : 71532 : elmbyval = true;
3410 : 71532 : elmalign = TYPALIGN_INT;
3411 : 71532 : break;
3412 : :
517 msawada@postgresql.o 3413 : 24 : case FLOAT8OID:
3414 : 24 : elmlen = sizeof(float8);
214 tgl@sss.pgh.pa.us 3415 :GNC 24 : elmbyval = true;
517 msawada@postgresql.o 3416 :CBC 24 : elmalign = TYPALIGN_DOUBLE;
3417 : 24 : break;
3418 : :
1353 peter@eisentraut.org 3419 : 29694 : case INT2OID:
3420 : 29694 : elmlen = sizeof(int16);
3421 : 29694 : elmbyval = true;
3422 : 29694 : elmalign = TYPALIGN_SHORT;
3423 : 29694 : break;
3424 : :
3425 : 3726 : case INT4OID:
3426 : 3726 : elmlen = sizeof(int32);
3427 : 3726 : elmbyval = true;
3428 : 3726 : elmalign = TYPALIGN_INT;
3429 : 3726 : break;
3430 : :
3431 : 2 : case INT8OID:
3432 : 2 : elmlen = sizeof(int64);
214 tgl@sss.pgh.pa.us 3433 :GNC 2 : elmbyval = true;
1353 peter@eisentraut.org 3434 :CBC 2 : elmalign = TYPALIGN_DOUBLE;
3435 : 2 : break;
3436 : :
3437 : 451 : case NAMEOID:
3438 : 451 : elmlen = NAMEDATALEN;
3439 : 451 : elmbyval = false;
3440 : 451 : elmalign = TYPALIGN_CHAR;
3441 : 451 : break;
3442 : :
3443 : 8687 : case OIDOID:
3444 : : case REGTYPEOID:
3445 : 8687 : elmlen = sizeof(Oid);
3446 : 8687 : elmbyval = true;
3447 : 8687 : elmalign = TYPALIGN_INT;
3448 : 8687 : break;
3449 : :
3450 : 5790 : case TEXTOID:
3451 : 5790 : elmlen = -1;
3452 : 5790 : elmbyval = false;
3453 : 5790 : elmalign = TYPALIGN_INT;
3454 : 5790 : break;
3455 : :
3456 : 21 : case TIDOID:
3457 : 21 : elmlen = sizeof(ItemPointerData);
3458 : 21 : elmbyval = false;
3459 : 21 : elmalign = TYPALIGN_SHORT;
3460 : 21 : break;
3461 : :
517 msawada@postgresql.o 3462 : 2 : case XIDOID:
3463 : 2 : elmlen = sizeof(TransactionId);
3464 : 2 : elmbyval = true;
3465 : 2 : elmalign = TYPALIGN_INT;
3466 : 2 : break;
3467 : :
1353 peter@eisentraut.org 3468 :UBC 0 : default:
3469 [ # # ]: 0 : elog(ERROR, "type %u not supported by construct_array_builtin()", elmtype);
3470 : : /* keep compiler quiet */
3471 : : elmlen = 0;
3472 : : elmbyval = false;
3473 : : elmalign = 0;
3474 : : }
3475 : :
1353 peter@eisentraut.org 3476 :CBC 125578 : return construct_array(elems, nelems, elmtype, elmlen, elmbyval, elmalign);
3477 : : }
3478 : :
3479 : : /*
3480 : : * construct_md_array --- simple method for constructing an array object
3481 : : * with arbitrary dimensions and possible NULLs
3482 : : *
3483 : : * elems: array of Datum items to become the array contents
3484 : : * nulls: array of is-null flags (can be NULL if no nulls)
3485 : : * ndims: number of dimensions
3486 : : * dims: integer array with size of each dimension
3487 : : * lbs: integer array with lower bound of each dimension
3488 : : * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
3489 : : *
3490 : : * A palloc'd ndims-D array object is constructed and returned. Note that
3491 : : * elem values will be copied into the object even if pass-by-ref type.
3492 : : * Also note the result will be 0-D not ndims-D if any dims[i] = 0.
3493 : : *
3494 : : * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
3495 : : * from the system catalogs, given the elmtype. However, the caller is
3496 : : * in a better position to cache this info across multiple uses, or even
3497 : : * to hard-wire values if the element type is hard-wired.
3498 : : */
3499 : : ArrayType *
8377 tgl@sss.pgh.pa.us 3500 : 800592 : construct_md_array(Datum *elems,
3501 : : bool *nulls,
3502 : : int ndims,
3503 : : int *dims,
3504 : : int *lbs,
3505 : : Oid elmtype, int elmlen, bool elmbyval, char elmalign)
3506 : : {
3507 : : ArrayType *result;
3508 : : bool hasnulls;
3509 : : int32 nbytes;
3510 : : int32 dataoffset;
3511 : : int i;
3512 : : int nelems;
41 tgl@sss.pgh.pa.us 3513 :GNC 800592 : uint8 elmalignby = typalign_to_alignby(elmalign);
3514 : :
8267 tgl@sss.pgh.pa.us 3515 [ - + ]:CBC 800592 : if (ndims < 0) /* we do allow zero-dimension arrays */
8267 tgl@sss.pgh.pa.us 3516 [ # # ]:UBC 0 : ereport(ERROR,
3517 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3518 : : errmsg("invalid number of dimensions: %d", ndims)));
8267 tgl@sss.pgh.pa.us 3519 [ - + ]:CBC 800592 : if (ndims > MAXDIM)
8267 tgl@sss.pgh.pa.us 3520 [ # # ]:UBC 0 : ereport(ERROR,
3521 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3522 : : errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
3523 : : ndims, MAXDIM)));
3524 : :
3525 : : /* This checks for overflow of the array dimensions */
8377 tgl@sss.pgh.pa.us 3526 :CBC 800592 : nelems = ArrayGetNItems(ndims, dims);
1770 3527 : 800592 : ArrayCheckBounds(ndims, dims, lbs);
3528 : :
3529 : : /* if ndims <= 0 or any dims[i] == 0, return empty array */
3093 3530 [ + + ]: 800592 : if (nelems <= 0)
3531 : 29544 : return construct_empty_array(elmtype);
3532 : :
3533 : : /* compute required space */
7423 3534 : 771048 : nbytes = 0;
3535 : 771048 : hasnulls = false;
3536 [ + + ]: 5730352 : for (i = 0; i < nelems; i++)
3537 : : {
3538 [ + + + + ]: 4959304 : if (nulls && nulls[i])
3539 : : {
3540 : 16521 : hasnulls = true;
3541 : 16521 : continue;
3542 : : }
3543 : : /* make sure data is not toasted */
3544 [ + + ]: 4942783 : if (elmlen == -1)
3545 : 1362539 : elems[i] = PointerGetDatum(PG_DETOAST_DATUM(elems[i]));
6918 3546 [ + + + + : 4942783 : nbytes = att_addlength_datum(nbytes, elmlen, elems[i]);
- + - - -
- - - - +
- + ]
41 tgl@sss.pgh.pa.us 3547 :GNC 4942783 : nbytes = att_nominal_alignby(nbytes, elmalignby);
3548 : : /* check for overflow of total request */
7423 tgl@sss.pgh.pa.us 3549 [ - + ]:CBC 4942783 : if (!AllocSizeIsValid(nbytes))
7423 tgl@sss.pgh.pa.us 3550 [ # # ]:UBC 0 : ereport(ERROR,
3551 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3552 : : errmsg("array size exceeds the maximum allowed (%zu)",
3553 : : MaxAllocSize)));
3554 : : }
3555 : :
3556 : : /* Allocate and initialize result array */
7423 tgl@sss.pgh.pa.us 3557 [ + + ]:CBC 771048 : if (hasnulls)
3558 : : {
3559 : 8887 : dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nelems);
3560 : 8887 : nbytes += dataoffset;
3561 : : }
3562 : : else
3563 : : {
3564 : 762161 : dataoffset = 0; /* marker for no null bitmap */
3565 : 762161 : nbytes += ARR_OVERHEAD_NONULLS(ndims);
3566 : : }
5436 3567 : 771048 : result = (ArrayType *) palloc0(nbytes);
6956 3568 : 771048 : SET_VARSIZE(result, nbytes);
8377 3569 : 771048 : result->ndim = ndims;
7423 3570 : 771048 : result->dataoffset = dataoffset;
8602 3571 : 771048 : result->elemtype = elmtype;
8103 neilc@samurai.com 3572 : 771048 : memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
3573 : 771048 : memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
3574 : :
7423 tgl@sss.pgh.pa.us 3575 : 771048 : CopyArrayEls(result,
3576 : : elems, nulls, nelems,
3577 : : elmlen, elmbyval, elmalign,
3578 : : false);
3579 : :
3580 : 771048 : return result;
3581 : : }
3582 : :
3583 : : /*
3584 : : * construct_empty_array --- make a zero-dimensional array of given type
3585 : : */
3586 : : ArrayType *
3587 : 1419001 : construct_empty_array(Oid elmtype)
3588 : : {
3589 : : ArrayType *result;
3590 : :
95 michael@paquier.xyz 3591 :GNC 1419001 : result = palloc0_object(ArrayType);
6956 tgl@sss.pgh.pa.us 3592 :CBC 1419001 : SET_VARSIZE(result, sizeof(ArrayType));
7423 3593 : 1419001 : result->ndim = 0;
3594 : 1419001 : result->dataoffset = 0;
3595 : 1419001 : result->elemtype = elmtype;
9372 3596 : 1419001 : return result;
3597 : : }
3598 : :
3599 : : /*
3600 : : * construct_empty_expanded_array: make an empty expanded array
3601 : : * given only type information. (metacache can be NULL if not needed.)
3602 : : */
3603 : : ExpandedArrayHeader *
3958 3604 : 12 : construct_empty_expanded_array(Oid element_type,
3605 : : MemoryContext parentcontext,
3606 : : ArrayMetaState *metacache)
3607 : : {
3608 : 12 : ArrayType *array = construct_empty_array(element_type);
3609 : : Datum d;
3610 : :
3611 : 12 : d = expand_array(PointerGetDatum(array), parentcontext, metacache);
3612 : 12 : pfree(array);
3613 : 12 : return (ExpandedArrayHeader *) DatumGetEOHP(d);
3614 : : }
3615 : :
3616 : : /*
3617 : : * deconstruct_array --- simple method for extracting data from an array
3618 : : *
3619 : : * array: array object to examine (must not be NULL)
3620 : : * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
3621 : : * elemsp: return value, set to point to palloc'd array of Datum values
3622 : : * nullsp: return value, set to point to palloc'd array of isnull markers
3623 : : * nelemsp: return value, set to number of extracted values
3624 : : *
3625 : : * The caller may pass nullsp == NULL if it does not support NULLs in the
3626 : : * array. Note that this produces a very uninformative error message,
3627 : : * so do it only in cases where a NULL is really not expected.
3628 : : *
3629 : : * If array elements are pass-by-ref data type, the returned Datums will
3630 : : * be pointers into the array object.
3631 : : *
3632 : : * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
3633 : : * from the system catalogs, given the elmtype. However, the caller is
3634 : : * in a better position to cache this info across multiple uses, or even
3635 : : * to hard-wire values if the element type is hard-wired.
3636 : : */
3637 : : void
135 peter@eisentraut.org 3638 :GNC 1699720 : deconstruct_array(const ArrayType *array,
3639 : : Oid elmtype,
3640 : : int elmlen, bool elmbyval, char elmalign,
3641 : : Datum **elemsp, bool **nullsp, int *nelemsp)
3642 : : {
3643 : : Datum *elems;
3644 : : bool *nulls;
3645 : : int nelems;
3646 : : char *p;
3647 : : bits8 *bitmap;
3648 : : int bitmask;
3649 : : int i;
41 tgl@sss.pgh.pa.us 3650 : 1699720 : uint8 elmalignby = typalign_to_alignby(elmalign);
3651 : :
8602 tgl@sss.pgh.pa.us 3652 [ - + ]:CBC 1699720 : Assert(ARR_ELEMTYPE(array) == elmtype);
3653 : :
9367 3654 : 1699720 : nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
95 michael@paquier.xyz 3655 :GNC 1699720 : *elemsp = elems = palloc_array(Datum, nelems);
7423 tgl@sss.pgh.pa.us 3656 [ + + ]:CBC 1699720 : if (nullsp)
95 michael@paquier.xyz 3657 :GNC 943991 : *nullsp = nulls = palloc0_array(bool, nelems);
3658 : : else
7423 tgl@sss.pgh.pa.us 3659 :CBC 755729 : nulls = NULL;
9372 3660 : 1699720 : *nelemsp = nelems;
3661 : :
3662 [ + + ]: 1699720 : p = ARR_DATA_PTR(array);
7423 3663 [ + + ]: 1699720 : bitmap = ARR_NULLBITMAP(array);
3664 : 1699720 : bitmask = 1;
3665 : :
9372 3666 [ + + ]: 30491363 : for (i = 0; i < nelems; i++)
3667 : : {
3668 : : /* Get source element, checking for NULL */
7423 3669 [ + + + + ]: 28791643 : if (bitmap && (*bitmap & bitmask) == 0)
3670 : : {
3671 : 1748 : elems[i] = (Datum) 0;
3672 [ + - ]: 1748 : if (nulls)
3673 : 1748 : nulls[i] = true;
3674 : : else
7423 tgl@sss.pgh.pa.us 3675 [ # # ]:UBC 0 : ereport(ERROR,
3676 : : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
3677 : : errmsg("null array element not allowed in this context")));
3678 : : }
3679 : : else
3680 : : {
7423 tgl@sss.pgh.pa.us 3681 :CBC 28789895 : elems[i] = fetch_att(p, elmbyval, elmlen);
6918 3682 [ + + + + : 28789895 : p = att_addlength_pointer(p, elmlen, p);
- + - - -
- - - - +
- + ]
41 tgl@sss.pgh.pa.us 3683 :GNC 28789895 : p = (char *) att_nominal_alignby(p, elmalignby);
3684 : : }
3685 : :
3686 : : /* advance bitmap pointer if any */
7423 tgl@sss.pgh.pa.us 3687 [ + + ]:CBC 28791643 : if (bitmap)
3688 : : {
3689 : 2751 : bitmask <<= 1;
3690 [ + + ]: 2751 : if (bitmask == 0x100)
3691 : : {
3692 : 45 : bitmap++;
3693 : 45 : bitmask = 1;
3694 : : }
3695 : : }
3696 : : }
9372 3697 : 1699720 : }
3698 : :
3699 : : /*
3700 : : * Like deconstruct_array(), where elmtype must be a built-in type, and
3701 : : * elmlen/elmbyval/elmalign is looked up from hardcoded data. This is often
3702 : : * useful when manipulating arrays from/for system catalogs.
3703 : : */
3704 : : void
135 peter@eisentraut.org 3705 :GNC 280959 : deconstruct_array_builtin(const ArrayType *array,
3706 : : Oid elmtype,
3707 : : Datum **elemsp, bool **nullsp, int *nelemsp)
3708 : : {
3709 : : int elmlen;
3710 : : bool elmbyval;
3711 : : char elmalign;
3712 : :
1353 peter@eisentraut.org 3713 [ + + + + :CBC 280959 : switch (elmtype)
+ + + +
- ]
3714 : : {
3715 : 9 : case CHAROID:
3716 : 9 : elmlen = 1;
3717 : 9 : elmbyval = true;
3718 : 9 : elmalign = TYPALIGN_CHAR;
3719 : 9 : break;
3720 : :
3721 : 6014 : case CSTRINGOID:
3722 : 6014 : elmlen = -2;
3723 : 6014 : elmbyval = false;
3724 : 6014 : elmalign = TYPALIGN_CHAR;
3725 : 6014 : break;
3726 : :
3727 : 15 : case FLOAT8OID:
3728 : 15 : elmlen = sizeof(float8);
214 tgl@sss.pgh.pa.us 3729 :GNC 15 : elmbyval = true;
1353 peter@eisentraut.org 3730 :CBC 15 : elmalign = TYPALIGN_DOUBLE;
3731 : 15 : break;
3732 : :
3733 : 2810 : case INT2OID:
3734 : 2810 : elmlen = sizeof(int16);
3735 : 2810 : elmbyval = true;
3736 : 2810 : elmalign = TYPALIGN_SHORT;
3737 : 2810 : break;
3738 : :
62 tmunro@postgresql.or 3739 : 168 : case INT4OID:
3740 : 168 : elmlen = sizeof(int32);
3741 : 168 : elmbyval = true;
3742 : 168 : elmalign = TYPALIGN_INT;
3743 : 168 : break;
3744 : :
1353 peter@eisentraut.org 3745 : 154 : case OIDOID:
3746 : 154 : elmlen = sizeof(Oid);
3747 : 154 : elmbyval = true;
3748 : 154 : elmalign = TYPALIGN_INT;
3749 : 154 : break;
3750 : :
3751 : 271767 : case TEXTOID:
3752 : 271767 : elmlen = -1;
3753 : 271767 : elmbyval = false;
3754 : 271767 : elmalign = TYPALIGN_INT;
3755 : 271767 : break;
3756 : :
3757 : 22 : case TIDOID:
3758 : 22 : elmlen = sizeof(ItemPointerData);
3759 : 22 : elmbyval = false;
3760 : 22 : elmalign = TYPALIGN_SHORT;
3761 : 22 : break;
3762 : :
1353 peter@eisentraut.org 3763 :UBC 0 : default:
3764 [ # # ]: 0 : elog(ERROR, "type %u not supported by deconstruct_array_builtin()", elmtype);
3765 : : /* keep compiler quiet */
3766 : : elmlen = 0;
3767 : : elmbyval = false;
3768 : : elmalign = 0;
3769 : : }
3770 : :
1353 peter@eisentraut.org 3771 :CBC 280959 : deconstruct_array(array, elmtype, elmlen, elmbyval, elmalign, elemsp, nullsp, nelemsp);
3772 : 280959 : }
3773 : :
3774 : : /*
3775 : : * array_contains_nulls --- detect whether an array has any null elements
3776 : : *
3777 : : * This gives an accurate answer, whereas testing ARR_HASNULL only tells
3778 : : * if the array *might* contain a null.
3779 : : */
3780 : : bool
135 peter@eisentraut.org 3781 :GNC 38779 : array_contains_nulls(const ArrayType *array)
3782 : : {
3783 : : int nelems;
3784 : : bits8 *bitmap;
3785 : : int bitmask;
3786 : :
3787 : : /* Easy answer if there's no null bitmap */
5545 tgl@sss.pgh.pa.us 3788 [ + + ]:CBC 38779 : if (!ARR_HASNULL(array))
3789 : 38748 : return false;
3790 : :
3791 : 31 : nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
3792 : :
3793 [ + - ]: 31 : bitmap = ARR_NULLBITMAP(array);
3794 : :
3795 : : /* check whole bytes of the bitmap byte-at-a-time */
3796 [ + + ]: 31 : while (nelems >= 8)
3797 : : {
3798 [ + - ]: 9 : if (*bitmap != 0xFF)
3799 : 9 : return true;
5545 tgl@sss.pgh.pa.us 3800 :UBC 0 : bitmap++;
3801 : 0 : nelems -= 8;
3802 : : }
3803 : :
3804 : : /* check last partial byte */
5545 tgl@sss.pgh.pa.us 3805 :CBC 22 : bitmask = 1;
3806 [ + - ]: 49 : while (nelems > 0)
3807 : : {
3808 [ + + ]: 49 : if ((*bitmap & bitmask) == 0)
3809 : 22 : return true;
3810 : 27 : bitmask <<= 1;
3811 : 27 : nelems--;
3812 : : }
3813 : :
5545 tgl@sss.pgh.pa.us 3814 :UBC 0 : return false;
3815 : : }
3816 : :
3817 : :
3818 : : /*
3819 : : * array_eq :
3820 : : * compares two arrays for equality
3821 : : * result :
3822 : : * returns true if the arrays are equal, false otherwise.
3823 : : *
3824 : : * Note: we do not use array_cmp here, since equality may be meaningful in
3825 : : * datatypes that don't have a total ordering (and hence no btree support).
3826 : : */
3827 : : Datum
9406 tgl@sss.pgh.pa.us 3828 :CBC 134742 : array_eq(PG_FUNCTION_ARGS)
3829 : : {
2605 andres@anarazel.de 3830 : 134742 : LOCAL_FCINFO(locfcinfo, 2);
3100 tgl@sss.pgh.pa.us 3831 : 134742 : AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
3832 : 134742 : AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
5451 3833 : 134742 : Oid collation = PG_GET_COLLATION();
3958 3834 [ + + ]: 134742 : int ndims1 = AARR_NDIM(array1);
3835 [ + + ]: 134742 : int ndims2 = AARR_NDIM(array2);
3836 [ + + ]: 134742 : int *dims1 = AARR_DIMS(array1);
3837 [ + + ]: 134742 : int *dims2 = AARR_DIMS(array2);
3838 [ + + ]: 134742 : int *lbs1 = AARR_LBOUND(array1);
3839 [ + + ]: 134742 : int *lbs2 = AARR_LBOUND(array2);
3840 [ + + ]: 134742 : Oid element_type = AARR_ELEMTYPE(array1);
9367 3841 : 134742 : bool result = true;
3842 : : int nitems;
3843 : : TypeCacheEntry *typentry;
3844 : : int typlen;
3845 : : bool typbyval;
3846 : : char typalign;
3847 : : array_iter it1;
3848 : : array_iter it2;
3849 : : int i;
3850 : :
3958 3851 [ + + - + ]: 134742 : if (element_type != AARR_ELEMTYPE(array2))
8267 tgl@sss.pgh.pa.us 3852 [ # # ]:UBC 0 : ereport(ERROR,
3853 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
3854 : : errmsg("cannot compare arrays of different element types")));
3855 : :
3856 : : /* fast path if the arrays do not have the same dimensionality */
7421 tgl@sss.pgh.pa.us 3857 [ + + ]:CBC 134742 : if (ndims1 != ndims2 ||
3958 3858 [ + + ]: 127352 : memcmp(dims1, dims2, ndims1 * sizeof(int)) != 0 ||
3859 [ - + ]: 95786 : memcmp(lbs1, lbs2, ndims1 * sizeof(int)) != 0)
9367 3860 : 38956 : result = false;
3861 : : else
3862 : : {
3863 : : /*
3864 : : * We arrange to look up the equality function only once per series of
3865 : : * calls, assuming the element type doesn't change underneath us. The
3866 : : * typcache is used so that we have no memory leakage when being used
3867 : : * as an index support function.
3868 : : */
8246 3869 : 95786 : typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3870 [ + + ]: 95786 : if (typentry == NULL ||
3871 [ - + ]: 94082 : typentry->type_id != element_type)
3872 : : {
3873 : 1704 : typentry = lookup_type_cache(element_type,
3874 : : TYPECACHE_EQ_OPR_FINFO);
3875 [ - + ]: 1704 : if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
8246 tgl@sss.pgh.pa.us 3876 [ # # ]:UBC 0 : ereport(ERROR,
3877 : : (errcode(ERRCODE_UNDEFINED_FUNCTION),
3878 : : errmsg("could not identify an equality operator for type %s",
3879 : : format_type_be(element_type))));
472 peter@eisentraut.org 3880 :CBC 1704 : fcinfo->flinfo->fn_extra = typentry;
3881 : : }
8246 tgl@sss.pgh.pa.us 3882 : 95786 : typlen = typentry->typlen;
3883 : 95786 : typbyval = typentry->typbyval;
3884 : 95786 : typalign = typentry->typalign;
3885 : :
3886 : : /*
3887 : : * apply the operator to each pair of array elements.
3888 : : */
2605 andres@anarazel.de 3889 : 95786 : InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
3890 : : collation, NULL, NULL);
3891 : :
3892 : : /* Loop over source data */
7421 tgl@sss.pgh.pa.us 3893 : 95786 : nitems = ArrayGetNItems(ndims1, dims1);
41 tgl@sss.pgh.pa.us 3894 :GNC 95786 : array_iter_setup(&it1, array1, typlen, typbyval, typalign);
3895 : 95786 : array_iter_setup(&it2, array2, typlen, typbyval, typalign);
3896 : :
7421 tgl@sss.pgh.pa.us 3897 [ + + ]:CBC 224633 : for (i = 0; i < nitems; i++)
3898 : : {
3899 : : Datum elt1;
3900 : : Datum elt2;
3901 : : bool isnull1;
3902 : : bool isnull2;
3903 : : bool oprresult;
3904 : :
3905 : : /* Get elements, checking for NULL */
41 tgl@sss.pgh.pa.us 3906 :GNC 146301 : elt1 = array_iter_next(&it1, &isnull1, i);
3907 : 146301 : elt2 = array_iter_next(&it2, &isnull2, i);
3908 : :
3909 : : /*
3910 : : * We consider two NULLs equal; NULL and not-NULL are unequal.
3911 : : */
7423 tgl@sss.pgh.pa.us 3912 [ + + + - ]:CBC 146301 : if (isnull1 && isnull2)
3913 : 23 : continue;
3914 [ + - + + ]: 146278 : if (isnull1 || isnull2)
3915 : : {
3916 : 54 : result = false;
3917 : 17454 : break;
3918 : : }
3919 : :
3920 : : /*
3921 : : * Apply the operator to the element pair; treat NULL as false
3922 : : */
2605 andres@anarazel.de 3923 : 146224 : locfcinfo->args[0].value = elt1;
3924 : 146224 : locfcinfo->args[0].isnull = false;
3925 : 146224 : locfcinfo->args[1].value = elt2;
3926 : 146224 : locfcinfo->args[1].isnull = false;
3927 : 146224 : locfcinfo->isnull = false;
3928 : 146224 : oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
2154 tgl@sss.pgh.pa.us 3929 [ + - + + ]: 146224 : if (locfcinfo->isnull || !oprresult)
3930 : : {
8297 3931 : 17400 : result = false;
3932 : 17400 : break;
3933 : : }
3934 : : }
3935 : : }
3936 : :
3937 : : /* Avoid leaking memory when handed toasted input. */
3958 3938 [ + + + + ]: 134742 : AARR_FREE_IF_COPY(array1, 0);
3939 [ + + + + ]: 134742 : AARR_FREE_IF_COPY(array2, 1);
3940 : :
9367 3941 : 134742 : PG_RETURN_BOOL(result);
3942 : : }
3943 : :
3944 : :
3945 : : /*-----------------------------------------------------------------------------
3946 : : * array-array bool operators:
3947 : : * Given two arrays, iterate comparison operators
3948 : : * over the array. Uses logic similar to text comparison
3949 : : * functions, except element-by-element instead of
3950 : : * character-by-character.
3951 : : *----------------------------------------------------------------------------
3952 : : */
3953 : :
3954 : : Datum
8297 3955 : 498 : array_ne(PG_FUNCTION_ARGS)
3956 : : {
3957 : 498 : PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo)));
3958 : : }
3959 : :
3960 : : Datum
3961 : 5277 : array_lt(PG_FUNCTION_ARGS)
3962 : : {
3963 : 5277 : PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
3964 : : }
3965 : :
3966 : : Datum
3967 : 9 : array_gt(PG_FUNCTION_ARGS)
3968 : : {
3969 : 9 : PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
3970 : : }
3971 : :
3972 : : Datum
3973 : 9 : array_le(PG_FUNCTION_ARGS)
3974 : : {
3975 : 9 : PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
3976 : : }
3977 : :
3978 : : Datum
3979 : 9 : array_ge(PG_FUNCTION_ARGS)
3980 : : {
3981 : 9 : PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
3982 : : }
3983 : :
3984 : : Datum
3985 : 4365965 : btarraycmp(PG_FUNCTION_ARGS)
3986 : : {
3987 : 4365965 : PG_RETURN_INT32(array_cmp(fcinfo));
3988 : : }
3989 : :
3990 : : /*
3991 : : * array_cmp()
3992 : : * Internal comparison function for arrays.
3993 : : *
3994 : : * Returns -1, 0 or 1
3995 : : */
3996 : : static int
3997 : 4371542 : array_cmp(FunctionCallInfo fcinfo)
3998 : : {
2605 andres@anarazel.de 3999 : 4371542 : LOCAL_FCINFO(locfcinfo, 2);
3100 tgl@sss.pgh.pa.us 4000 : 4371542 : AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
4001 : 4371542 : AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
5514 peter_e@gmx.net 4002 : 4371542 : Oid collation = PG_GET_COLLATION();
3958 tgl@sss.pgh.pa.us 4003 [ - + ]: 4371542 : int ndims1 = AARR_NDIM(array1);
4004 [ - + ]: 4371542 : int ndims2 = AARR_NDIM(array2);
4005 [ - + ]: 4371542 : int *dims1 = AARR_DIMS(array1);
4006 [ - + ]: 4371542 : int *dims2 = AARR_DIMS(array2);
8248 4007 : 4371542 : int nitems1 = ArrayGetNItems(ndims1, dims1);
4008 : 4371542 : int nitems2 = ArrayGetNItems(ndims2, dims2);
3958 4009 [ - + ]: 4371542 : Oid element_type = AARR_ELEMTYPE(array1);
8297 4010 : 4371542 : int result = 0;
4011 : : TypeCacheEntry *typentry;
4012 : : int typlen;
4013 : : bool typbyval;
4014 : : char typalign;
4015 : : int min_nitems;
4016 : : array_iter it1;
4017 : : array_iter it2;
4018 : : int i;
4019 : :
3958 4020 [ - + + + ]: 4371542 : if (element_type != AARR_ELEMTYPE(array2))
8267 4021 [ + - ]: 3 : ereport(ERROR,
4022 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
4023 : : errmsg("cannot compare arrays of different element types")));
4024 : :
4025 : : /*
4026 : : * We arrange to look up the comparison function only once per series of
4027 : : * calls, assuming the element type doesn't change underneath us. The
4028 : : * typcache is used so that we have no memory leakage when being used as
4029 : : * an index support function.
4030 : : */
8246 4031 : 4371539 : typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
4032 [ + + ]: 4371539 : if (typentry == NULL ||
5451 4033 [ - + ]: 4370139 : typentry->type_id != element_type)
4034 : : {
8246 4035 : 1400 : typentry = lookup_type_cache(element_type,
4036 : : TYPECACHE_CMP_PROC_FINFO);
4037 [ + + ]: 1400 : if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
4038 [ + - ]: 3 : ereport(ERROR,
4039 : : (errcode(ERRCODE_UNDEFINED_FUNCTION),
4040 : : errmsg("could not identify a comparison function for type %s",
4041 : : format_type_be(element_type))));
472 peter@eisentraut.org 4042 : 1397 : fcinfo->flinfo->fn_extra = typentry;
4043 : : }
8246 tgl@sss.pgh.pa.us 4044 : 4371536 : typlen = typentry->typlen;
4045 : 4371536 : typbyval = typentry->typbyval;
4046 : 4371536 : typalign = typentry->typalign;
4047 : :
4048 : : /*
4049 : : * apply the operator to each pair of array elements.
4050 : : */
2605 andres@anarazel.de 4051 : 4371536 : InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
4052 : : collation, NULL, NULL);
4053 : :
4054 : : /* Loop over source data */
7421 tgl@sss.pgh.pa.us 4055 : 4371536 : min_nitems = Min(nitems1, nitems2);
41 tgl@sss.pgh.pa.us 4056 :GNC 4371536 : array_iter_setup(&it1, array1, typlen, typbyval, typalign);
4057 : 4371536 : array_iter_setup(&it2, array2, typlen, typbyval, typalign);
4058 : :
8248 tgl@sss.pgh.pa.us 4059 [ + + ]:CBC 9266430 : for (i = 0; i < min_nitems; i++)
4060 : : {
4061 : : Datum elt1;
4062 : : Datum elt2;
4063 : : bool isnull1;
4064 : : bool isnull2;
4065 : : int32 cmpresult;
4066 : :
4067 : : /* Get elements, checking for NULL */
41 tgl@sss.pgh.pa.us 4068 :GNC 7862124 : elt1 = array_iter_next(&it1, &isnull1, i);
4069 : 7862124 : elt2 = array_iter_next(&it2, &isnull2, i);
4070 : :
4071 : : /*
4072 : : * We consider two NULLs equal; NULL > not-NULL.
4073 : : */
7423 tgl@sss.pgh.pa.us 4074 [ + + + + ]:CBC 7862124 : if (isnull1 && isnull2)
4075 : 4894894 : continue;
4076 [ + + ]: 7862113 : if (isnull1)
4077 : : {
4078 : : /* arg1 is greater than arg2 */
4079 : 120 : result = 1;
4080 : 2967230 : break;
4081 : : }
4082 [ + + ]: 7861993 : if (isnull2)
4083 : : {
4084 : : /* arg1 is less than arg2 */
4085 : 198 : result = -1;
4086 : 198 : break;
4087 : : }
4088 : :
4089 : : /* Compare the pair of elements */
2605 andres@anarazel.de 4090 : 7861795 : locfcinfo->args[0].value = elt1;
4091 : 7861795 : locfcinfo->args[0].isnull = false;
4092 : 7861795 : locfcinfo->args[1].value = elt2;
4093 : 7861795 : locfcinfo->args[1].isnull = false;
4094 : 7861795 : cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
4095 : :
4096 : : /* We don't expect comparison support functions to return null */
2154 tgl@sss.pgh.pa.us 4097 [ - + ]: 7861795 : Assert(!locfcinfo->isnull);
4098 : :
8246 4099 [ + + ]: 7861795 : if (cmpresult == 0)
4100 : 4894883 : continue; /* equal */
4101 : :
4102 [ + + ]: 2966912 : if (cmpresult < 0)
4103 : : {
4104 : : /* arg1 is less than arg2 */
8248 4105 : 1684415 : result = -1;
4106 : 1684415 : break;
4107 : : }
4108 : : else
4109 : : {
4110 : : /* arg1 is greater than arg2 */
4111 : 1282497 : result = 1;
4112 : 1282497 : break;
4113 : : }
4114 : : }
4115 : :
4116 : : /*
4117 : : * If arrays contain same data (up to end of shorter one), apply
4118 : : * additional rules to sort by dimensionality. The relative significance
4119 : : * of the different bits of information is historical; mainly we just care
4120 : : * that we don't say "equal" for arrays of different dimensionality.
4121 : : */
7421 4122 [ + + ]: 4371536 : if (result == 0)
4123 : : {
4124 [ + + ]: 1404306 : if (nitems1 != nitems2)
4125 [ + + ]: 112985 : result = (nitems1 < nitems2) ? -1 : 1;
4126 [ - + ]: 1291321 : else if (ndims1 != ndims2)
7421 tgl@sss.pgh.pa.us 4127 [ # # ]:UBC 0 : result = (ndims1 < ndims2) ? -1 : 1;
4128 : : else
4129 : : {
3958 tgl@sss.pgh.pa.us 4130 [ + + ]:CBC 2582616 : for (i = 0; i < ndims1; i++)
4131 : : {
7421 4132 [ - + ]: 1291295 : if (dims1[i] != dims2[i])
4133 : : {
7421 tgl@sss.pgh.pa.us 4134 [ # # ]:UBC 0 : result = (dims1[i] < dims2[i]) ? -1 : 1;
4135 : 0 : break;
4136 : : }
4137 : : }
3958 tgl@sss.pgh.pa.us 4138 [ + - ]:CBC 1291321 : if (result == 0)
4139 : : {
4140 [ - + ]: 1291321 : int *lbound1 = AARR_LBOUND(array1);
4141 [ - + ]: 1291321 : int *lbound2 = AARR_LBOUND(array2);
4142 : :
4143 [ + + ]: 2582616 : for (i = 0; i < ndims1; i++)
4144 : : {
4145 [ - + ]: 1291295 : if (lbound1[i] != lbound2[i])
4146 : : {
3958 tgl@sss.pgh.pa.us 4147 [ # # ]:UBC 0 : result = (lbound1[i] < lbound2[i]) ? -1 : 1;
4148 : 0 : break;
4149 : : }
4150 : : }
4151 : : }
4152 : : }
4153 : : }
4154 : :
4155 : : /* Avoid leaking memory when handed toasted input. */
3958 tgl@sss.pgh.pa.us 4156 [ + - + + ]:CBC 4371536 : AARR_FREE_IF_COPY(array1, 0);
4157 [ + - + + ]: 4371536 : AARR_FREE_IF_COPY(array2, 1);
4158 : :
8297 4159 : 4371536 : return result;
4160 : : }
4161 : :
4162 : :
4163 : : /*-----------------------------------------------------------------------------
4164 : : * array hashing
4165 : : * Hash the elements and combine the results.
4166 : : *----------------------------------------------------------------------------
4167 : : */
4168 : :
4169 : : Datum
5615 4170 : 33688 : hash_array(PG_FUNCTION_ARGS)
4171 : : {
2605 andres@anarazel.de 4172 : 33688 : LOCAL_FCINFO(locfcinfo, 1);
3100 tgl@sss.pgh.pa.us 4173 : 33688 : AnyArrayType *array = PG_GETARG_ANY_ARRAY_P(0);
3958 4174 [ + + ]: 33688 : int ndims = AARR_NDIM(array);
4175 [ + + ]: 33688 : int *dims = AARR_DIMS(array);
4176 [ + + ]: 33688 : Oid element_type = AARR_ELEMTYPE(array);
5410 rhaas@postgresql.org 4177 : 33688 : uint32 result = 1;
4178 : : int nitems;
4179 : : TypeCacheEntry *typentry;
4180 : : int typlen;
4181 : : bool typbyval;
4182 : : char typalign;
4183 : : int i;
4184 : : array_iter iter;
4185 : :
4186 : : /*
4187 : : * We arrange to look up the hash function only once per series of calls,
4188 : : * assuming the element type doesn't change underneath us. The typcache
4189 : : * is used so that we have no memory leakage when being used as an index
4190 : : * support function.
4191 : : */
5615 tgl@sss.pgh.pa.us 4192 : 33688 : typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
4193 [ + + ]: 33688 : if (typentry == NULL ||
4194 [ - + ]: 33300 : typentry->type_id != element_type)
4195 : : {
4196 : 388 : typentry = lookup_type_cache(element_type,
4197 : : TYPECACHE_HASH_PROC_FINFO);
1649 peter@eisentraut.org 4198 [ + + + + ]: 388 : if (!OidIsValid(typentry->hash_proc_finfo.fn_oid) && element_type != RECORDOID)
5615 tgl@sss.pgh.pa.us 4199 [ + - ]: 3 : ereport(ERROR,
4200 : : (errcode(ERRCODE_UNDEFINED_FUNCTION),
4201 : : errmsg("could not identify a hash function for type %s",
4202 : : format_type_be(element_type))));
4203 : :
4204 : : /*
4205 : : * The type cache doesn't believe that record is hashable (see
4206 : : * cache_record_field_properties()), but since we're here, we're
4207 : : * committed to hashing, so we can assume it does. Worst case, if any
4208 : : * components of the record don't support hashing, we will fail at
4209 : : * execution.
4210 : : */
1649 peter@eisentraut.org 4211 [ + + ]: 385 : if (element_type == RECORDOID)
4212 : : {
4213 : : MemoryContext oldcontext;
4214 : : TypeCacheEntry *record_typentry;
4215 : :
1642 4216 : 9 : oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
4217 : :
4218 : : /*
4219 : : * Make fake type cache entry structure. Note that we can't just
4220 : : * modify typentry, since that points directly into the type
4221 : : * cache.
4222 : : */
95 michael@paquier.xyz 4223 :GNC 9 : record_typentry = palloc0_object(TypeCacheEntry);
1642 peter@eisentraut.org 4224 :CBC 9 : record_typentry->type_id = element_type;
4225 : :
4226 : : /* fill in what we need below */
1649 4227 : 9 : record_typentry->typlen = typentry->typlen;
4228 : 9 : record_typentry->typbyval = typentry->typbyval;
4229 : 9 : record_typentry->typalign = typentry->typalign;
4230 : 9 : fmgr_info(F_HASH_RECORD, &record_typentry->hash_proc_finfo);
4231 : :
4232 : 9 : MemoryContextSwitchTo(oldcontext);
4233 : :
4234 : 9 : typentry = record_typentry;
4235 : : }
4236 : :
472 4237 : 385 : fcinfo->flinfo->fn_extra = typentry;
4238 : : }
4239 : :
5615 tgl@sss.pgh.pa.us 4240 : 33685 : typlen = typentry->typlen;
4241 : 33685 : typbyval = typentry->typbyval;
4242 : 33685 : typalign = typentry->typalign;
4243 : :
4244 : : /*
4245 : : * apply the hash function to each array element.
4246 : : */
2605 andres@anarazel.de 4247 : 33685 : InitFunctionCallInfoData(*locfcinfo, &typentry->hash_proc_finfo, 1,
4248 : : PG_GET_COLLATION(), NULL, NULL);
4249 : :
4250 : : /* Loop over source data */
5615 tgl@sss.pgh.pa.us 4251 : 33685 : nitems = ArrayGetNItems(ndims, dims);
41 tgl@sss.pgh.pa.us 4252 :GNC 33685 : array_iter_setup(&iter, array, typlen, typbyval, typalign);
4253 : :
5615 tgl@sss.pgh.pa.us 4254 [ + + ]:CBC 87040 : for (i = 0; i < nitems; i++)
4255 : : {
4256 : : Datum elt;
4257 : : bool isnull;
4258 : : uint32 elthash;
4259 : :
4260 : : /* Get element, checking for NULL */
41 tgl@sss.pgh.pa.us 4261 :GNC 53355 : elt = array_iter_next(&iter, &isnull, i);
4262 : :
3958 tgl@sss.pgh.pa.us 4263 [ + + ]:CBC 53355 : if (isnull)
4264 : : {
4265 : : /* Treat nulls as having hashvalue 0 */
5615 tgl@sss.pgh.pa.us 4266 :GBC 24 : elthash = 0;
4267 : : }
4268 : : else
4269 : : {
4270 : : /* Apply the hash function */
2605 andres@anarazel.de 4271 :CBC 53331 : locfcinfo->args[0].value = elt;
4272 : 53331 : locfcinfo->args[0].isnull = false;
4273 : 53331 : elthash = DatumGetUInt32(FunctionCallInvoke(locfcinfo));
4274 : : /* We don't expect hash functions to return null */
2154 tgl@sss.pgh.pa.us 4275 [ - + ]: 53331 : Assert(!locfcinfo->isnull);
4276 : : }
4277 : :
4278 : : /*
4279 : : * Combine hash values of successive elements by multiplying the
4280 : : * current value by 31 and adding on the new element's hash value.
4281 : : *
4282 : : * The result is a sum in which each element's hash value is
4283 : : * multiplied by a different power of 31. This is modulo 2^32
4284 : : * arithmetic, and the powers of 31 modulo 2^32 form a cyclic group of
4285 : : * order 2^27. So for arrays of up to 2^27 elements, each element's
4286 : : * hash value is multiplied by a different (odd) number, resulting in
4287 : : * a good mixing of all the elements' hash values.
4288 : : */
5410 rhaas@postgresql.org 4289 : 53355 : result = (result << 5) - result + elthash;
4290 : : }
4291 : :
4292 : : /* Avoid leaking memory when handed toasted input. */
3958 tgl@sss.pgh.pa.us 4293 [ + + + + ]: 33685 : AARR_FREE_IF_COPY(array, 0);
4294 : :
5615 4295 : 33685 : PG_RETURN_UINT32(result);
4296 : : }
4297 : :
4298 : : /*
4299 : : * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
4300 : : * Otherwise, similar to hash_array.
4301 : : */
4302 : : Datum
3118 rhaas@postgresql.org 4303 : 72 : hash_array_extended(PG_FUNCTION_ARGS)
4304 : : {
2605 andres@anarazel.de 4305 : 72 : LOCAL_FCINFO(locfcinfo, 2);
3100 tgl@sss.pgh.pa.us 4306 : 72 : AnyArrayType *array = PG_GETARG_ANY_ARRAY_P(0);
3118 rhaas@postgresql.org 4307 : 72 : uint64 seed = PG_GETARG_INT64(1);
4308 [ - + ]: 72 : int ndims = AARR_NDIM(array);
4309 [ - + ]: 72 : int *dims = AARR_DIMS(array);
4310 [ - + ]: 72 : Oid element_type = AARR_ELEMTYPE(array);
4311 : 72 : uint64 result = 1;
4312 : : int nitems;
4313 : : TypeCacheEntry *typentry;
4314 : : int typlen;
4315 : : bool typbyval;
4316 : : char typalign;
4317 : : int i;
4318 : : array_iter iter;
4319 : :
4320 : 72 : typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
4321 [ + + ]: 72 : if (typentry == NULL ||
4322 [ - + ]: 42 : typentry->type_id != element_type)
4323 : : {
4324 : 30 : typentry = lookup_type_cache(element_type,
4325 : : TYPECACHE_HASH_EXTENDED_PROC_FINFO);
4326 [ + + ]: 30 : if (!OidIsValid(typentry->hash_extended_proc_finfo.fn_oid))
4327 [ + - ]: 3 : ereport(ERROR,
4328 : : (errcode(ERRCODE_UNDEFINED_FUNCTION),
4329 : : errmsg("could not identify an extended hash function for type %s",
4330 : : format_type_be(element_type))));
472 peter@eisentraut.org 4331 : 27 : fcinfo->flinfo->fn_extra = typentry;
4332 : : }
3118 rhaas@postgresql.org 4333 : 69 : typlen = typentry->typlen;
4334 : 69 : typbyval = typentry->typbyval;
4335 : 69 : typalign = typentry->typalign;
4336 : :
2605 andres@anarazel.de 4337 : 69 : InitFunctionCallInfoData(*locfcinfo, &typentry->hash_extended_proc_finfo, 2,
4338 : : PG_GET_COLLATION(), NULL, NULL);
4339 : :
4340 : : /* Loop over source data */
3118 rhaas@postgresql.org 4341 : 69 : nitems = ArrayGetNItems(ndims, dims);
41 tgl@sss.pgh.pa.us 4342 :GNC 69 : array_iter_setup(&iter, array, typlen, typbyval, typalign);
4343 : :
3118 rhaas@postgresql.org 4344 [ + + ]:CBC 228 : for (i = 0; i < nitems; i++)
4345 : : {
4346 : : Datum elt;
4347 : : bool isnull;
4348 : : uint64 elthash;
4349 : :
4350 : : /* Get element, checking for NULL */
41 tgl@sss.pgh.pa.us 4351 :GNC 159 : elt = array_iter_next(&iter, &isnull, i);
4352 : :
3118 rhaas@postgresql.org 4353 [ - + ]:CBC 159 : if (isnull)
4354 : : {
3118 rhaas@postgresql.org 4355 :UBC 0 : elthash = 0;
4356 : : }
4357 : : else
4358 : : {
4359 : : /* Apply the hash function */
2605 andres@anarazel.de 4360 :CBC 159 : locfcinfo->args[0].value = elt;
4361 : 159 : locfcinfo->args[0].isnull = false;
4362 : 159 : locfcinfo->args[1].value = Int64GetDatum(seed);
4363 : 159 : locfcinfo->args[1].isnull = false;
4364 : 159 : elthash = DatumGetUInt64(FunctionCallInvoke(locfcinfo));
4365 : : /* We don't expect hash functions to return null */
2154 tgl@sss.pgh.pa.us 4366 [ - + ]: 159 : Assert(!locfcinfo->isnull);
4367 : : }
4368 : :
3118 rhaas@postgresql.org 4369 : 159 : result = (result << 5) - result + elthash;
4370 : : }
4371 : :
4372 [ + - - + ]: 69 : AARR_FREE_IF_COPY(array, 0);
4373 : :
4374 : 69 : PG_RETURN_UINT64(result);
4375 : : }
4376 : :
4377 : :
4378 : : /*-----------------------------------------------------------------------------
4379 : : * array overlap/containment comparisons
4380 : : * These use the same methods of comparing array elements as array_eq.
4381 : : * We consider only the elements of the arrays, ignoring dimensionality.
4382 : : *----------------------------------------------------------------------------
4383 : : */
4384 : :
4385 : : /*
4386 : : * array_contain_compare :
4387 : : * compares two arrays for overlap/containment
4388 : : *
4389 : : * When matchall is true, return true if all members of array1 are in array2.
4390 : : * When matchall is false, return true if any members of array1 are in array2.
4391 : : */
4392 : : static bool
3958 tgl@sss.pgh.pa.us 4393 : 11229 : array_contain_compare(AnyArrayType *array1, AnyArrayType *array2, Oid collation,
4394 : : bool matchall, void **fn_extra)
4395 : : {
2605 andres@anarazel.de 4396 : 11229 : LOCAL_FCINFO(locfcinfo, 2);
7126 tgl@sss.pgh.pa.us 4397 : 11229 : bool result = matchall;
3958 4398 [ + + ]: 11229 : Oid element_type = AARR_ELEMTYPE(array1);
4399 : : TypeCacheEntry *typentry;
4400 : : int nelems1;
4401 : : Datum *values2;
4402 : : bool *nulls2;
4403 : : int nelems2;
4404 : : int typlen;
4405 : : bool typbyval;
4406 : : char typalign;
4407 : : int i;
4408 : : int j;
4409 : : array_iter it1;
4410 : :
4411 [ + + - + ]: 11229 : if (element_type != AARR_ELEMTYPE(array2))
7126 tgl@sss.pgh.pa.us 4412 [ # # ]:UBC 0 : ereport(ERROR,
4413 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
4414 : : errmsg("cannot compare arrays of different element types")));
4415 : :
4416 : : /*
4417 : : * We arrange to look up the equality function only once per series of
4418 : : * calls, assuming the element type doesn't change underneath us. The
4419 : : * typcache is used so that we have no memory leakage when being used as
4420 : : * an index support function.
4421 : : */
7126 tgl@sss.pgh.pa.us 4422 :CBC 11229 : typentry = (TypeCacheEntry *) *fn_extra;
4423 [ + + ]: 11229 : if (typentry == NULL ||
4424 [ - + ]: 10961 : typentry->type_id != element_type)
4425 : : {
4426 : 268 : typentry = lookup_type_cache(element_type,
4427 : : TYPECACHE_EQ_OPR_FINFO);
4428 [ - + ]: 268 : if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
7126 tgl@sss.pgh.pa.us 4429 [ # # ]:UBC 0 : ereport(ERROR,
4430 : : (errcode(ERRCODE_UNDEFINED_FUNCTION),
4431 : : errmsg("could not identify an equality operator for type %s",
4432 : : format_type_be(element_type))));
472 peter@eisentraut.org 4433 :CBC 268 : *fn_extra = typentry;
4434 : : }
7126 tgl@sss.pgh.pa.us 4435 : 11229 : typlen = typentry->typlen;
4436 : 11229 : typbyval = typentry->typbyval;
4437 : 11229 : typalign = typentry->typalign;
4438 : :
4439 : : /*
4440 : : * Since we probably will need to scan array2 multiple times, it's
4441 : : * worthwhile to use deconstruct_array on it. We scan array1 the hard way
4442 : : * however, since we very likely won't need to look at all of it.
4443 : : */
3958 4444 [ + + ]: 11229 : if (VARATT_IS_EXPANDED_HEADER(array2))
4445 : : {
4446 : : /* This should be safe even if input is read-only */
4447 : 2556 : deconstruct_expanded_array(&(array2->xpn));
4448 : 2556 : values2 = array2->xpn.dvalues;
4449 : 2556 : nulls2 = array2->xpn.dnulls;
4450 : 2556 : nelems2 = array2->xpn.nelems;
4451 : : }
4452 : : else
2450 noah@leadboat.com 4453 : 8673 : deconstruct_array((ArrayType *) array2,
4454 : : element_type, typlen, typbyval, typalign,
4455 : : &values2, &nulls2, &nelems2);
4456 : :
4457 : : /*
4458 : : * Apply the comparison operator to each pair of array elements.
4459 : : */
2605 andres@anarazel.de 4460 : 11229 : InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
4461 : : collation, NULL, NULL);
4462 : :
4463 : : /* Loop over source data */
3958 tgl@sss.pgh.pa.us 4464 [ + + + + ]: 11229 : nelems1 = ArrayGetNItems(AARR_NDIM(array1), AARR_DIMS(array1));
41 tgl@sss.pgh.pa.us 4465 :GNC 11229 : array_iter_setup(&it1, array1, typlen, typbyval, typalign);
4466 : :
7126 tgl@sss.pgh.pa.us 4467 [ + + ]:CBC 210167 : for (i = 0; i < nelems1; i++)
4468 : : {
4469 : : Datum elt1;
4470 : : bool isnull1;
4471 : :
4472 : : /* Get element, checking for NULL */
41 tgl@sss.pgh.pa.us 4473 :GNC 203661 : elt1 = array_iter_next(&it1, &isnull1, i);
4474 : :
4475 : : /*
4476 : : * We assume that the comparison operator is strict, so a NULL can't
4477 : : * match anything. XXX this diverges from the "NULL=NULL" behavior of
4478 : : * array_eq, should we act like that?
4479 : : */
7126 tgl@sss.pgh.pa.us 4480 [ + + ]:CBC 203661 : if (isnull1)
4481 : : {
4482 [ + + ]: 660 : if (matchall)
4483 : : {
4484 : 630 : result = false;
4485 : 4723 : break;
4486 : : }
4487 : 30 : continue;
4488 : : }
4489 : :
4490 [ + + ]: 9208189 : for (j = 0; j < nelems2; j++)
4491 : : {
4492 : 9188487 : Datum elt2 = values2[j];
3950 4493 [ + + + + ]: 9188487 : bool isnull2 = nulls2 ? nulls2[j] : false;
4494 : : bool oprresult;
4495 : :
7126 4496 [ + + ]: 9188487 : if (isnull2)
4497 : 3606 : continue; /* can't match */
4498 : :
4499 : : /*
4500 : : * Apply the operator to the element pair; treat NULL as false
4501 : : */
2605 andres@anarazel.de 4502 : 9184881 : locfcinfo->args[0].value = elt1;
4503 : 9184881 : locfcinfo->args[0].isnull = false;
4504 : 9184881 : locfcinfo->args[1].value = elt2;
4505 : 9184881 : locfcinfo->args[1].isnull = false;
4506 : 9184881 : locfcinfo->isnull = false;
4507 : 9184881 : oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
2154 tgl@sss.pgh.pa.us 4508 [ + - + + ]: 9184881 : if (!locfcinfo->isnull && oprresult)
7126 4509 : 183299 : break;
4510 : : }
4511 : :
4512 [ + + ]: 203001 : if (j < nelems2)
4513 : : {
4514 : : /* found a match for elt1 */
4515 [ + + ]: 183299 : if (!matchall)
4516 : : {
4517 : 114 : result = true;
4518 : 114 : break;
4519 : : }
4520 : : }
4521 : : else
4522 : : {
4523 : : /* no match for elt1 */
4524 [ + + ]: 19702 : if (matchall)
4525 : : {
4526 : 3979 : result = false;
4527 : 3979 : break;
4528 : : }
4529 : : }
4530 : : }
4531 : :
4532 : 11229 : return result;
4533 : : }
4534 : :
4535 : : Datum
4536 : 3060 : arrayoverlap(PG_FUNCTION_ARGS)
4537 : : {
3100 4538 : 3060 : AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
4539 : 3060 : AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
5451 4540 : 3060 : Oid collation = PG_GET_COLLATION();
4541 : : bool result;
4542 : :
4543 : 3060 : result = array_contain_compare(array1, array2, collation, false,
7126 4544 : 3060 : &fcinfo->flinfo->fn_extra);
4545 : :
4546 : : /* Avoid leaking memory when handed toasted input. */
3958 4547 [ + - + + ]: 3060 : AARR_FREE_IF_COPY(array1, 0);
4548 [ + - - + ]: 3060 : AARR_FREE_IF_COPY(array2, 1);
4549 : :
7126 4550 : 3060 : PG_RETURN_BOOL(result);
4551 : : }
4552 : :
4553 : : Datum
4554 : 4911 : arraycontains(PG_FUNCTION_ARGS)
4555 : : {
3100 4556 : 4911 : AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
4557 : 4911 : AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
5451 4558 : 4911 : Oid collation = PG_GET_COLLATION();
4559 : : bool result;
4560 : :
4561 : 4911 : result = array_contain_compare(array2, array1, collation, true,
7126 4562 : 4911 : &fcinfo->flinfo->fn_extra);
4563 : :
4564 : : /* Avoid leaking memory when handed toasted input. */
3958 4565 [ + + + + ]: 4911 : AARR_FREE_IF_COPY(array1, 0);
4566 [ + + - + ]: 4911 : AARR_FREE_IF_COPY(array2, 1);
4567 : :
7126 4568 : 4911 : PG_RETURN_BOOL(result);
4569 : : }
4570 : :
4571 : : Datum
4572 : 3258 : arraycontained(PG_FUNCTION_ARGS)
4573 : : {
3100 4574 : 3258 : AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
4575 : 3258 : AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
5451 4576 : 3258 : Oid collation = PG_GET_COLLATION();
4577 : : bool result;
4578 : :
4579 : 3258 : result = array_contain_compare(array1, array2, collation, true,
7126 4580 : 3258 : &fcinfo->flinfo->fn_extra);
4581 : :
4582 : : /* Avoid leaking memory when handed toasted input. */
3958 4583 [ + + + + ]: 3258 : AARR_FREE_IF_COPY(array1, 0);
4584 [ + + - + ]: 3258 : AARR_FREE_IF_COPY(array2, 1);
4585 : :
7126 4586 : 3258 : PG_RETURN_BOOL(result);
4587 : : }
4588 : :
4589 : :
4590 : : /*-----------------------------------------------------------------------------
4591 : : * Array iteration functions
4592 : : * These functions are used to iterate efficiently through arrays
4593 : : *-----------------------------------------------------------------------------
4594 : : */
4595 : :
4596 : : /*
4597 : : * array_create_iterator --- set up to iterate through an array
4598 : : *
4599 : : * If slice_ndim is zero, we will iterate element-by-element; the returned
4600 : : * datums are of the array's element type.
4601 : : *
4602 : : * If slice_ndim is 1..ARR_NDIM(arr), we will iterate by slices: the
4603 : : * returned datums are of the same array type as 'arr', but of size
4604 : : * equal to the rightmost N dimensions of 'arr'.
4605 : : *
4606 : : * The passed-in array must remain valid for the lifetime of the iterator.
4607 : : */
4608 : : ArrayIterator
4015 alvherre@alvh.no-ip. 4609 : 289 : array_create_iterator(ArrayType *arr, int slice_ndim, ArrayMetaState *mstate)
4610 : : {
95 michael@paquier.xyz 4611 :GNC 289 : ArrayIterator iterator = palloc0_object(ArrayIteratorData);
4612 : :
4613 : : /*
4614 : : * Sanity-check inputs --- caller should have got this right already
4615 : : */
172 peter@eisentraut.org 4616 [ - + ]: 289 : Assert(arr);
5506 tgl@sss.pgh.pa.us 4617 [ + - - + ]:CBC 289 : if (slice_ndim < 0 || slice_ndim > ARR_NDIM(arr))
5506 tgl@sss.pgh.pa.us 4618 [ # # ]:UBC 0 : elog(ERROR, "invalid arguments to array_create_iterator");
4619 : :
4620 : : /*
4621 : : * Remember basic info about the array and its element type
4622 : : */
5506 tgl@sss.pgh.pa.us 4623 :CBC 289 : iterator->arr = arr;
4624 [ + + ]: 289 : iterator->nullbitmap = ARR_NULLBITMAP(arr);
4625 : 289 : iterator->nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
4626 : :
4015 alvherre@alvh.no-ip. 4627 [ + + ]: 289 : if (mstate != NULL)
4628 : : {
4629 [ - + ]: 192 : Assert(mstate->element_type == ARR_ELEMTYPE(arr));
4630 : :
4631 : 192 : iterator->typlen = mstate->typlen;
4632 : 192 : iterator->typbyval = mstate->typbyval;
4633 : 192 : iterator->typalign = mstate->typalign;
4634 : : }
4635 : : else
4636 : 97 : get_typlenbyvalalign(ARR_ELEMTYPE(arr),
4637 : : &iterator->typlen,
4638 : : &iterator->typbyval,
4639 : : &iterator->typalign);
41 tgl@sss.pgh.pa.us 4640 :GNC 289 : iterator->typalignby = typalign_to_alignby(iterator->typalign);
4641 : :
4642 : : /*
4643 : : * Remember the slicing parameters.
4644 : : */
5506 tgl@sss.pgh.pa.us 4645 :CBC 289 : iterator->slice_ndim = slice_ndim;
4646 : :
4647 [ + + ]: 289 : if (slice_ndim > 0)
4648 : : {
4649 : : /*
4650 : : * Get pointers into the array's dims and lbound arrays to represent
4651 : : * the dims/lbound arrays of a slice. These are the same as the
4652 : : * rightmost N dimensions of the array.
4653 : : */
4654 : 39 : iterator->slice_dims = ARR_DIMS(arr) + ARR_NDIM(arr) - slice_ndim;
4655 : 39 : iterator->slice_lbound = ARR_LBOUND(arr) + ARR_NDIM(arr) - slice_ndim;
4656 : :
4657 : : /*
4658 : : * Compute number of elements in a slice.
4659 : : */
4660 : 78 : iterator->slice_len = ArrayGetNItems(slice_ndim,
4661 : 39 : iterator->slice_dims);
4662 : :
4663 : : /*
4664 : : * Create workspace for building sub-arrays.
4665 : : */
4666 : 39 : iterator->slice_values = (Datum *)
4667 : 39 : palloc(iterator->slice_len * sizeof(Datum));
4668 : 39 : iterator->slice_nulls = (bool *)
4669 : 39 : palloc(iterator->slice_len * sizeof(bool));
4670 : : }
4671 : :
4672 : : /*
4673 : : * Initialize our data pointer and linear element number. These will
4674 : : * advance through the array during array_iterate().
4675 : : */
4676 [ + + ]: 289 : iterator->data_ptr = ARR_DATA_PTR(arr);
4677 : 289 : iterator->current_item = 0;
4678 : :
4679 : 289 : return iterator;
4680 : : }
4681 : :
4682 : : /*
4683 : : * Iterate through the array referenced by 'iterator'.
4684 : : *
4685 : : * As long as there is another element (or slice), return it into
4686 : : * *value / *isnull, and return true. Return false when no more data.
4687 : : */
4688 : : bool
4689 : 4712 : array_iterate(ArrayIterator iterator, Datum *value, bool *isnull)
4690 : : {
4691 : : /* Done if we have reached the end of the array */
4692 [ + + ]: 4712 : if (iterator->current_item >= iterator->nitems)
4693 : 199 : return false;
4694 : :
4695 [ + + ]: 4513 : if (iterator->slice_ndim == 0)
4696 : : {
4697 : : /*
4698 : : * Scalar case: return one element.
4699 : : */
4700 [ + + ]: 4435 : if (array_get_isnull(iterator->nullbitmap, iterator->current_item++))
4701 : : {
4702 : 33 : *isnull = true;
4703 : 33 : *value = (Datum) 0;
4704 : : }
4705 : : else
4706 : : {
4707 : : /* non-NULL, so fetch the individual Datum to return */
4708 : 4402 : char *p = iterator->data_ptr;
4709 : :
4710 : 4402 : *isnull = false;
4711 : 4402 : *value = fetch_att(p, iterator->typbyval, iterator->typlen);
4712 : :
4713 : : /* Move our data pointer forward to the next element */
4714 [ + + + - : 4402 : p = att_addlength_pointer(p, iterator->typlen, p);
- - - - -
- - - - +
- - ]
41 tgl@sss.pgh.pa.us 4715 :GNC 4402 : p = (char *) att_nominal_alignby(p, iterator->typalignby);
5506 tgl@sss.pgh.pa.us 4716 :CBC 4402 : iterator->data_ptr = p;
4717 : : }
4718 : : }
4719 : : else
4720 : : {
4721 : : /*
4722 : : * Slice case: build and return an array of the requested size.
4723 : : */
4724 : : ArrayType *result;
4725 : 78 : Datum *values = iterator->slice_values;
4726 : 78 : bool *nulls = iterator->slice_nulls;
4727 : 78 : char *p = iterator->data_ptr;
4728 : : int i;
4729 : :
4730 [ + + ]: 249 : for (i = 0; i < iterator->slice_len; i++)
4731 : : {
4732 [ - + ]: 171 : if (array_get_isnull(iterator->nullbitmap,
4733 : 171 : iterator->current_item++))
4734 : : {
5506 tgl@sss.pgh.pa.us 4735 :UBC 0 : nulls[i] = true;
4736 : 0 : values[i] = (Datum) 0;
4737 : : }
4738 : : else
4739 : : {
5506 tgl@sss.pgh.pa.us 4740 :CBC 171 : nulls[i] = false;
4741 : 171 : values[i] = fetch_att(p, iterator->typbyval, iterator->typlen);
4742 : :
4743 : : /* Move our data pointer forward to the next element */
4744 [ + + + - : 171 : p = att_addlength_pointer(p, iterator->typlen, p);
- - - - -
- - - - +
- - ]
41 tgl@sss.pgh.pa.us 4745 :GNC 171 : p = (char *) att_nominal_alignby(p, iterator->typalignby);
4746 : : }
4747 : : }
4748 : :
5506 tgl@sss.pgh.pa.us 4749 :CBC 78 : iterator->data_ptr = p;
4750 : :
4751 : 78 : result = construct_md_array(values,
4752 : : nulls,
4753 : : iterator->slice_ndim,
4754 : : iterator->slice_dims,
4755 : : iterator->slice_lbound,
4756 : 78 : ARR_ELEMTYPE(iterator->arr),
4757 : 78 : iterator->typlen,
4758 : 78 : iterator->typbyval,
4759 : 78 : iterator->typalign);
4760 : :
4761 : 78 : *isnull = false;
4762 : 78 : *value = PointerGetDatum(result);
4763 : : }
4764 : :
4765 : 4513 : return true;
4766 : : }
4767 : :
4768 : : /*
4769 : : * Release an ArrayIterator data structure
4770 : : */
4771 : : void
4772 : 192 : array_free_iterator(ArrayIterator iterator)
4773 : : {
4774 [ + + ]: 192 : if (iterator->slice_ndim > 0)
4775 : : {
4776 : 21 : pfree(iterator->slice_values);
4777 : 21 : pfree(iterator->slice_nulls);
4778 : : }
4779 : 192 : pfree(iterator);
4780 : 192 : }
4781 : :
4782 : :
4783 : : /***************************************************************************/
4784 : : /******************| Support Routines |*****************/
4785 : : /***************************************************************************/
4786 : :
4787 : : /*
4788 : : * Check whether a specific array element is NULL
4789 : : *
4790 : : * nullbitmap: pointer to array's null bitmap (NULL if none)
4791 : : * offset: 0-based linear element number of array element
4792 : : */
4793 : : static bool
7423 4794 : 454660 : array_get_isnull(const bits8 *nullbitmap, int offset)
4795 : : {
4796 [ + + ]: 454660 : if (nullbitmap == NULL)
4797 : 454244 : return false; /* assume not null */
4798 [ + + ]: 416 : if (nullbitmap[offset / 8] & (1 << (offset % 8)))
4799 : 329 : return false; /* not null */
4800 : 87 : return true;
4801 : : }
4802 : :
4803 : : /*
4804 : : * Set a specific array element's null-bitmap entry
4805 : : *
4806 : : * nullbitmap: pointer to array's null bitmap (mustn't be NULL)
4807 : : * offset: 0-based linear element number of array element
4808 : : * isNull: null status to set
4809 : : */
4810 : : static void
4811 : 67 : array_set_isnull(bits8 *nullbitmap, int offset, bool isNull)
4812 : : {
4813 : : int bitmask;
4814 : :
4815 : 67 : nullbitmap += offset / 8;
4816 : 67 : bitmask = 1 << (offset % 8);
4817 [ + + ]: 67 : if (isNull)
4818 : 10 : *nullbitmap &= ~bitmask;
4819 : : else
4820 : 57 : *nullbitmap |= bitmask;
4821 : 67 : }
4822 : :
4823 : : /*
4824 : : * Fetch array element at pointer, converted correctly to a Datum
4825 : : *
4826 : : * Caller must have handled case of NULL element
4827 : : */
4828 : : static Datum
9367 4829 : 449714 : ArrayCast(char *value, bool byval, int len)
4830 : : {
9209 4831 : 449714 : return fetch_att(value, byval, len);
4832 : : }
4833 : :
4834 : : /*
4835 : : * Copy datum to *dest and return total space used (including align padding)
4836 : : *
4837 : : * Caller must have handled case of NULL element
4838 : : */
4839 : : static int
9406 4840 : 7287739 : ArrayCastAndSet(Datum src,
4841 : : int typlen,
4842 : : bool typbyval,
4843 : : uint8 typalignby,
4844 : : char *dest)
4845 : : {
4846 : : int inc;
4847 : :
10416 bruce@momjian.us 4848 [ + + ]: 7287739 : if (typlen > 0)
4849 : : {
4850 [ + + ]: 5554664 : if (typbyval)
9209 tgl@sss.pgh.pa.us 4851 : 3578767 : store_att_byval(dest, src, typlen);
4852 : : else
9406 4853 : 1975897 : memmove(dest, DatumGetPointer(src), typlen);
41 tgl@sss.pgh.pa.us 4854 :GNC 5554664 : inc = att_nominal_alignby(typlen, typalignby);
4855 : : }
4856 : : else
4857 : : {
8602 tgl@sss.pgh.pa.us 4858 [ - + ]:CBC 1733075 : Assert(!typbyval);
6918 4859 [ + - + + : 1733075 : inc = att_addlength_datum(0, typlen, src);
- + - - -
- - - - +
- + ]
8602 4860 : 1733075 : memmove(dest, DatumGetPointer(src), inc);
41 tgl@sss.pgh.pa.us 4861 :GNC 1733075 : inc = att_nominal_alignby(inc, typalignby);
4862 : : }
4863 : :
9367 tgl@sss.pgh.pa.us 4864 :CBC 7287739 : return inc;
4865 : : }
4866 : :
4867 : : /*
4868 : : * Advance ptr over nitems array elements
4869 : : *
4870 : : * ptr: starting location in array
4871 : : * offset: 0-based linear element number of first element (the one at *ptr)
4872 : : * nullbitmap: start of array's null bitmap, or NULL if none
4873 : : * nitems: number of array elements to advance over (>= 0)
4874 : : * typlen, typbyval, typalign: storage parameters of array element datatype
4875 : : *
4876 : : * It is caller's responsibility to ensure that nitems is within range
4877 : : */
4878 : : static char *
7423 4879 : 450939 : array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
4880 : : int typlen, bool typbyval, char typalign)
4881 : : {
41 tgl@sss.pgh.pa.us 4882 :GNC 450939 : uint8 typalignby = typalign_to_alignby(typalign);
4883 : : int bitmask;
4884 : : int i;
4885 : :
4886 : : /* easy if fixed-size elements and no NULLs */
7423 tgl@sss.pgh.pa.us 4887 [ + + + + ]:CBC 450939 : if (typlen > 0 && !nullbitmap)
41 tgl@sss.pgh.pa.us 4888 :GNC 320926 : return ptr + nitems * ((Size) att_nominal_alignby(typlen, typalignby));
4889 : :
4890 : : /* seems worth having separate loops for NULL and no-NULLs cases */
7423 tgl@sss.pgh.pa.us 4891 [ + + ]:CBC 130013 : if (nullbitmap)
4892 : : {
4893 : 302 : nullbitmap += offset / 8;
4894 : 302 : bitmask = 1 << (offset % 8);
4895 : :
4896 [ + + ]: 926 : for (i = 0; i < nitems; i++)
4897 : : {
4898 [ + + ]: 624 : if (*nullbitmap & bitmask)
4899 : : {
6918 4900 [ + + + - : 444 : ptr = att_addlength_pointer(ptr, typlen, ptr);
- - - - -
- - - - +
- - ]
41 tgl@sss.pgh.pa.us 4901 :GNC 444 : ptr = (char *) att_nominal_alignby(ptr, typalignby);
4902 : : }
7423 tgl@sss.pgh.pa.us 4903 :CBC 624 : bitmask <<= 1;
4904 [ + + ]: 624 : if (bitmask == 0x100)
4905 : : {
4906 : 24 : nullbitmap++;
4907 : 24 : bitmask = 1;
4908 : : }
4909 : : }
4910 : : }
4911 : : else
4912 : : {
4913 [ + + ]: 587358 : for (i = 0; i < nitems; i++)
4914 : : {
6918 4915 [ - + + - : 457647 : ptr = att_addlength_pointer(ptr, typlen, ptr);
- - - - -
- - - - +
- - ]
41 tgl@sss.pgh.pa.us 4916 :GNC 457647 : ptr = (char *) att_nominal_alignby(ptr, typalignby);
4917 : : }
4918 : : }
7423 tgl@sss.pgh.pa.us 4919 :CBC 130013 : return ptr;
4920 : : }
4921 : :
4922 : : /*
4923 : : * Compute total size of the nitems array elements starting at *ptr
4924 : : *
4925 : : * Parameters same as for array_seek
4926 : : */
4927 : : static int
4928 : 819 : array_nelems_size(char *ptr, int offset, bits8 *nullbitmap, int nitems,
4929 : : int typlen, bool typbyval, char typalign)
4930 : : {
4931 : 819 : return array_seek(ptr, offset, nullbitmap, nitems,
4932 : 819 : typlen, typbyval, typalign) - ptr;
4933 : : }
4934 : :
4935 : : /*
4936 : : * Copy nitems array elements from srcptr to destptr
4937 : : *
4938 : : * destptr: starting destination location (must be enough room!)
4939 : : * nitems: number of array elements to copy (>= 0)
4940 : : * srcptr: starting location in source array
4941 : : * offset: 0-based linear element number of first element (the one at *srcptr)
4942 : : * nullbitmap: start of source array's null bitmap, or NULL if none
4943 : : * typlen, typbyval, typalign: storage parameters of array element datatype
4944 : : *
4945 : : * Returns number of bytes copied
4946 : : *
4947 : : * NB: this does not take care of setting up the destination's null bitmap!
4948 : : */
4949 : : static int
4950 : 585 : array_copy(char *destptr, int nitems,
4951 : : char *srcptr, int offset, bits8 *nullbitmap,
4952 : : int typlen, bool typbyval, char typalign)
4953 : : {
4954 : : int numbytes;
4955 : :
4956 : 585 : numbytes = array_nelems_size(srcptr, offset, nullbitmap, nitems,
4957 : : typlen, typbyval, typalign);
4958 : 585 : memcpy(destptr, srcptr, numbytes);
9366 4959 : 585 : return numbytes;
4960 : : }
4961 : :
4962 : : /*
4963 : : * Copy nitems null-bitmap bits from source to destination
4964 : : *
4965 : : * destbitmap: start of destination array's null bitmap (mustn't be NULL)
4966 : : * destoffset: 0-based linear element number of first dest element
4967 : : * srcbitmap: start of source array's null bitmap, or NULL if none
4968 : : * srcoffset: 0-based linear element number of first source element
4969 : : * nitems: number of bits to copy (>= 0)
4970 : : *
4971 : : * If srcbitmap is NULL then we assume the source is all-non-NULL and
4972 : : * fill 1's into the destination bitmap. Note that only the specified
4973 : : * bits in the destination map are changed, not any before or after.
4974 : : *
4975 : : * Note: this could certainly be optimized using standard bitblt methods.
4976 : : * However, it's not clear that the typical Postgres array has enough elements
4977 : : * to make it worth worrying too much. For the moment, KISS.
4978 : : */
4979 : : void
7423 4980 : 15532 : array_bitmap_copy(bits8 *destbitmap, int destoffset,
4981 : : const bits8 *srcbitmap, int srcoffset,
4982 : : int nitems)
4983 : : {
4984 : : int destbitmask,
4985 : : destbitval,
4986 : : srcbitmask,
4987 : : srcbitval;
4988 : :
4989 [ - + ]: 15532 : Assert(destbitmap);
4990 [ + + ]: 15532 : if (nitems <= 0)
4991 : 96 : return; /* don't risk fetch off end of memory */
4992 : 15436 : destbitmap += destoffset / 8;
4993 : 15436 : destbitmask = 1 << (destoffset % 8);
4994 : 15436 : destbitval = *destbitmap;
4995 [ + + ]: 15436 : if (srcbitmap)
4996 : : {
4997 : 7859 : srcbitmap += srcoffset / 8;
4998 : 7859 : srcbitmask = 1 << (srcoffset % 8);
4999 : 7859 : srcbitval = *srcbitmap;
5000 [ + + ]: 35994 : while (nitems-- > 0)
5001 : : {
5002 [ + + ]: 28135 : if (srcbitval & srcbitmask)
5003 : 10549 : destbitval |= destbitmask;
5004 : : else
5005 : 17586 : destbitval &= ~destbitmask;
5006 : 28135 : destbitmask <<= 1;
5007 [ + + ]: 28135 : if (destbitmask == 0x100)
5008 : : {
5009 : 3318 : *destbitmap++ = destbitval;
5010 : 3318 : destbitmask = 1;
5011 [ + + ]: 3318 : if (nitems > 0)
5012 : 2467 : destbitval = *destbitmap;
5013 : : }
5014 : 28135 : srcbitmask <<= 1;
5015 [ + + ]: 28135 : if (srcbitmask == 0x100)
5016 : : {
5017 : 2481 : srcbitmap++;
5018 : 2481 : srcbitmask = 1;
5019 [ + + ]: 2481 : if (nitems > 0)
5020 : 2441 : srcbitval = *srcbitmap;
5021 : : }
5022 : : }
5023 [ + + ]: 7859 : if (destbitmask != 1)
5024 : 7008 : *destbitmap = destbitval;
5025 : : }
5026 : : else
5027 : : {
5028 [ + + ]: 15312 : while (nitems-- > 0)
5029 : : {
5030 : 7735 : destbitval |= destbitmask;
5031 : 7735 : destbitmask <<= 1;
5032 [ + + ]: 7735 : if (destbitmask == 0x100)
5033 : : {
5034 : 1066 : *destbitmap++ = destbitval;
5035 : 1066 : destbitmask = 1;
5036 [ - + ]: 1066 : if (nitems > 0)
7423 tgl@sss.pgh.pa.us 5037 :UBC 0 : destbitval = *destbitmap;
5038 : : }
5039 : : }
7423 tgl@sss.pgh.pa.us 5040 [ + + ]:CBC 7577 : if (destbitmask != 1)
5041 : 6511 : *destbitmap = destbitval;
5042 : : }
5043 : : }
5044 : :
5045 : : /*
5046 : : * Compute space needed for a slice of an array
5047 : : *
5048 : : * We assume the caller has verified that the slice coordinates are valid.
5049 : : */
5050 : : static int
5051 : 153 : array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
5052 : : int ndim, int *dim, int *lb,
5053 : : int *st, int *endp,
5054 : : int typlen, bool typbyval, char typalign)
5055 : : {
5056 : : int src_offset,
5057 : : span[MAXDIM],
5058 : : prod[MAXDIM],
5059 : : dist[MAXDIM],
5060 : : indx[MAXDIM];
5061 : : char *ptr;
5062 : : int i,
5063 : : j,
5064 : : inc;
10415 bruce@momjian.us 5065 : 153 : int count = 0;
41 tgl@sss.pgh.pa.us 5066 :GNC 153 : uint8 typalignby = typalign_to_alignby(typalign);
5067 : :
9366 tgl@sss.pgh.pa.us 5068 :CBC 153 : mda_get_range(ndim, span, st, endp);
5069 : :
5070 : : /* Pretty easy for fixed element length without nulls ... */
7423 5071 [ + + + + ]: 153 : if (typlen > 0 && !arraynullsptr)
41 tgl@sss.pgh.pa.us 5072 :GNC 114 : return ArrayGetNItems(ndim, span) * att_nominal_alignby(typlen, typalignby);
5073 : :
5074 : : /* Else gotta do it the hard way */
7423 tgl@sss.pgh.pa.us 5075 :CBC 39 : src_offset = ArrayGetOffset(ndim, dim, lb, st);
5076 : 39 : ptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
5077 : : typlen, typbyval, typalign);
9366 5078 : 39 : mda_get_prod(ndim, dim, prod);
5079 : 39 : mda_get_offset_values(ndim, dist, prod, span);
5080 [ + + ]: 99 : for (i = 0; i < ndim; i++)
9367 5081 : 60 : indx[i] = 0;
9366 5082 : 39 : j = ndim - 1;
5083 : : do
5084 : : {
7423 5085 [ - + ]: 129 : if (dist[j])
5086 : : {
7423 tgl@sss.pgh.pa.us 5087 :UBC 0 : ptr = array_seek(ptr, src_offset, arraynullsptr, dist[j],
5088 : : typlen, typbyval, typalign);
5089 : 0 : src_offset += dist[j];
5090 : : }
7423 tgl@sss.pgh.pa.us 5091 [ + + ]:CBC 129 : if (!array_get_isnull(arraynullsptr, src_offset))
5092 : : {
6918 5093 [ + + + - : 123 : inc = att_addlength_pointer(0, typlen, ptr);
- - - - -
- - - - +
- - ]
41 tgl@sss.pgh.pa.us 5094 :GNC 123 : inc = att_nominal_alignby(inc, typalignby);
7423 tgl@sss.pgh.pa.us 5095 :CBC 123 : ptr += inc;
5096 : 123 : count += inc;
5097 : : }
5098 : 129 : src_offset++;
9366 5099 [ + + ]: 129 : } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
10416 bruce@momjian.us 5100 : 39 : return count;
5101 : : }
5102 : :
5103 : : /*
5104 : : * Extract a slice of an array into consecutive elements in the destination
5105 : : * array.
5106 : : *
5107 : : * We assume the caller has verified that the slice coordinates are valid,
5108 : : * allocated enough storage for the result, and initialized the header
5109 : : * of the new array.
5110 : : */
5111 : : static void
7423 tgl@sss.pgh.pa.us 5112 : 138 : array_extract_slice(ArrayType *newarray,
5113 : : int ndim,
5114 : : int *dim,
5115 : : int *lb,
5116 : : char *arraydataptr,
5117 : : bits8 *arraynullsptr,
5118 : : int *st,
5119 : : int *endp,
5120 : : int typlen,
5121 : : bool typbyval,
5122 : : char typalign)
5123 : : {
5124 [ + + ]: 138 : char *destdataptr = ARR_DATA_PTR(newarray);
5125 [ + + ]: 138 : bits8 *destnullsptr = ARR_NULLBITMAP(newarray);
5126 : : char *srcdataptr;
5127 : : int src_offset,
5128 : : dest_offset,
5129 : : prod[MAXDIM],
5130 : : span[MAXDIM],
5131 : : dist[MAXDIM],
5132 : : indx[MAXDIM];
5133 : : int i,
5134 : : j,
5135 : : inc;
5136 : :
5137 : 138 : src_offset = ArrayGetOffset(ndim, dim, lb, st);
5138 : 138 : srcdataptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
5139 : : typlen, typbyval, typalign);
9366 5140 : 138 : mda_get_prod(ndim, dim, prod);
5141 : 138 : mda_get_range(ndim, span, st, endp);
5142 : 138 : mda_get_offset_values(ndim, dist, prod, span);
5143 [ + + ]: 348 : for (i = 0; i < ndim; i++)
5144 : 210 : indx[i] = 0;
7423 5145 : 138 : dest_offset = 0;
9366 5146 : 138 : j = ndim - 1;
5147 : : do
5148 : : {
7423 5149 [ + + ]: 507 : if (dist[j])
5150 : : {
5151 : : /* skip unwanted elements */
5152 : 15 : srcdataptr = array_seek(srcdataptr, src_offset, arraynullsptr,
5153 : : dist[j],
5154 : : typlen, typbyval, typalign);
5155 : 15 : src_offset += dist[j];
5156 : : }
5157 : 507 : inc = array_copy(destdataptr, 1,
5158 : : srcdataptr, src_offset, arraynullsptr,
5159 : : typlen, typbyval, typalign);
5160 [ + + ]: 507 : if (destnullsptr)
5161 : 90 : array_bitmap_copy(destnullsptr, dest_offset,
5162 : : arraynullsptr, src_offset,
5163 : : 1);
5164 : 507 : destdataptr += inc;
5165 : 507 : srcdataptr += inc;
5166 : 507 : src_offset++;
5167 : 507 : dest_offset++;
9366 5168 [ + + ]: 507 : } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
10841 scrappy@hub.org 5169 : 138 : }
5170 : :
5171 : : /*
5172 : : * Insert a slice into an array.
5173 : : *
5174 : : * ndim/dim[]/lb[] are dimensions of the original array. A new array with
5175 : : * those same dimensions is to be constructed. destArray must already
5176 : : * have been allocated and its header initialized.
5177 : : *
5178 : : * st[]/endp[] identify the slice to be replaced. Elements within the slice
5179 : : * volume are taken from consecutive elements of the srcArray; elements
5180 : : * outside it are copied from origArray.
5181 : : *
5182 : : * We assume the caller has verified that the slice coordinates are valid.
5183 : : */
5184 : : static void
7423 tgl@sss.pgh.pa.us 5185 : 15 : array_insert_slice(ArrayType *destArray,
5186 : : ArrayType *origArray,
5187 : : ArrayType *srcArray,
5188 : : int ndim,
5189 : : int *dim,
5190 : : int *lb,
5191 : : int *st,
5192 : : int *endp,
5193 : : int typlen,
5194 : : bool typbyval,
5195 : : char typalign)
5196 : : {
5197 [ - + ]: 15 : char *destPtr = ARR_DATA_PTR(destArray);
5198 [ - + ]: 15 : char *origPtr = ARR_DATA_PTR(origArray);
5199 [ - + ]: 15 : char *srcPtr = ARR_DATA_PTR(srcArray);
5200 [ - + ]: 15 : bits8 *destBitmap = ARR_NULLBITMAP(destArray);
5201 [ - + ]: 15 : bits8 *origBitmap = ARR_NULLBITMAP(origArray);
5202 [ - + ]: 15 : bits8 *srcBitmap = ARR_NULLBITMAP(srcArray);
5203 : 15 : int orignitems = ArrayGetNItems(ARR_NDIM(origArray),
5204 : : ARR_DIMS(origArray));
5205 : : int dest_offset,
5206 : : orig_offset,
5207 : : src_offset,
5208 : : prod[MAXDIM],
5209 : : span[MAXDIM],
5210 : : dist[MAXDIM],
5211 : : indx[MAXDIM];
5212 : : int i,
5213 : : j,
5214 : : inc;
5215 : :
5216 : 15 : dest_offset = ArrayGetOffset(ndim, dim, lb, st);
5217 : : /* copy items before the slice start */
5218 : 15 : inc = array_copy(destPtr, dest_offset,
5219 : : origPtr, 0, origBitmap,
5220 : : typlen, typbyval, typalign);
9366 5221 : 15 : destPtr += inc;
5222 : 15 : origPtr += inc;
7423 5223 [ - + ]: 15 : if (destBitmap)
7423 tgl@sss.pgh.pa.us 5224 :UBC 0 : array_bitmap_copy(destBitmap, 0, origBitmap, 0, dest_offset);
7423 tgl@sss.pgh.pa.us 5225 :CBC 15 : orig_offset = dest_offset;
9366 5226 : 15 : mda_get_prod(ndim, dim, prod);
5227 : 15 : mda_get_range(ndim, span, st, endp);
5228 : 15 : mda_get_offset_values(ndim, dist, prod, span);
5229 [ + + ]: 51 : for (i = 0; i < ndim; i++)
5230 : 36 : indx[i] = 0;
7423 5231 : 15 : src_offset = 0;
9366 5232 : 15 : j = ndim - 1;
5233 : : do
5234 : : {
5235 : : /* Copy/advance over elements between here and next part of slice */
7423 5236 [ + + ]: 39 : if (dist[j])
5237 : : {
5238 : 9 : inc = array_copy(destPtr, dist[j],
5239 : : origPtr, orig_offset, origBitmap,
5240 : : typlen, typbyval, typalign);
5241 : 9 : destPtr += inc;
5242 : 9 : origPtr += inc;
5243 [ - + ]: 9 : if (destBitmap)
7423 tgl@sss.pgh.pa.us 5244 :UBC 0 : array_bitmap_copy(destBitmap, dest_offset,
5245 : : origBitmap, orig_offset,
5246 : : dist[j]);
7423 tgl@sss.pgh.pa.us 5247 :CBC 9 : dest_offset += dist[j];
5248 : 9 : orig_offset += dist[j];
5249 : : }
5250 : : /* Copy new element at this slice position */
5251 : 39 : inc = array_copy(destPtr, 1,
5252 : : srcPtr, src_offset, srcBitmap,
5253 : : typlen, typbyval, typalign);
5254 [ - + ]: 39 : if (destBitmap)
7423 tgl@sss.pgh.pa.us 5255 :UBC 0 : array_bitmap_copy(destBitmap, dest_offset,
5256 : : srcBitmap, src_offset,
5257 : : 1);
9366 tgl@sss.pgh.pa.us 5258 :CBC 39 : destPtr += inc;
5259 : 39 : srcPtr += inc;
7423 5260 : 39 : dest_offset++;
5261 : 39 : src_offset++;
5262 : : /* Advance over old element at this slice position */
5263 : 39 : origPtr = array_seek(origPtr, orig_offset, origBitmap, 1,
5264 : : typlen, typbyval, typalign);
5265 : 39 : orig_offset++;
9366 5266 [ + + ]: 39 : } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
5267 : :
5268 : : /* don't miss any data at the end */
7423 5269 : 15 : array_copy(destPtr, orignitems - orig_offset,
5270 : : origPtr, orig_offset, origBitmap,
5271 : : typlen, typbyval, typalign);
5272 [ - + ]: 15 : if (destBitmap)
7423 tgl@sss.pgh.pa.us 5273 :UBC 0 : array_bitmap_copy(destBitmap, dest_offset,
5274 : : origBitmap, orig_offset,
5275 : : orignitems - orig_offset);
10841 scrappy@hub.org 5276 :CBC 15 : }
5277 : :
5278 : : /*
5279 : : * initArrayResult - initialize an empty ArrayBuildState
5280 : : *
5281 : : * element_type is the array element type (must be a valid array element type)
5282 : : * rcontext is where to keep working state
5283 : : * subcontext is a flag determining whether to use a separate memory context
5284 : : *
5285 : : * Note: there are two common schemes for using accumArrayResult().
5286 : : * In the older scheme, you start with a NULL ArrayBuildState pointer, and
5287 : : * call accumArrayResult once per element. In this scheme you end up with
5288 : : * a NULL pointer if there were no elements, which you need to special-case.
5289 : : * In the newer scheme, call initArrayResult and then call accumArrayResult
5290 : : * once per element. In this scheme you always end with a non-NULL pointer
5291 : : * that you can pass to makeArrayResult; you get an empty array if there
5292 : : * were no elements. This is preferred if an empty array is what you want.
5293 : : *
5294 : : * It's possible to choose whether to create a separate memory context for the
5295 : : * array build state, or whether to allocate it directly within rcontext.
5296 : : *
5297 : : * When there are many concurrent small states (e.g. array_agg() using hash
5298 : : * aggregation of many small groups), using a separate memory context for each
5299 : : * one may result in severe memory bloat. In such cases, use the same memory
5300 : : * context to initialize all such array build states, and pass
5301 : : * subcontext=false.
5302 : : *
5303 : : * In cases when the array build states have different lifetimes, using a
5304 : : * single memory context is impractical. Instead, pass subcontext=true so that
5305 : : * the array build states can be freed individually.
5306 : : */
5307 : : ArrayBuildState *
4040 jdavis@postgresql.or 5308 : 210277 : initArrayResult(Oid element_type, MemoryContext rcontext, bool subcontext)
5309 : : {
5310 : : /*
5311 : : * When using a subcontext, we can afford to start with a somewhat larger
5312 : : * initial array size. Without subcontexts, we'd better hope that most of
5313 : : * the states stay small ...
5314 : : */
1147 drowley@postgresql.o 5315 [ + + ]: 210277 : return initArrayResultWithSize(element_type, rcontext, subcontext,
5316 : : subcontext ? 64 : 8);
5317 : : }
5318 : :
5319 : : /*
5320 : : * initArrayResultWithSize
5321 : : * As initArrayResult, but allow the initial size of the allocated arrays
5322 : : * to be specified.
5323 : : */
5324 : : ArrayBuildState *
5325 : 210367 : initArrayResultWithSize(Oid element_type, MemoryContext rcontext,
5326 : : bool subcontext, int initsize)
5327 : : {
5328 : : ArrayBuildState *astate;
4040 jdavis@postgresql.or 5329 : 210367 : MemoryContext arr_context = rcontext;
5330 : :
5331 : : /* Make a temporary context to hold all the junk */
5332 [ + + ]: 210367 : if (subcontext)
5333 : 139170 : arr_context = AllocSetContextCreate(rcontext,
5334 : : "accumArrayResult",
5335 : : ALLOCSET_DEFAULT_SIZES);
5336 : :
5337 : : astate = (ArrayBuildState *)
4128 tgl@sss.pgh.pa.us 5338 : 210367 : MemoryContextAlloc(arr_context, sizeof(ArrayBuildState));
5339 : 210367 : astate->mcontext = arr_context;
4040 jdavis@postgresql.or 5340 : 210367 : astate->private_cxt = subcontext;
1147 drowley@postgresql.o 5341 : 210367 : astate->alen = initsize;
4128 tgl@sss.pgh.pa.us 5342 : 210367 : astate->dvalues = (Datum *)
5343 : 210367 : MemoryContextAlloc(arr_context, astate->alen * sizeof(Datum));
5344 : 210367 : astate->dnulls = (bool *)
5345 : 210367 : MemoryContextAlloc(arr_context, astate->alen * sizeof(bool));
5346 : 210367 : astate->nelems = 0;
5347 : 210367 : astate->element_type = element_type;
5348 : 210367 : get_typlenbyvalalign(element_type,
5349 : : &astate->typlen,
5350 : : &astate->typbyval,
5351 : : &astate->typalign);
5352 : :
5353 : 210367 : return astate;
5354 : : }
5355 : :
5356 : : /*
5357 : : * accumArrayResult - accumulate one (more) Datum for an array result
5358 : : *
5359 : : * astate is working state (can be NULL on first call)
5360 : : * dvalue/disnull represent the new Datum to append to the array
5361 : : * element_type is the Datum's type (must be a valid array element type)
5362 : : * rcontext is where to keep working state
5363 : : */
5364 : : ArrayBuildState *
8255 bruce@momjian.us 5365 : 1841186 : accumArrayResult(ArrayBuildState *astate,
5366 : : Datum dvalue, bool disnull,
5367 : : Oid element_type,
5368 : : MemoryContext rcontext)
5369 : : {
5370 : : MemoryContext oldcontext;
5371 : :
8297 tgl@sss.pgh.pa.us 5372 [ + + ]: 1841186 : if (astate == NULL)
5373 : : {
5374 : : /* First time through --- initialize */
4040 jdavis@postgresql.or 5375 : 109664 : astate = initArrayResult(element_type, rcontext, true);
5376 : : }
5377 : : else
5378 : : {
8297 tgl@sss.pgh.pa.us 5379 [ - + ]: 1731522 : Assert(astate->element_type == element_type);
5380 : : }
5381 : :
4128 5382 : 1841186 : oldcontext = MemoryContextSwitchTo(astate->mcontext);
5383 : :
5384 : : /* enlarge dvalues[]/dnulls[] if needed */
5385 [ + + ]: 1841186 : if (astate->nelems >= astate->alen)
5386 : : {
5387 : 43731 : astate->alen *= 2;
5388 : : /* give an array-related error if we go past MaxAllocSize */
975 5389 [ - + ]: 43731 : if (!AllocSizeIsValid(astate->alen * sizeof(Datum)))
975 tgl@sss.pgh.pa.us 5390 [ # # ]:UBC 0 : ereport(ERROR,
5391 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
5392 : : errmsg("array size exceeds the maximum allowed (%zu)",
5393 : : MaxAllocSize)));
4128 tgl@sss.pgh.pa.us 5394 :CBC 43731 : astate->dvalues = (Datum *)
5395 : 43731 : repalloc(astate->dvalues, astate->alen * sizeof(Datum));
5396 : 43731 : astate->dnulls = (bool *)
5397 : 43731 : repalloc(astate->dnulls, astate->alen * sizeof(bool));
5398 : : }
5399 : :
5400 : : /*
5401 : : * Ensure pass-by-ref stuff is copied into mcontext; and detoast it too if
5402 : : * it's varlena. (You might think that detoasting is not needed here
5403 : : * because construct_md_array can detoast the array elements later.
5404 : : * However, we must not let construct_md_array modify the ArrayBuildState
5405 : : * because that would mean array_agg_finalfn damages its input, which is
5406 : : * verboten. Also, this way frequently saves one copying step.)
5407 : : */
7423 5408 [ + + + + ]: 1841186 : if (!disnull && !astate->typbyval)
5409 : : {
6112 5410 [ + + ]: 1249741 : if (astate->typlen == -1)
5411 : 933440 : dvalue = PointerGetDatum(PG_DETOAST_DATUM_COPY(dvalue));
5412 : : else
5413 : 316301 : dvalue = datumCopy(dvalue, astate->typbyval, astate->typlen);
5414 : : }
5415 : :
7423 5416 : 1841186 : astate->dvalues[astate->nelems] = dvalue;
5417 : 1841186 : astate->dnulls[astate->nelems] = disnull;
5418 : 1841186 : astate->nelems++;
5419 : :
8297 5420 : 1841186 : MemoryContextSwitchTo(oldcontext);
5421 : :
5422 : 1841186 : return astate;
5423 : : }
5424 : :
5425 : : /*
5426 : : * makeArrayResult - produce 1-D final result of accumArrayResult
5427 : : *
5428 : : * Note: only releases astate if it was initialized within a separate memory
5429 : : * context (i.e. using subcontext=true when calling initArrayResult).
5430 : : *
5431 : : * astate is working state (must not be NULL)
5432 : : * rcontext is where to construct result
5433 : : */
5434 : : Datum
8255 bruce@momjian.us 5435 : 109758 : makeArrayResult(ArrayBuildState *astate,
5436 : : MemoryContext rcontext)
5437 : : {
5438 : : int ndims;
5439 : : int dims[1];
5440 : : int lbs[1];
5441 : :
5442 : : /* If no elements were presented, we want to create an empty array */
4128 tgl@sss.pgh.pa.us 5443 : 109758 : ndims = (astate->nelems > 0) ? 1 : 0;
8297 5444 : 109758 : dims[0] = astate->nelems;
5445 : 109758 : lbs[0] = 1;
5446 : :
4040 jdavis@postgresql.or 5447 : 219516 : return makeMdArrayResult(astate, ndims, dims, lbs, rcontext,
5448 : 109758 : astate->private_cxt);
5449 : : }
5450 : :
5451 : : /*
5452 : : * makeMdArrayResult - produce multi-D final result of accumArrayResult
5453 : : *
5454 : : * beware: no check that specified dimensions match the number of values
5455 : : * accumulated.
5456 : : *
5457 : : * Note: if the astate was not initialized within a separate memory context
5458 : : * (that is, initArrayResult was called with subcontext=false), then using
5459 : : * release=true is illegal. Instead, release astate along with the rest of its
5460 : : * context when appropriate.
5461 : : *
5462 : : * astate is working state (must not be NULL)
5463 : : * rcontext is where to construct result
5464 : : * release is true if okay to release working state
5465 : : */
5466 : : Datum
8255 bruce@momjian.us 5467 : 209708 : makeMdArrayResult(ArrayBuildState *astate,
5468 : : int ndims,
5469 : : int *dims,
5470 : : int *lbs,
5471 : : MemoryContext rcontext,
5472 : : bool release)
5473 : : {
5474 : : ArrayType *result;
5475 : : MemoryContext oldcontext;
5476 : :
5477 : : /* Build the final array result in rcontext */
8297 tgl@sss.pgh.pa.us 5478 : 209708 : oldcontext = MemoryContextSwitchTo(rcontext);
5479 : :
5480 : 209708 : result = construct_md_array(astate->dvalues,
5481 : : astate->dnulls,
5482 : : ndims,
5483 : : dims,
5484 : : lbs,
5485 : : astate->element_type,
5486 : 209708 : astate->typlen,
5487 : 209708 : astate->typbyval,
5488 : 209708 : astate->typalign);
5489 : :
5490 : 209708 : MemoryContextSwitchTo(oldcontext);
5491 : :
5492 : : /* Clean up all the junk */
6286 5493 [ + + ]: 209708 : if (release)
5494 : : {
4040 jdavis@postgresql.or 5495 [ - + ]: 138868 : Assert(astate->private_cxt);
6286 tgl@sss.pgh.pa.us 5496 : 138868 : MemoryContextDelete(astate->mcontext);
5497 : : }
5498 : :
8297 5499 : 209708 : return PointerGetDatum(result);
5500 : : }
5501 : :
5502 : : /*
5503 : : * The following three functions provide essentially the same API as
5504 : : * initArrayResult/accumArrayResult/makeArrayResult, but instead of accepting
5505 : : * inputs that are array elements, they accept inputs that are arrays and
5506 : : * produce an output array having N+1 dimensions. The inputs must all have
5507 : : * identical dimensionality as well as element type.
5508 : : */
5509 : :
5510 : : /*
5511 : : * initArrayResultArr - initialize an empty ArrayBuildStateArr
5512 : : *
5513 : : * array_type is the array type (must be a valid varlena array type)
5514 : : * element_type is the type of the array's elements (lookup if InvalidOid)
5515 : : * rcontext is where to keep working state
5516 : : * subcontext is a flag determining whether to use a separate memory context
5517 : : */
5518 : : ArrayBuildStateArr *
4040 jdavis@postgresql.or 5519 : 320 : initArrayResultArr(Oid array_type, Oid element_type, MemoryContext rcontext,
5520 : : bool subcontext)
5521 : : {
5522 : : ArrayBuildStateArr *astate;
3189 tgl@sss.pgh.pa.us 5523 : 320 : MemoryContext arr_context = rcontext; /* by default use the parent ctx */
5524 : :
5525 : : /* Lookup element type, unless element_type already provided */
3958 5526 [ + + ]: 320 : if (!OidIsValid(element_type))
5527 : : {
4040 jdavis@postgresql.or 5528 : 260 : element_type = get_element_type(array_type);
5529 : :
5530 [ - + ]: 260 : if (!OidIsValid(element_type))
4040 jdavis@postgresql.or 5531 [ # # ]:UBC 0 : ereport(ERROR,
5532 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
5533 : : errmsg("data type %s is not an array type",
5534 : : format_type_be(array_type))));
5535 : : }
5536 : :
5537 : : /* Make a temporary context to hold all the junk */
4040 jdavis@postgresql.or 5538 [ + + ]:CBC 320 : if (subcontext)
5539 : 24 : arr_context = AllocSetContextCreate(rcontext,
5540 : : "accumArrayResultArr",
5541 : : ALLOCSET_DEFAULT_SIZES);
5542 : :
5543 : : /* Note we initialize all fields to zero */
5544 : : astate = (ArrayBuildStateArr *)
4128 tgl@sss.pgh.pa.us 5545 : 320 : MemoryContextAllocZero(arr_context, sizeof(ArrayBuildStateArr));
5546 : 320 : astate->mcontext = arr_context;
4040 jdavis@postgresql.or 5547 : 320 : astate->private_cxt = subcontext;
5548 : :
5549 : : /* Save relevant datatype information */
4128 tgl@sss.pgh.pa.us 5550 : 320 : astate->array_type = array_type;
5551 : 320 : astate->element_type = element_type;
5552 : :
5553 : 320 : return astate;
5554 : : }
5555 : :
5556 : : /*
5557 : : * accumArrayResultArr - accumulate one (more) sub-array for an array result
5558 : : *
5559 : : * astate is working state (can be NULL on first call)
5560 : : * dvalue/disnull represent the new sub-array to append to the array
5561 : : * array_type is the array type (must be a valid varlena array type)
5562 : : * rcontext is where to keep working state
5563 : : */
5564 : : ArrayBuildStateArr *
5565 : 33575 : accumArrayResultArr(ArrayBuildStateArr *astate,
5566 : : Datum dvalue, bool disnull,
5567 : : Oid array_type,
5568 : : MemoryContext rcontext)
5569 : : {
5570 : : ArrayType *arg;
5571 : : MemoryContext oldcontext;
5572 : : int *dims,
5573 : : *lbs,
5574 : : ndims,
5575 : : nitems,
5576 : : ndatabytes;
5577 : : char *data;
5578 : : int i;
5579 : :
5580 : : /*
5581 : : * We disallow accumulating null subarrays. Another plausible definition
5582 : : * is to ignore them, but callers that want that can just skip calling
5583 : : * this function.
5584 : : */
5585 [ + + ]: 33575 : if (disnull)
5586 [ + - ]: 3 : ereport(ERROR,
5587 : : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5588 : : errmsg("cannot accumulate null arrays")));
5589 : :
5590 : : /* Detoast input array in caller's context */
5591 : 33572 : arg = DatumGetArrayTypeP(dvalue);
5592 : :
5593 [ - + ]: 33572 : if (astate == NULL)
4040 jdavis@postgresql.or 5594 :UBC 0 : astate = initArrayResultArr(array_type, InvalidOid, rcontext, true);
5595 : : else
4128 tgl@sss.pgh.pa.us 5596 [ - + ]:CBC 33572 : Assert(astate->array_type == array_type);
5597 : :
5598 : 33572 : oldcontext = MemoryContextSwitchTo(astate->mcontext);
5599 : :
5600 : : /* Collect this input's dimensions */
5601 : 33572 : ndims = ARR_NDIM(arg);
5602 : 33572 : dims = ARR_DIMS(arg);
5603 : 33572 : lbs = ARR_LBOUND(arg);
5604 [ + + ]: 33572 : data = ARR_DATA_PTR(arg);
5605 : 33572 : nitems = ArrayGetNItems(ndims, dims);
5606 [ + + ]: 33572 : ndatabytes = ARR_SIZE(arg) - ARR_DATA_OFFSET(arg);
5607 : :
5608 [ + + ]: 33572 : if (astate->ndims == 0)
5609 : : {
5610 : : /* First input; check/save the dimensionality info */
5611 : :
5612 : : /* Should we allow empty inputs and just produce an empty output? */
5613 [ + + ]: 227 : if (ndims == 0)
5614 [ + - ]: 3 : ereport(ERROR,
5615 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5616 : : errmsg("cannot accumulate empty arrays")));
5617 [ - + ]: 224 : if (ndims + 1 > MAXDIM)
4128 tgl@sss.pgh.pa.us 5618 [ # # ]:UBC 0 : ereport(ERROR,
5619 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
5620 : : errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
5621 : : ndims + 1, MAXDIM)));
5622 : :
5623 : : /*
5624 : : * The output array will have n+1 dimensions, with the ones after the
5625 : : * first matching the input's dimensions.
5626 : : */
4128 tgl@sss.pgh.pa.us 5627 :CBC 224 : astate->ndims = ndims + 1;
5628 : 224 : astate->dims[0] = 0;
5629 : 224 : memcpy(&astate->dims[1], dims, ndims * sizeof(int));
5630 : 224 : astate->lbs[0] = 1;
5631 : 224 : memcpy(&astate->lbs[1], lbs, ndims * sizeof(int));
5632 : :
5633 : : /* Allocate at least enough data space for this item */
2167 drowley@postgresql.o 5634 : 224 : astate->abytes = pg_nextpower2_32(Max(1024, ndatabytes + 1));
4128 tgl@sss.pgh.pa.us 5635 : 224 : astate->data = (char *) palloc(astate->abytes);
5636 : : }
5637 : : else
5638 : : {
5639 : : /* Second or later input: must match first input's dimensionality */
5640 [ - + ]: 33345 : if (astate->ndims != ndims + 1)
4128 tgl@sss.pgh.pa.us 5641 [ # # ]:UBC 0 : ereport(ERROR,
5642 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5643 : : errmsg("cannot accumulate arrays of different dimensionality")));
4128 tgl@sss.pgh.pa.us 5644 [ + + ]:CBC 66687 : for (i = 0; i < ndims; i++)
5645 : : {
5646 [ + + - + ]: 33345 : if (astate->dims[i + 1] != dims[i] || astate->lbs[i + 1] != lbs[i])
5647 [ + - ]: 3 : ereport(ERROR,
5648 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5649 : : errmsg("cannot accumulate arrays of different dimensionality")));
5650 : : }
5651 : :
5652 : : /* Enlarge data space if needed */
5653 [ + + ]: 33342 : if (astate->nbytes + ndatabytes > astate->abytes)
5654 : : {
5655 : 61 : astate->abytes = Max(astate->abytes * 2,
5656 : : astate->nbytes + ndatabytes);
5657 : 61 : astate->data = (char *) repalloc(astate->data, astate->abytes);
5658 : : }
5659 : : }
5660 : :
5661 : : /*
5662 : : * Copy the data portion of the sub-array. Note we assume that the
5663 : : * advertised data length of the sub-array is properly aligned. We do not
5664 : : * have to worry about detoasting elements since whatever's in the
5665 : : * sub-array should be OK already.
5666 : : */
5667 : 33566 : memcpy(astate->data + astate->nbytes, data, ndatabytes);
5668 : 33566 : astate->nbytes += ndatabytes;
5669 : :
5670 : : /* Deal with null bitmap if needed */
5671 [ + + + + ]: 33566 : if (astate->nullbitmap || ARR_HASNULL(arg))
5672 : : {
5673 : 15046 : int newnitems = astate->nitems + nitems;
5674 : :
5675 [ + + ]: 15046 : if (astate->nullbitmap == NULL)
5676 : : {
5677 : : /*
5678 : : * First input with nulls; we must retrospectively handle any
5679 : : * previous inputs by marking all their items non-null.
5680 : : */
2167 drowley@postgresql.o 5681 : 64 : astate->aitems = pg_nextpower2_32(Max(256, newnitems + 1));
4128 tgl@sss.pgh.pa.us 5682 : 64 : astate->nullbitmap = (bits8 *) palloc((astate->aitems + 7) / 8);
5683 : 64 : array_bitmap_copy(astate->nullbitmap, 0,
5684 : : NULL, 0,
5685 : : astate->nitems);
5686 : : }
5687 [ + + ]: 14982 : else if (newnitems > astate->aitems)
5688 : : {
5689 : 25 : astate->aitems = Max(astate->aitems * 2, newnitems);
5690 : 25 : astate->nullbitmap = (bits8 *)
5691 : 25 : repalloc(astate->nullbitmap, (astate->aitems + 7) / 8);
5692 : : }
5693 : 15046 : array_bitmap_copy(astate->nullbitmap, astate->nitems,
5694 [ + + ]: 15046 : ARR_NULLBITMAP(arg), 0,
5695 : : nitems);
5696 : : }
5697 : :
5698 : 33566 : astate->nitems += nitems;
5699 : 33566 : astate->dims[0] += 1;
5700 : :
5701 : 33566 : MemoryContextSwitchTo(oldcontext);
5702 : :
5703 : : /* Release detoasted copy if any */
101 peter@eisentraut.org 5704 [ + + ]:GNC 33566 : if (arg != DatumGetPointer(dvalue))
4128 tgl@sss.pgh.pa.us 5705 :CBC 3407 : pfree(arg);
5706 : :
5707 : 33566 : return astate;
5708 : : }
5709 : :
5710 : : /*
5711 : : * makeArrayResultArr - produce N+1-D final result of accumArrayResultArr
5712 : : *
5713 : : * astate is working state (must not be NULL)
5714 : : * rcontext is where to construct result
5715 : : * release is true if okay to release working state
5716 : : */
5717 : : Datum
5718 : 191 : makeArrayResultArr(ArrayBuildStateArr *astate,
5719 : : MemoryContext rcontext,
5720 : : bool release)
5721 : : {
5722 : : ArrayType *result;
5723 : : MemoryContext oldcontext;
5724 : :
5725 : : /* Build the final array result in rcontext */
5726 : 191 : oldcontext = MemoryContextSwitchTo(rcontext);
5727 : :
5728 [ - + ]: 191 : if (astate->ndims == 0)
5729 : : {
5730 : : /* No inputs, return empty array */
4128 tgl@sss.pgh.pa.us 5731 :UBC 0 : result = construct_empty_array(astate->element_type);
5732 : : }
5733 : : else
5734 : : {
5735 : : int dataoffset,
5736 : : nbytes;
5737 : :
5738 : : /* Check for overflow of the array dimensions */
1770 tgl@sss.pgh.pa.us 5739 :CBC 191 : (void) ArrayGetNItems(astate->ndims, astate->dims);
5740 : 191 : ArrayCheckBounds(astate->ndims, astate->dims, astate->lbs);
5741 : :
5742 : : /* Compute required space */
4128 5743 : 191 : nbytes = astate->nbytes;
5744 [ + + ]: 191 : if (astate->nullbitmap != NULL)
5745 : : {
5746 : 49 : dataoffset = ARR_OVERHEAD_WITHNULLS(astate->ndims, astate->nitems);
5747 : 49 : nbytes += dataoffset;
5748 : : }
5749 : : else
5750 : : {
5751 : 142 : dataoffset = 0;
5752 : 142 : nbytes += ARR_OVERHEAD_NONULLS(astate->ndims);
5753 : : }
5754 : :
5755 : 191 : result = (ArrayType *) palloc0(nbytes);
5756 : 191 : SET_VARSIZE(result, nbytes);
5757 : 191 : result->ndim = astate->ndims;
5758 : 191 : result->dataoffset = dataoffset;
5759 : 191 : result->elemtype = astate->element_type;
5760 : :
5761 : 191 : memcpy(ARR_DIMS(result), astate->dims, astate->ndims * sizeof(int));
5762 : 191 : memcpy(ARR_LBOUND(result), astate->lbs, astate->ndims * sizeof(int));
5763 [ + + ]: 191 : memcpy(ARR_DATA_PTR(result), astate->data, astate->nbytes);
5764 : :
5765 [ + + ]: 191 : if (astate->nullbitmap != NULL)
5766 : 49 : array_bitmap_copy(ARR_NULLBITMAP(result), 0,
5767 [ + - ]: 49 : astate->nullbitmap, 0,
5768 : : astate->nitems);
5769 : : }
5770 : :
5771 : 191 : MemoryContextSwitchTo(oldcontext);
5772 : :
5773 : : /* Clean up all the junk */
5774 [ + + ]: 191 : if (release)
5775 : : {
4040 jdavis@postgresql.or 5776 [ - + ]: 24 : Assert(astate->private_cxt);
4128 tgl@sss.pgh.pa.us 5777 : 24 : MemoryContextDelete(astate->mcontext);
5778 : : }
5779 : :
5780 : 191 : return PointerGetDatum(result);
5781 : : }
5782 : :
5783 : : /*
5784 : : * The following three functions provide essentially the same API as
5785 : : * initArrayResult/accumArrayResult/makeArrayResult, but can accept either
5786 : : * scalar or array inputs, invoking the appropriate set of functions above.
5787 : : */
5788 : :
5789 : : /*
5790 : : * initArrayResultAny - initialize an empty ArrayBuildStateAny
5791 : : *
5792 : : * input_type is the input datatype (either element or array type)
5793 : : * rcontext is where to keep working state
5794 : : * subcontext is a flag determining whether to use a separate memory context
5795 : : */
5796 : : ArrayBuildStateAny *
4040 jdavis@postgresql.or 5797 : 28313 : initArrayResultAny(Oid input_type, MemoryContext rcontext, bool subcontext)
5798 : : {
5799 : : ArrayBuildStateAny *astate;
5800 : :
5801 : : /*
5802 : : * int2vector and oidvector will satisfy both get_element_type and
5803 : : * get_array_type. We prefer to treat them as scalars, to be consistent
5804 : : * with get_promoted_array_type. Hence, check get_array_type not
5805 : : * get_element_type.
5806 : : */
367 tgl@sss.pgh.pa.us 5807 [ + + ]: 28313 : if (!OidIsValid(get_array_type(input_type)))
5808 : : {
5809 : : /* Array case */
5810 : : ArrayBuildStateArr *arraystate;
5811 : :
4040 jdavis@postgresql.or 5812 : 24 : arraystate = initArrayResultArr(input_type, InvalidOid, rcontext, subcontext);
5813 : : astate = (ArrayBuildStateAny *)
4128 tgl@sss.pgh.pa.us 5814 : 24 : MemoryContextAlloc(arraystate->mcontext,
5815 : : sizeof(ArrayBuildStateAny));
5816 : 24 : astate->scalarstate = NULL;
5817 : 24 : astate->arraystate = arraystate;
5818 : : }
5819 : : else
5820 : : {
5821 : : /* Scalar case */
5822 : : ArrayBuildState *scalarstate;
5823 : :
4040 jdavis@postgresql.or 5824 : 28289 : scalarstate = initArrayResult(input_type, rcontext, subcontext);
5825 : : astate = (ArrayBuildStateAny *)
4128 tgl@sss.pgh.pa.us 5826 : 28289 : MemoryContextAlloc(scalarstate->mcontext,
5827 : : sizeof(ArrayBuildStateAny));
5828 : 28289 : astate->scalarstate = scalarstate;
5829 : 28289 : astate->arraystate = NULL;
5830 : : }
5831 : :
5832 : 28313 : return astate;
5833 : : }
5834 : :
5835 : : /*
5836 : : * accumArrayResultAny - accumulate one (more) input for an array result
5837 : : *
5838 : : * astate is working state (can be NULL on first call)
5839 : : * dvalue/disnull represent the new input to append to the array
5840 : : * input_type is the input datatype (either element or array type)
5841 : : * rcontext is where to keep working state
5842 : : */
5843 : : ArrayBuildStateAny *
5844 : 8826 : accumArrayResultAny(ArrayBuildStateAny *astate,
5845 : : Datum dvalue, bool disnull,
5846 : : Oid input_type,
5847 : : MemoryContext rcontext)
5848 : : {
5849 [ + + ]: 8826 : if (astate == NULL)
4040 jdavis@postgresql.or 5850 : 60 : astate = initArrayResultAny(input_type, rcontext, true);
5851 : :
4128 tgl@sss.pgh.pa.us 5852 [ + + ]: 8826 : if (astate->scalarstate)
5853 : 8757 : (void) accumArrayResult(astate->scalarstate,
5854 : : dvalue, disnull,
5855 : : input_type, rcontext);
5856 : : else
5857 : 69 : (void) accumArrayResultArr(astate->arraystate,
5858 : : dvalue, disnull,
5859 : : input_type, rcontext);
5860 : :
5861 : 8826 : return astate;
5862 : : }
5863 : :
5864 : : /*
5865 : : * makeArrayResultAny - produce final result of accumArrayResultAny
5866 : : *
5867 : : * astate is working state (must not be NULL)
5868 : : * rcontext is where to construct result
5869 : : * release is true if okay to release working state
5870 : : */
5871 : : Datum
5872 : 28313 : makeArrayResultAny(ArrayBuildStateAny *astate,
5873 : : MemoryContext rcontext, bool release)
5874 : : {
5875 : : Datum result;
5876 : :
5877 [ + + ]: 28313 : if (astate->scalarstate)
5878 : : {
5879 : : /* Must use makeMdArrayResult to support "release" parameter */
5880 : : int ndims;
5881 : : int dims[1];
5882 : : int lbs[1];
5883 : :
5884 : : /* If no elements were presented, we want to create an empty array */
5885 : 28289 : ndims = (astate->scalarstate->nelems > 0) ? 1 : 0;
5886 : 28289 : dims[0] = astate->scalarstate->nelems;
5887 : 28289 : lbs[0] = 1;
5888 : :
5889 : 28289 : result = makeMdArrayResult(astate->scalarstate, ndims, dims, lbs,
5890 : : rcontext, release);
5891 : : }
5892 : : else
5893 : : {
5894 : 24 : result = makeArrayResultArr(astate->arraystate,
5895 : : rcontext, release);
5896 : : }
5897 : 28313 : return result;
5898 : : }
5899 : :
5900 : :
5901 : : Datum
7685 neilc@samurai.com 5902 : 144 : array_larger(PG_FUNCTION_ARGS)
5903 : : {
3958 tgl@sss.pgh.pa.us 5904 [ + + ]: 144 : if (array_cmp(fcinfo) > 0)
5905 : 72 : PG_RETURN_DATUM(PG_GETARG_DATUM(0));
5906 : : else
5907 : 69 : PG_RETURN_DATUM(PG_GETARG_DATUM(1));
5908 : : }
5909 : :
5910 : : Datum
7685 neilc@samurai.com 5911 : 129 : array_smaller(PG_FUNCTION_ARGS)
5912 : : {
3958 tgl@sss.pgh.pa.us 5913 [ + + ]: 129 : if (array_cmp(fcinfo) < 0)
5914 : 87 : PG_RETURN_DATUM(PG_GETARG_DATUM(0));
5915 : : else
5916 : 42 : PG_RETURN_DATUM(PG_GETARG_DATUM(1));
5917 : : }
5918 : :
5919 : :
5920 : : typedef struct generate_subscripts_fctx
5921 : : {
5922 : : int32 lower;
5923 : : int32 upper;
5924 : : bool reverse;
5925 : : } generate_subscripts_fctx;
5926 : :
5927 : : /*
5928 : : * generate_subscripts(array anyarray, dim int [, reverse bool])
5929 : : * Returns all subscripts of the array for any dimension
5930 : : */
5931 : : Datum
6530 alvherre@alvh.no-ip. 5932 : 2453 : generate_subscripts(PG_FUNCTION_ARGS)
5933 : : {
5934 : : FuncCallContext *funcctx;
5935 : : MemoryContext oldcontext;
5936 : : generate_subscripts_fctx *fctx;
5937 : :
5938 : : /* stuff done only on the first call of the function */
5939 [ + + ]: 2453 : if (SRF_IS_FIRSTCALL())
5940 : : {
3100 tgl@sss.pgh.pa.us 5941 : 1144 : AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
6121 bruce@momjian.us 5942 : 1144 : int reqdim = PG_GETARG_INT32(1);
5943 : : int *lb,
5944 : : *dimv;
5945 : :
5946 : : /* create a function context for cross-call persistence */
6530 alvherre@alvh.no-ip. 5947 : 1144 : funcctx = SRF_FIRSTCALL_INIT();
5948 : :
5949 : : /* Sanity check: does it look like an array at all? */
3958 tgl@sss.pgh.pa.us 5950 [ - + - - : 1144 : if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
+ + - + -
- - + ]
6530 alvherre@alvh.no-ip. 5951 : 3 : SRF_RETURN_DONE(funcctx);
5952 : :
5953 : : /* Sanity check: was the requested dim valid */
3958 tgl@sss.pgh.pa.us 5954 [ + - - + : 1141 : if (reqdim <= 0 || reqdim > AARR_NDIM(v))
- + ]
6530 alvherre@alvh.no-ip. 5955 :UBC 0 : SRF_RETURN_DONE(funcctx);
5956 : :
5957 : : /*
5958 : : * switch to memory context appropriate for multiple function calls
5959 : : */
6530 alvherre@alvh.no-ip. 5960 :CBC 1141 : oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
95 michael@paquier.xyz 5961 :GNC 1141 : fctx = palloc_object(generate_subscripts_fctx);
5962 : :
3958 tgl@sss.pgh.pa.us 5963 [ - + ]:CBC 1141 : lb = AARR_LBOUND(v);
5964 [ - + ]: 1141 : dimv = AARR_DIMS(v);
5965 : :
6530 alvherre@alvh.no-ip. 5966 : 1141 : fctx->lower = lb[reqdim - 1];
5967 : 1141 : fctx->upper = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
5968 [ - + - - ]: 1141 : fctx->reverse = (PG_NARGS() < 3) ? false : PG_GETARG_BOOL(2);
5969 : :
5970 : 1141 : funcctx->user_fctx = fctx;
5971 : :
5972 : 1141 : MemoryContextSwitchTo(oldcontext);
5973 : : }
5974 : :
5975 : 2450 : funcctx = SRF_PERCALL_SETUP();
5976 : :
5977 : 2450 : fctx = funcctx->user_fctx;
5978 : :
5979 [ + + ]: 2450 : if (fctx->lower <= fctx->upper)
5980 : : {
5981 [ + - ]: 1309 : if (!fctx->reverse)
5982 : 1309 : SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->lower++));
5983 : : else
6530 alvherre@alvh.no-ip. 5984 :UBC 0 : SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->upper--));
5985 : : }
5986 : : else
5987 : : /* done when there are no more elements left */
6530 alvherre@alvh.no-ip. 5988 :CBC 1141 : SRF_RETURN_DONE(funcctx);
5989 : : }
5990 : :
5991 : : /*
5992 : : * generate_subscripts_nodir
5993 : : * Implements the 2-argument version of generate_subscripts
5994 : : */
5995 : : Datum
5996 : 2453 : generate_subscripts_nodir(PG_FUNCTION_ARGS)
5997 : : {
5998 : : /* just call the other one -- it can handle both cases */
5999 : 2453 : return generate_subscripts(fcinfo);
6000 : : }
6001 : :
6002 : : /*
6003 : : * array_fill_with_lower_bounds
6004 : : * Create and fill array with defined lower bounds.
6005 : : */
6006 : : Datum
6451 bruce@momjian.us 6007 : 33 : array_fill_with_lower_bounds(PG_FUNCTION_ARGS)
6008 : : {
6009 : : ArrayType *dims;
6010 : : ArrayType *lbs;
6011 : : ArrayType *result;
6012 : : Oid elmtype;
6013 : : Datum value;
6014 : : bool isnull;
6015 : :
6016 [ + + + + ]: 33 : if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
6446 tgl@sss.pgh.pa.us 6017 [ + - ]: 6 : ereport(ERROR,
6018 : : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
6019 : : errmsg("dimension array or low bound array cannot be null")));
6020 : :
6451 bruce@momjian.us 6021 : 27 : dims = PG_GETARG_ARRAYTYPE_P(1);
6121 6022 : 27 : lbs = PG_GETARG_ARRAYTYPE_P(2);
6023 : :
6451 6024 [ + + ]: 27 : if (!PG_ARGISNULL(0))
6025 : : {
6026 : 21 : value = PG_GETARG_DATUM(0);
6027 : 21 : isnull = false;
6028 : : }
6029 : : else
6030 : : {
6031 : 6 : value = 0;
6032 : 6 : isnull = true;
6033 : : }
6034 : :
6446 tgl@sss.pgh.pa.us 6035 : 27 : elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
6036 [ - + ]: 27 : if (!OidIsValid(elmtype))
6446 tgl@sss.pgh.pa.us 6037 [ # # ]:UBC 0 : elog(ERROR, "could not determine data type of input");
6038 : :
6446 tgl@sss.pgh.pa.us 6039 :CBC 27 : result = array_fill_internal(dims, lbs, value, isnull, elmtype, fcinfo);
6451 bruce@momjian.us 6040 : 21 : PG_RETURN_ARRAYTYPE_P(result);
6041 : : }
6042 : :
6043 : : /*
6044 : : * array_fill
6045 : : * Create and fill array with default lower bounds.
6046 : : */
6047 : : Datum
6048 : 45 : array_fill(PG_FUNCTION_ARGS)
6049 : : {
6050 : : ArrayType *dims;
6051 : : ArrayType *result;
6052 : : Oid elmtype;
6053 : : Datum value;
6054 : : bool isnull;
6055 : :
6056 [ - + ]: 45 : if (PG_ARGISNULL(1))
6446 tgl@sss.pgh.pa.us 6057 [ # # ]:UBC 0 : ereport(ERROR,
6058 : : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
6059 : : errmsg("dimension array or low bound array cannot be null")));
6060 : :
6451 bruce@momjian.us 6061 :CBC 45 : dims = PG_GETARG_ARRAYTYPE_P(1);
6062 : :
6063 [ + + ]: 45 : if (!PG_ARGISNULL(0))
6064 : : {
6065 : 39 : value = PG_GETARG_DATUM(0);
6066 : 39 : isnull = false;
6067 : : }
6068 : : else
6069 : : {
6070 : 6 : value = 0;
6071 : 6 : isnull = true;
6072 : : }
6073 : :
6446 tgl@sss.pgh.pa.us 6074 : 45 : elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
6075 [ - + ]: 45 : if (!OidIsValid(elmtype))
6446 tgl@sss.pgh.pa.us 6076 [ # # ]:UBC 0 : elog(ERROR, "could not determine data type of input");
6077 : :
6446 tgl@sss.pgh.pa.us 6078 :CBC 45 : result = array_fill_internal(dims, NULL, value, isnull, elmtype, fcinfo);
6451 bruce@momjian.us 6079 : 39 : PG_RETURN_ARRAYTYPE_P(result);
6080 : : }
6081 : :
6082 : : static ArrayType *
6083 : 33 : create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
6084 : : Oid elmtype, int dataoffset)
6085 : : {
6086 : : ArrayType *result;
6087 : :
6088 : 33 : result = (ArrayType *) palloc0(nbytes);
6089 : 33 : SET_VARSIZE(result, nbytes);
6090 : 33 : result->ndim = ndims;
6091 : 33 : result->dataoffset = dataoffset;
6092 : 33 : result->elemtype = elmtype;
6093 : 33 : memcpy(ARR_DIMS(result), dimv, ndims * sizeof(int));
6094 : 33 : memcpy(ARR_LBOUND(result), lbsv, ndims * sizeof(int));
6095 : :
6096 : 33 : return result;
6097 : : }
6098 : :
6099 : : static ArrayType *
6446 tgl@sss.pgh.pa.us 6100 : 72 : array_fill_internal(ArrayType *dims, ArrayType *lbs,
6101 : : Datum value, bool isnull, Oid elmtype,
6102 : : FunctionCallInfo fcinfo)
6103 : : {
6104 : : ArrayType *result;
6105 : : int *dimv;
6106 : : int *lbsv;
6107 : : int ndims;
6108 : : int nitems;
6109 : : int deflbs[MAXDIM];
6110 : : int16 elmlen;
6111 : : bool elmbyval;
6112 : : char elmalign;
6113 : : uint8 elmalignby;
6114 : : ArrayMetaState *my_extra;
6115 : :
6116 : : /*
6117 : : * Params checks
6118 : : */
3356 6119 [ + + ]: 72 : if (ARR_NDIM(dims) > 1)
6451 bruce@momjian.us 6120 [ + - ]: 3 : ereport(ERROR,
6121 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
6122 : : errmsg("wrong number of array subscripts"),
6123 : : errdetail("Dimension array must be one dimensional.")));
6124 : :
5544 tgl@sss.pgh.pa.us 6125 [ + + ]: 69 : if (array_contains_nulls(dims))
6446 6126 [ + - ]: 3 : ereport(ERROR,
6127 : : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
6128 : : errmsg("dimension values cannot be null")));
6129 : :
6451 bruce@momjian.us 6130 [ - + ]: 66 : dimv = (int *) ARR_DATA_PTR(dims);
3356 tgl@sss.pgh.pa.us 6131 [ + + ]: 66 : ndims = (ARR_NDIM(dims) > 0) ? ARR_DIMS(dims)[0] : 0;
6132 : :
6451 bruce@momjian.us 6133 [ - + ]: 66 : if (ndims < 0) /* we do allow zero-dimension arrays */
6451 bruce@momjian.us 6134 [ # # ]:UBC 0 : ereport(ERROR,
6135 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
6136 : : errmsg("invalid number of dimensions: %d", ndims)));
6451 bruce@momjian.us 6137 [ - + ]:CBC 66 : if (ndims > MAXDIM)
6451 bruce@momjian.us 6138 [ # # ]:UBC 0 : ereport(ERROR,
6139 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
6140 : : errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
6141 : : ndims, MAXDIM)));
6142 : :
6451 bruce@momjian.us 6143 [ + + ]:CBC 66 : if (lbs != NULL)
6144 : : {
3356 tgl@sss.pgh.pa.us 6145 [ - + ]: 27 : if (ARR_NDIM(lbs) > 1)
6451 bruce@momjian.us 6146 [ # # ]:UBC 0 : ereport(ERROR,
6147 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
6148 : : errmsg("wrong number of array subscripts"),
6149 : : errdetail("Dimension array must be one dimensional.")));
6150 : :
5544 tgl@sss.pgh.pa.us 6151 [ - + ]:CBC 27 : if (array_contains_nulls(lbs))
6446 tgl@sss.pgh.pa.us 6152 [ # # ]:UBC 0 : ereport(ERROR,
6153 : : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
6154 : : errmsg("dimension values cannot be null")));
6155 : :
3356 tgl@sss.pgh.pa.us 6156 [ + + + + ]:CBC 27 : if (ndims != ((ARR_NDIM(lbs) > 0) ? ARR_DIMS(lbs)[0] : 0))
6451 bruce@momjian.us 6157 [ + - ]: 6 : ereport(ERROR,
6158 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
6159 : : errmsg("wrong number of array subscripts"),
6160 : : errdetail("Low bound array has different size than dimensions array.")));
6161 : :
6162 [ - + ]: 21 : lbsv = (int *) ARR_DATA_PTR(lbs);
6163 : : }
6164 : : else
6165 : : {
6166 : : int i;
6167 : :
6168 [ + + ]: 273 : for (i = 0; i < MAXDIM; i++)
6169 : 234 : deflbs[i] = 1;
6170 : :
6171 : 39 : lbsv = deflbs;
6172 : : }
6173 : :
6174 : : /* This checks for overflow of the array dimensions */
3356 tgl@sss.pgh.pa.us 6175 : 60 : nitems = ArrayGetNItems(ndims, dimv);
1770 6176 : 60 : ArrayCheckBounds(ndims, dimv, lbsv);
6177 : :
6178 : : /* fast track for empty array */
3356 6179 [ + + ]: 60 : if (nitems <= 0)
6451 bruce@momjian.us 6180 : 27 : return construct_empty_array(elmtype);
6181 : :
6182 : : /*
6183 : : * We arrange to look up info about element type only once per series of
6184 : : * calls, assuming the element type doesn't change underneath us.
6185 : : */
6186 : 33 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
6187 [ + - ]: 33 : if (my_extra == NULL)
6188 : : {
6189 : 33 : fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
6190 : : sizeof(ArrayMetaState));
6191 : 33 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
6192 : 33 : my_extra->element_type = InvalidOid;
6193 : : }
6194 : :
6195 [ + - ]: 33 : if (my_extra->element_type != elmtype)
6196 : : {
6197 : : /* Get info about element type */
6198 : 33 : get_typlenbyvalalign(elmtype,
6199 : : &my_extra->typlen,
6200 : : &my_extra->typbyval,
6201 : : &my_extra->typalign);
6202 : 33 : my_extra->element_type = elmtype;
6203 : : }
6204 : :
6205 : 33 : elmlen = my_extra->typlen;
6206 : 33 : elmbyval = my_extra->typbyval;
6207 : 33 : elmalign = my_extra->typalign;
41 tgl@sss.pgh.pa.us 6208 :GNC 33 : elmalignby = typalign_to_alignby(elmalign);
6209 : :
6210 : : /* compute required space */
6451 bruce@momjian.us 6211 [ + + ]:CBC 33 : if (!isnull)
6212 : : {
6213 : : int i;
6214 : : char *p;
6215 : : int nbytes;
6216 : : int totbytes;
6217 : :
6218 : : /* make sure data is not toasted */
6219 [ + + ]: 21 : if (elmlen == -1)
6220 : 6 : value = PointerGetDatum(PG_DETOAST_DATUM(value));
6221 : :
6222 [ + + + - : 21 : nbytes = att_addlength_datum(0, elmlen, value);
- - - - -
- - - - +
- - ]
41 tgl@sss.pgh.pa.us 6223 :GNC 21 : nbytes = att_nominal_alignby(nbytes, elmalignby);
6446 tgl@sss.pgh.pa.us 6224 [ - + ]:CBC 21 : Assert(nbytes > 0);
6225 : :
6226 : 21 : totbytes = nbytes * nitems;
6227 : :
6228 : : /* check for overflow of multiplication or total request */
6229 [ + - ]: 21 : if (totbytes / nbytes != nitems ||
6230 [ - + ]: 21 : !AllocSizeIsValid(totbytes))
6451 bruce@momjian.us 6231 [ # # ]:UBC 0 : ereport(ERROR,
6232 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
6233 : : errmsg("array size exceeds the maximum allowed (%zu)",
6234 : : MaxAllocSize)));
6235 : :
6236 : : /*
6237 : : * This addition can't overflow, but it might cause us to go past
6238 : : * MaxAllocSize. We leave it to palloc to complain in that case.
6239 : : */
6446 tgl@sss.pgh.pa.us 6240 :CBC 21 : totbytes += ARR_OVERHEAD_NONULLS(ndims);
6241 : :
6242 : 21 : result = create_array_envelope(ndims, dimv, lbsv, totbytes,
6243 : : elmtype, 0);
6244 : :
6451 bruce@momjian.us 6245 [ - + ]: 21 : p = ARR_DATA_PTR(result);
6246 [ + + ]: 1500153 : for (i = 0; i < nitems; i++)
41 tgl@sss.pgh.pa.us 6247 :GNC 1500132 : p += ArrayCastAndSet(value, elmlen, elmbyval, elmalignby, p);
6248 : : }
6249 : : else
6250 : : {
6251 : : int nbytes;
6252 : : int dataoffset;
6253 : :
6451 bruce@momjian.us 6254 :CBC 12 : dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
6255 : 12 : nbytes = dataoffset;
6256 : :
6257 : 12 : result = create_array_envelope(ndims, dimv, lbsv, nbytes,
6258 : : elmtype, dataoffset);
6259 : :
6260 : : /* create_array_envelope already zeroed the bitmap, so we're done */
6261 : : }
6262 : :
6263 : 33 : return result;
6264 : : }
6265 : :
6266 : :
6267 : : /*
6268 : : * UNNEST
6269 : : */
6270 : : Datum
6330 tgl@sss.pgh.pa.us 6271 : 271400 : array_unnest(PG_FUNCTION_ARGS)
6272 : : {
6273 : : typedef struct
6274 : : {
6275 : : array_iter iter;
6276 : : int nextelem;
6277 : : int numelems;
6278 : : } array_unnest_fctx;
6279 : :
6280 : : FuncCallContext *funcctx;
6281 : : array_unnest_fctx *fctx;
6282 : : MemoryContext oldcontext;
6283 : :
6284 : : /* stuff done only on the first call of the function */
6285 [ + + ]: 271400 : if (SRF_IS_FIRSTCALL())
6286 : : {
6287 : : AnyArrayType *arr;
6288 : : int16 elmlen;
6289 : : bool elmbyval;
6290 : : char elmalign;
6291 : :
6292 : : /* create a function context for cross-call persistence */
6293 : 44480 : funcctx = SRF_FIRSTCALL_INIT();
6294 : :
6295 : : /*
6296 : : * switch to memory context appropriate for multiple function calls
6297 : : */
6298 : 44480 : oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
6299 : :
6300 : : /*
6301 : : * Get the array value and detoast if needed. We can't do this
6302 : : * earlier because if we have to detoast, we want the detoasted copy
6303 : : * to be in multi_call_memory_ctx, so it will go away when we're done
6304 : : * and not before. (If no detoast happens, we assume the originally
6305 : : * passed array will stick around till then.)
6306 : : */
3100 6307 : 44480 : arr = PG_GETARG_ANY_ARRAY_P(0);
6308 : :
6309 : : /* allocate memory for user context */
95 michael@paquier.xyz 6310 :GNC 44480 : fctx = palloc_object(array_unnest_fctx);
6311 : :
6312 : : /* get element-type data */
3958 tgl@sss.pgh.pa.us 6313 [ + + ]:CBC 44480 : if (VARATT_IS_EXPANDED_HEADER(arr))
6314 : : {
6315 : : /* we can just grab the type data from expanded array */
41 tgl@sss.pgh.pa.us 6316 :GNC 1 : elmlen = arr->xpn.typlen;
6317 : 1 : elmbyval = arr->xpn.typbyval;
6318 : 1 : elmalign = arr->xpn.typalign;
6319 : : }
6320 : : else
3958 tgl@sss.pgh.pa.us 6321 [ - + ]:CBC 44479 : get_typlenbyvalalign(AARR_ELEMTYPE(arr),
6322 : : &elmlen,
6323 : : &elmbyval,
6324 : : &elmalign);
6325 : :
6326 : : /* initialize state */
41 tgl@sss.pgh.pa.us 6327 :GNC 44480 : array_iter_setup(&fctx->iter, arr, elmlen, elmbyval, elmalign);
6328 : 44480 : fctx->nextelem = 0;
6329 [ + + + + ]: 44480 : fctx->numelems = ArrayGetNItems(AARR_NDIM(arr), AARR_DIMS(arr));
6330 : :
6330 tgl@sss.pgh.pa.us 6331 :CBC 44480 : funcctx->user_fctx = fctx;
6332 : 44480 : MemoryContextSwitchTo(oldcontext);
6333 : : }
6334 : :
6335 : : /* stuff done on every call of the function */
6336 : 271400 : funcctx = SRF_PERCALL_SETUP();
6337 : 271400 : fctx = funcctx->user_fctx;
6338 : :
6339 [ + + ]: 271400 : if (fctx->nextelem < fctx->numelems)
6340 : : {
6121 bruce@momjian.us 6341 : 226920 : int offset = fctx->nextelem++;
6342 : : Datum elem;
6343 : :
41 tgl@sss.pgh.pa.us 6344 :GNC 226920 : elem = array_iter_next(&fctx->iter, &fcinfo->isnull, offset);
6345 : :
6330 tgl@sss.pgh.pa.us 6346 :CBC 226920 : SRF_RETURN_NEXT(funcctx, elem);
6347 : : }
6348 : : else
6349 : : {
6350 : : /* do when there is no more left */
6351 : 44480 : SRF_RETURN_DONE(funcctx);
6352 : : }
6353 : : }
6354 : :
6355 : : /*
6356 : : * Planner support function for array_unnest(anyarray)
6357 : : *
6358 : : * Note: this is now also used for information_schema._pg_expandarray(),
6359 : : * which is simply a wrapper around array_unnest().
6360 : : */
6361 : : Datum
2591 6362 : 15710 : array_unnest_support(PG_FUNCTION_ARGS)
6363 : : {
6364 : 15710 : Node *rawreq = (Node *) PG_GETARG_POINTER(0);
6365 : 15710 : Node *ret = NULL;
6366 : :
6367 [ + + ]: 15710 : if (IsA(rawreq, SupportRequestRows))
6368 : : {
6369 : : /* Try to estimate the number of rows returned */
6370 : 4314 : SupportRequestRows *req = (SupportRequestRows *) rawreq;
6371 : :
6372 [ + + ]: 4314 : if (is_funcclause(req->node)) /* be paranoid */
6373 : : {
6374 : 4311 : List *args = ((FuncExpr *) req->node)->args;
6375 : : Node *arg1;
6376 : :
6377 : : /* We can use estimated argument values here */
6378 : 4311 : arg1 = estimate_expression_value(req->root, linitial(args));
6379 : :
801 6380 : 4311 : req->rows = estimate_array_length(req->root, arg1);
2591 6381 : 4311 : ret = (Node *) req;
6382 : : }
6383 : : }
6384 : :
6385 : 15710 : PG_RETURN_POINTER(ret);
6386 : : }
6387 : :
6388 : :
6389 : : /*
6390 : : * array_replace/array_remove support
6391 : : *
6392 : : * Find all array entries matching (not distinct from) search/search_isnull,
6393 : : * and delete them if remove is true, else replace them with
6394 : : * replace/replace_isnull. Comparisons are done using the specified
6395 : : * collation. fcinfo is passed only for caching purposes.
6396 : : */
6397 : : static ArrayType *
4995 6398 : 2177 : array_replace_internal(ArrayType *array,
6399 : : Datum search, bool search_isnull,
6400 : : Datum replace, bool replace_isnull,
6401 : : bool remove, Oid collation,
6402 : : FunctionCallInfo fcinfo)
6403 : : {
2605 andres@anarazel.de 6404 : 2177 : LOCAL_FCINFO(locfcinfo, 2);
6405 : : ArrayType *result;
6406 : : Oid element_type;
6407 : : Datum *values;
6408 : : bool *nulls;
6409 : : int *dim;
6410 : : int ndim;
6411 : : int nitems,
6412 : : nresult;
6413 : : int i;
4995 tgl@sss.pgh.pa.us 6414 : 2177 : int32 nbytes = 0;
6415 : : int32 dataoffset;
6416 : : bool hasnulls;
6417 : : int typlen;
6418 : : bool typbyval;
6419 : : char typalign;
6420 : : uint8 typalignby;
6421 : : char *arraydataptr;
6422 : : bits8 *bitmap;
6423 : : int bitmask;
6424 : 2177 : bool changed = false;
6425 : : TypeCacheEntry *typentry;
6426 : :
6427 : 2177 : element_type = ARR_ELEMTYPE(array);
6428 : 2177 : ndim = ARR_NDIM(array);
6429 : 2177 : dim = ARR_DIMS(array);
6430 : 2177 : nitems = ArrayGetNItems(ndim, dim);
6431 : :
6432 : : /* Return input array unmodified if it is empty */
6433 [ - + ]: 2177 : if (nitems <= 0)
4995 tgl@sss.pgh.pa.us 6434 :UBC 0 : return array;
6435 : :
6436 : : /*
6437 : : * We can't remove elements from multi-dimensional arrays, since the
6438 : : * result might not be rectangular.
6439 : : */
4995 tgl@sss.pgh.pa.us 6440 [ + + + + ]:CBC 2177 : if (remove && ndim > 1)
6441 [ + - ]: 3 : ereport(ERROR,
6442 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6443 : : errmsg("removing elements from multidimensional arrays is not supported")));
6444 : :
6445 : : /*
6446 : : * We arrange to look up the equality function only once per series of
6447 : : * calls, assuming the element type doesn't change underneath us.
6448 : : */
6449 : 2174 : typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
6450 [ + + ]: 2174 : if (typentry == NULL ||
6451 [ - + ]: 1618 : typentry->type_id != element_type)
6452 : : {
6453 : 556 : typentry = lookup_type_cache(element_type,
6454 : : TYPECACHE_EQ_OPR_FINFO);
6455 [ - + ]: 556 : if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
4995 tgl@sss.pgh.pa.us 6456 [ # # ]:UBC 0 : ereport(ERROR,
6457 : : (errcode(ERRCODE_UNDEFINED_FUNCTION),
6458 : : errmsg("could not identify an equality operator for type %s",
6459 : : format_type_be(element_type))));
472 peter@eisentraut.org 6460 :CBC 556 : fcinfo->flinfo->fn_extra = typentry;
6461 : : }
4995 tgl@sss.pgh.pa.us 6462 : 2174 : typlen = typentry->typlen;
6463 : 2174 : typbyval = typentry->typbyval;
6464 : 2174 : typalign = typentry->typalign;
41 tgl@sss.pgh.pa.us 6465 :GNC 2174 : typalignby = typalign_to_alignby(typalign);
6466 : :
6467 : : /*
6468 : : * Detoast values if they are toasted. The replacement value must be
6469 : : * detoasted for insertion into the result array, while detoasting the
6470 : : * search value only once saves cycles.
6471 : : */
4995 tgl@sss.pgh.pa.us 6472 [ + + ]:CBC 2174 : if (typlen == -1)
6473 : : {
6474 [ + + ]: 2153 : if (!search_isnull)
6475 : 2150 : search = PointerGetDatum(PG_DETOAST_DATUM(search));
6476 [ + + ]: 2153 : if (!replace_isnull)
6477 : 6 : replace = PointerGetDatum(PG_DETOAST_DATUM(replace));
6478 : : }
6479 : :
6480 : : /* Prepare to apply the comparison operator */
2605 andres@anarazel.de 6481 : 2174 : InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
6482 : : collation, NULL, NULL);
6483 : :
6484 : : /* Allocate temporary arrays for new values */
4995 tgl@sss.pgh.pa.us 6485 : 2174 : values = (Datum *) palloc(nitems * sizeof(Datum));
6486 : 2174 : nulls = (bool *) palloc(nitems * sizeof(bool));
6487 : :
6488 : : /* Loop over source data */
6489 [ + + ]: 2174 : arraydataptr = ARR_DATA_PTR(array);
6490 [ + + ]: 2174 : bitmap = ARR_NULLBITMAP(array);
6491 : 2174 : bitmask = 1;
6492 : 2174 : hasnulls = false;
6493 : 2174 : nresult = 0;
6494 : :
6495 [ + + ]: 4658 : for (i = 0; i < nitems; i++)
6496 : : {
6497 : : Datum elt;
6498 : : bool isNull;
6499 : : bool oprresult;
6500 : 2484 : bool skip = false;
6501 : :
6502 : : /* Get source element, checking for NULL */
6503 [ + + + + ]: 2484 : if (bitmap && (*bitmap & bitmask) == 0)
6504 : : {
6505 : 18 : isNull = true;
6506 : : /* If searching for NULL, we have a match */
6507 [ + - ]: 18 : if (search_isnull)
6508 : : {
6509 [ + + ]: 18 : if (remove)
6510 : : {
6511 : 6 : skip = true;
6512 : 6 : changed = true;
6513 : : }
6514 [ + + ]: 12 : else if (!replace_isnull)
6515 : : {
6516 : 9 : values[nresult] = replace;
6517 : 9 : isNull = false;
6518 : 9 : changed = true;
6519 : : }
6520 : : }
6521 : : }
6522 : : else
6523 : : {
6524 : 2466 : isNull = false;
6525 : 2466 : elt = fetch_att(arraydataptr, typbyval, typlen);
6526 [ + + + - : 2466 : arraydataptr = att_addlength_datum(arraydataptr, typlen, elt);
- - - - -
- - - - +
- - ]
41 tgl@sss.pgh.pa.us 6527 :GNC 2466 : arraydataptr = (char *) att_nominal_alignby(arraydataptr, typalignby);
6528 : :
4995 tgl@sss.pgh.pa.us 6529 [ + + ]:CBC 2466 : if (search_isnull)
6530 : : {
6531 : : /* no match possible, keep element */
6532 : 27 : values[nresult] = elt;
6533 : : }
6534 : : else
6535 : : {
6536 : : /*
6537 : : * Apply the operator to the element pair; treat NULL as false
6538 : : */
2605 andres@anarazel.de 6539 : 2439 : locfcinfo->args[0].value = elt;
6540 : 2439 : locfcinfo->args[0].isnull = false;
6541 : 2439 : locfcinfo->args[1].value = search;
6542 : 2439 : locfcinfo->args[1].isnull = false;
6543 : 2439 : locfcinfo->isnull = false;
6544 : 2439 : oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
2154 tgl@sss.pgh.pa.us 6545 [ + - + + ]: 2439 : if (locfcinfo->isnull || !oprresult)
6546 : : {
6547 : : /* no match, keep element */
4995 6548 : 2360 : values[nresult] = elt;
6549 : : }
6550 : : else
6551 : : {
6552 : : /* match, so replace or delete */
6553 : 79 : changed = true;
6554 [ + + ]: 79 : if (remove)
6555 : 67 : skip = true;
6556 : : else
6557 : : {
6558 : 12 : values[nresult] = replace;
6559 : 12 : isNull = replace_isnull;
6560 : : }
6561 : : }
6562 : : }
6563 : : }
6564 : :
6565 [ + + ]: 2484 : if (!skip)
6566 : : {
6567 : 2411 : nulls[nresult] = isNull;
6568 [ + + ]: 2411 : if (isNull)
6569 : 6 : hasnulls = true;
6570 : : else
6571 : : {
6572 : : /* Update total result size */
6573 [ + + + - : 2405 : nbytes = att_addlength_datum(nbytes, typlen, values[nresult]);
- - - - -
- - - - +
- - ]
41 tgl@sss.pgh.pa.us 6574 :GNC 2405 : nbytes = att_nominal_alignby(nbytes, typalignby);
6575 : : /* check for overflow of total request */
4995 tgl@sss.pgh.pa.us 6576 [ - + ]:CBC 2405 : if (!AllocSizeIsValid(nbytes))
4995 tgl@sss.pgh.pa.us 6577 [ # # ]:UBC 0 : ereport(ERROR,
6578 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
6579 : : errmsg("array size exceeds the maximum allowed (%zu)",
6580 : : MaxAllocSize)));
6581 : : }
4995 tgl@sss.pgh.pa.us 6582 :CBC 2411 : nresult++;
6583 : : }
6584 : :
6585 : : /* advance bitmap pointer if any */
6586 [ + + ]: 2484 : if (bitmap)
6587 : : {
6588 : 45 : bitmask <<= 1;
6589 [ - + ]: 45 : if (bitmask == 0x100)
6590 : : {
4995 tgl@sss.pgh.pa.us 6591 :UBC 0 : bitmap++;
6592 : 0 : bitmask = 1;
6593 : : }
6594 : : }
6595 : : }
6596 : :
6597 : : /*
6598 : : * If not changed just return the original array
6599 : : */
4995 tgl@sss.pgh.pa.us 6600 [ + + ]:CBC 2174 : if (!changed)
6601 : : {
6602 : 2098 : pfree(values);
6603 : 2098 : pfree(nulls);
6604 : 2098 : return array;
6605 : : }
6606 : :
6607 : : /* If all elements were removed return an empty array */
4671 noah@leadboat.com 6608 [ + + ]: 76 : if (nresult == 0)
6609 : : {
6610 : 3 : pfree(values);
6611 : 3 : pfree(nulls);
6612 : 3 : return construct_empty_array(element_type);
6613 : : }
6614 : :
6615 : : /* Allocate and initialize the result array */
4995 tgl@sss.pgh.pa.us 6616 [ + + ]: 73 : if (hasnulls)
6617 : : {
6618 : 3 : dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nresult);
6619 : 3 : nbytes += dataoffset;
6620 : : }
6621 : : else
6622 : : {
6623 : 70 : dataoffset = 0; /* marker for no null bitmap */
6624 : 70 : nbytes += ARR_OVERHEAD_NONULLS(ndim);
6625 : : }
6626 : 73 : result = (ArrayType *) palloc0(nbytes);
6627 : 73 : SET_VARSIZE(result, nbytes);
6628 : 73 : result->ndim = ndim;
6629 : 73 : result->dataoffset = dataoffset;
6630 : 73 : result->elemtype = element_type;
3958 6631 : 73 : memcpy(ARR_DIMS(result), ARR_DIMS(array), ndim * sizeof(int));
6632 : 73 : memcpy(ARR_LBOUND(result), ARR_LBOUND(array), ndim * sizeof(int));
6633 : :
4995 6634 [ + + ]: 73 : if (remove)
6635 : : {
6636 : : /* Adjust the result length */
6637 : 58 : ARR_DIMS(result)[0] = nresult;
6638 : : }
6639 : :
6640 : : /* Insert data into result array */
6641 : 73 : CopyArrayEls(result,
6642 : : values, nulls, nresult,
6643 : : typlen, typbyval, typalign,
6644 : : false);
6645 : :
6646 : 73 : pfree(values);
6647 : 73 : pfree(nulls);
6648 : :
6649 : 73 : return result;
6650 : : }
6651 : :
6652 : : /*
6653 : : * Remove any occurrences of an element from an array
6654 : : *
6655 : : * If used on a multi-dimensional array this will raise an error.
6656 : : */
6657 : : Datum
6658 : 131729 : array_remove(PG_FUNCTION_ARGS)
6659 : : {
6660 : : ArrayType *array;
6661 : 131729 : Datum search = PG_GETARG_DATUM(1);
6662 : 131729 : bool search_isnull = PG_ARGISNULL(1);
6663 : :
6664 [ + + ]: 131729 : if (PG_ARGISNULL(0))
6665 : 129570 : PG_RETURN_NULL();
6666 : 2159 : array = PG_GETARG_ARRAYTYPE_P(0);
6667 : :
6668 : 2159 : array = array_replace_internal(array,
6669 : : search, search_isnull,
6670 : : (Datum) 0, true,
6671 : : true, PG_GET_COLLATION(),
6672 : : fcinfo);
6673 : 2156 : PG_RETURN_ARRAYTYPE_P(array);
6674 : : }
6675 : :
6676 : : /*
6677 : : * Replace any occurrences of an element in an array
6678 : : */
6679 : : Datum
6680 : 18 : array_replace(PG_FUNCTION_ARGS)
6681 : : {
6682 : : ArrayType *array;
6683 : 18 : Datum search = PG_GETARG_DATUM(1);
6684 : 18 : bool search_isnull = PG_ARGISNULL(1);
6685 : 18 : Datum replace = PG_GETARG_DATUM(2);
6686 : 18 : bool replace_isnull = PG_ARGISNULL(2);
6687 : :
6688 [ - + ]: 18 : if (PG_ARGISNULL(0))
4995 tgl@sss.pgh.pa.us 6689 :UBC 0 : PG_RETURN_NULL();
4995 tgl@sss.pgh.pa.us 6690 :CBC 18 : array = PG_GETARG_ARRAYTYPE_P(0);
6691 : :
6692 : 18 : array = array_replace_internal(array,
6693 : : search, search_isnull,
6694 : : replace, replace_isnull,
6695 : : false, PG_GET_COLLATION(),
6696 : : fcinfo);
6697 : 18 : PG_RETURN_ARRAYTYPE_P(array);
6698 : : }
6699 : :
6700 : : /*
6701 : : * Implements width_bucket(anyelement, anyarray).
6702 : : *
6703 : : * 'thresholds' is an array containing lower bound values for each bucket;
6704 : : * these must be sorted from smallest to largest, or bogus results will be
6705 : : * produced. If N thresholds are supplied, the output is from 0 to N:
6706 : : * 0 is for inputs < first threshold, N is for inputs >= last threshold.
6707 : : */
6708 : : Datum
4205 6709 : 405 : width_bucket_array(PG_FUNCTION_ARGS)
6710 : : {
6711 : 405 : Datum operand = PG_GETARG_DATUM(0);
6712 : 405 : ArrayType *thresholds = PG_GETARG_ARRAYTYPE_P(1);
6713 : 405 : Oid collation = PG_GET_COLLATION();
6714 : 405 : Oid element_type = ARR_ELEMTYPE(thresholds);
6715 : : int result;
6716 : :
6717 : : /* Check input */
6718 [ + + ]: 405 : if (ARR_NDIM(thresholds) > 1)
6719 [ + - ]: 3 : ereport(ERROR,
6720 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
6721 : : errmsg("thresholds must be one-dimensional array")));
6722 : :
6723 [ + + ]: 402 : if (array_contains_nulls(thresholds))
6724 [ + - ]: 3 : ereport(ERROR,
6725 : : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
6726 : : errmsg("thresholds array must not contain NULLs")));
6727 : :
6728 : : /* We have a dedicated implementation for float8 data */
6729 [ + + ]: 399 : if (element_type == FLOAT8OID)
6730 : 183 : result = width_bucket_array_float8(operand, thresholds);
6731 : : else
6732 : : {
6733 : : TypeCacheEntry *typentry;
6734 : :
6735 : : /* Cache information about the input type */
6736 : 216 : typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
6737 [ + + ]: 216 : if (typentry == NULL ||
6738 [ - + ]: 195 : typentry->type_id != element_type)
6739 : : {
6740 : 21 : typentry = lookup_type_cache(element_type,
6741 : : TYPECACHE_CMP_PROC_FINFO);
6742 [ - + ]: 21 : if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
4205 tgl@sss.pgh.pa.us 6743 [ # # ]:UBC 0 : ereport(ERROR,
6744 : : (errcode(ERRCODE_UNDEFINED_FUNCTION),
6745 : : errmsg("could not identify a comparison function for type %s",
6746 : : format_type_be(element_type))));
472 peter@eisentraut.org 6747 :CBC 21 : fcinfo->flinfo->fn_extra = typentry;
6748 : : }
6749 : :
6750 : : /*
6751 : : * We have separate implementation paths for fixed- and variable-width
6752 : : * types, since indexing the array is a lot cheaper in the first case.
6753 : : */
4205 tgl@sss.pgh.pa.us 6754 [ + + ]: 216 : if (typentry->typlen > 0)
6755 : 45 : result = width_bucket_array_fixed(operand, thresholds,
6756 : : collation, typentry);
6757 : : else
6758 : 171 : result = width_bucket_array_variable(operand, thresholds,
6759 : : collation, typentry);
6760 : : }
6761 : :
6762 : : /* Avoid leaking memory when handed toasted input. */
6763 [ - + ]: 399 : PG_FREE_IF_COPY(thresholds, 1);
6764 : :
6765 : 399 : PG_RETURN_INT32(result);
6766 : : }
6767 : :
6768 : : /*
6769 : : * width_bucket_array for float8 data.
6770 : : */
6771 : : static int
6772 : 183 : width_bucket_array_float8(Datum operand, ArrayType *thresholds)
6773 : : {
6774 : 183 : float8 op = DatumGetFloat8(operand);
6775 : : float8 *thresholds_data;
6776 : : int left;
6777 : : int right;
6778 : :
6779 : : /*
6780 : : * Since we know the array contains no NULLs, we can just index it
6781 : : * directly.
6782 : : */
6783 [ - + ]: 183 : thresholds_data = (float8 *) ARR_DATA_PTR(thresholds);
6784 : :
6785 : 183 : left = 0;
6786 : 183 : right = ArrayGetNItems(ARR_NDIM(thresholds), ARR_DIMS(thresholds));
6787 : :
6788 : : /*
6789 : : * If the probe value is a NaN, it's greater than or equal to all possible
6790 : : * threshold values (including other NaNs), so we need not search. Note
6791 : : * that this would give the same result as searching even if the array
6792 : : * contains multiple NaNs (as long as they're correctly sorted), since the
6793 : : * loop logic will find the rightmost of multiple equal threshold values.
6794 : : */
6795 [ + + ]: 183 : if (isnan(op))
6796 : 3 : return right;
6797 : :
6798 : : /* Find the bucket */
6799 [ + + ]: 567 : while (left < right)
6800 : : {
6801 : 387 : int mid = (left + right) / 2;
6802 : :
6803 [ + + + + ]: 387 : if (isnan(thresholds_data[mid]) || op < thresholds_data[mid])
6804 : 168 : right = mid;
6805 : : else
6806 : 219 : left = mid + 1;
6807 : : }
6808 : :
6809 : 180 : return left;
6810 : : }
6811 : :
6812 : : /*
6813 : : * width_bucket_array for generic fixed-width data types.
6814 : : */
6815 : : static int
6816 : 45 : width_bucket_array_fixed(Datum operand,
6817 : : ArrayType *thresholds,
6818 : : Oid collation,
6819 : : TypeCacheEntry *typentry)
6820 : : {
2605 andres@anarazel.de 6821 : 45 : LOCAL_FCINFO(locfcinfo, 2);
6822 : : char *thresholds_data;
4205 tgl@sss.pgh.pa.us 6823 : 45 : int typlen = typentry->typlen;
6824 : 45 : bool typbyval = typentry->typbyval;
6825 : : int left;
6826 : : int right;
6827 : :
6828 : : /*
6829 : : * Since we know the array contains no NULLs, we can just index it
6830 : : * directly.
6831 : : */
6832 [ - + ]: 45 : thresholds_data = (char *) ARR_DATA_PTR(thresholds);
6833 : :
2605 andres@anarazel.de 6834 : 45 : InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
6835 : : collation, NULL, NULL);
6836 : :
6837 : : /* Find the bucket */
4205 tgl@sss.pgh.pa.us 6838 : 45 : left = 0;
6839 : 45 : right = ArrayGetNItems(ARR_NDIM(thresholds), ARR_DIMS(thresholds));
6840 [ + + ]: 135 : while (left < right)
6841 : : {
6842 : 90 : int mid = (left + right) / 2;
6843 : : char *ptr;
6844 : : int32 cmpresult;
6845 : :
6846 : 90 : ptr = thresholds_data + mid * typlen;
6847 : :
2605 andres@anarazel.de 6848 : 90 : locfcinfo->args[0].value = operand;
6849 : 90 : locfcinfo->args[0].isnull = false;
6850 : 90 : locfcinfo->args[1].value = fetch_att(ptr, typbyval, typlen);
6851 : 90 : locfcinfo->args[1].isnull = false;
6852 : :
6853 : 90 : cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
6854 : :
6855 : : /* We don't expect comparison support functions to return null */
2154 tgl@sss.pgh.pa.us 6856 [ - + ]: 90 : Assert(!locfcinfo->isnull);
6857 : :
4205 6858 [ + + ]: 90 : if (cmpresult < 0)
6859 : 45 : right = mid;
6860 : : else
6861 : 45 : left = mid + 1;
6862 : : }
6863 : :
6864 : 45 : return left;
6865 : : }
6866 : :
6867 : : /*
6868 : : * width_bucket_array for generic variable-width data types.
6869 : : */
6870 : : static int
6871 : 171 : width_bucket_array_variable(Datum operand,
6872 : : ArrayType *thresholds,
6873 : : Oid collation,
6874 : : TypeCacheEntry *typentry)
6875 : : {
2605 andres@anarazel.de 6876 : 171 : LOCAL_FCINFO(locfcinfo, 2);
6877 : : char *thresholds_data;
4205 tgl@sss.pgh.pa.us 6878 : 171 : int typlen = typentry->typlen;
6879 : 171 : bool typbyval = typentry->typbyval;
6880 : 171 : char typalign = typentry->typalign;
41 tgl@sss.pgh.pa.us 6881 :GNC 171 : uint8 typalignby = typalign_to_alignby(typalign);
6882 : : int left;
6883 : : int right;
6884 : :
4205 tgl@sss.pgh.pa.us 6885 [ - + ]:CBC 171 : thresholds_data = (char *) ARR_DATA_PTR(thresholds);
6886 : :
2605 andres@anarazel.de 6887 : 171 : InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
6888 : : collation, NULL, NULL);
6889 : :
6890 : : /* Find the bucket */
4205 tgl@sss.pgh.pa.us 6891 : 171 : left = 0;
6892 : 171 : right = ArrayGetNItems(ARR_NDIM(thresholds), ARR_DIMS(thresholds));
6893 [ + + ]: 534 : while (left < right)
6894 : : {
6895 : 363 : int mid = (left + right) / 2;
6896 : : char *ptr;
6897 : : int i;
6898 : : int32 cmpresult;
6899 : :
6900 : : /* Locate mid'th array element by advancing from left element */
6901 : 363 : ptr = thresholds_data;
6902 [ + + ]: 621 : for (i = left; i < mid; i++)
6903 : : {
6904 [ - + + - : 258 : ptr = att_addlength_pointer(ptr, typlen, ptr);
- - - - -
- - - - +
- - ]
41 tgl@sss.pgh.pa.us 6905 :GNC 258 : ptr = (char *) att_nominal_alignby(ptr, typalignby);
6906 : : }
6907 : :
2605 andres@anarazel.de 6908 :CBC 363 : locfcinfo->args[0].value = operand;
6909 : 363 : locfcinfo->args[0].isnull = false;
6910 : 363 : locfcinfo->args[1].value = fetch_att(ptr, typbyval, typlen);
6911 : 363 : locfcinfo->args[1].isnull = false;
6912 : :
6913 : 363 : cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
6914 : :
6915 : : /* We don't expect comparison support functions to return null */
2154 tgl@sss.pgh.pa.us 6916 [ - + ]: 363 : Assert(!locfcinfo->isnull);
6917 : :
4205 6918 [ + + ]: 363 : if (cmpresult < 0)
6919 : 150 : right = mid;
6920 : : else
6921 : : {
6922 : 213 : left = mid + 1;
6923 : :
6924 : : /*
6925 : : * Move the thresholds pointer to match new "left" index, so we
6926 : : * don't have to seek over those elements again. This trick
6927 : : * ensures we do only O(N) array indexing work, not O(N^2).
6928 : : */
6929 [ - + + - : 213 : ptr = att_addlength_pointer(ptr, typlen, ptr);
- - - - -
- - - - +
- - ]
41 tgl@sss.pgh.pa.us 6930 :GNC 213 : thresholds_data = (char *) att_nominal_alignby(ptr, typalignby);
6931 : : }
6932 : : }
6933 : :
4205 tgl@sss.pgh.pa.us 6934 :CBC 171 : return left;
6935 : : }
6936 : :
6937 : : /*
6938 : : * Trim the last N elements from an array by building an appropriate slice.
6939 : : * Only the first dimension is trimmed.
6940 : : */
6941 : : Datum
1838 6942 : 24 : trim_array(PG_FUNCTION_ARGS)
6943 : : {
6944 : 24 : ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
6945 : 24 : int n = PG_GETARG_INT32(1);
1323 6946 [ + + ]: 24 : int array_length = (ARR_NDIM(v) > 0) ? ARR_DIMS(v)[0] : 0;
6947 : : int16 elmlen;
6948 : : bool elmbyval;
6949 : : char elmalign;
6950 : : int lower[MAXDIM];
6951 : : int upper[MAXDIM];
6952 : : bool lowerProvided[MAXDIM];
6953 : : bool upperProvided[MAXDIM];
6954 : : Datum result;
6955 : :
6956 : : /* Per spec, throw an error if out of bounds */
1838 6957 [ + + + + ]: 24 : if (n < 0 || n > array_length)
6958 [ + - ]: 9 : ereport(ERROR,
6959 : : (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
6960 : : errmsg("number of elements to trim must be between 0 and %d",
6961 : : array_length)));
6962 : :
6963 : : /* Set all the bounds as unprovided except the first upper bound */
6964 : 15 : memset(lowerProvided, false, sizeof(lowerProvided));
6965 : 15 : memset(upperProvided, false, sizeof(upperProvided));
1323 6966 [ + - ]: 15 : if (ARR_NDIM(v) > 0)
6967 : : {
6968 : 15 : upper[0] = ARR_LBOUND(v)[0] + array_length - n - 1;
6969 : 15 : upperProvided[0] = true;
6970 : : }
6971 : :
6972 : : /* Fetch the needed information about the element type */
1838 6973 : 15 : get_typlenbyvalalign(ARR_ELEMTYPE(v), &elmlen, &elmbyval, &elmalign);
6974 : :
6975 : : /* Get the slice */
6976 : 15 : result = array_get_slice(PointerGetDatum(v), 1,
6977 : : upper, lower, upperProvided, lowerProvided,
6978 : : -1, elmlen, elmbyval, elmalign);
6979 : :
6980 : 15 : PG_RETURN_DATUM(result);
6981 : : }
|