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