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