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-2025, 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
9216 tgl@sss.pgh.pa.us 179 :CBC 116666 : array_in(PG_FUNCTION_ARGS)
180 : : {
8934 bruce@momjian.us 181 : 116666 : char *string = PG_GETARG_CSTRING(0); /* external form */
2999 tgl@sss.pgh.pa.us 182 : 116666 : Oid element_type = PG_GETARG_OID(1); /* type of an array
183 : : * element */
7266 bruce@momjian.us 184 : 116666 : int32 typmod = PG_GETARG_INT32(2); /* typmod for array elements */
1002 tgl@sss.pgh.pa.us 185 : 116666 : 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 : : */
8107 209 : 116666 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
210 [ + + ]: 116666 : if (my_extra == NULL)
211 : : {
212 : 37149 : fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
213 : : sizeof(ArrayMetaState));
214 : 37149 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
7327 215 : 37149 : my_extra->element_type = ~element_type;
216 : : }
217 : :
8107 218 [ + + ]: 116666 : if (my_extra->element_type != element_type)
219 : : {
220 : : /*
221 : : * Get info about element type, including its input conversion proc
222 : : */
223 : 37166 : 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 : 37166 : fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
228 : 37166 : fcinfo->flinfo->fn_mcxt);
229 : 37166 : my_extra->element_type = element_type;
230 : : }
231 : 116666 : typlen = my_extra->typlen;
232 : 116666 : typbyval = my_extra->typbyval;
233 : 116666 : typalign = my_extra->typalign;
234 : 116666 : typdelim = my_extra->typdelim;
7762 235 : 116666 : 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 : : */
663 242 [ + + ]: 816662 : for (int i = 0; i < MAXDIM; i++)
243 : : {
244 : 699996 : dim[i] = -1; /* indicates "not yet known" */
245 : 699996 : 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 : 116666 : p = string;
255 [ - + ]: 116666 : if (!ReadArrayDimensions(&p, &ndim, dim, lBound, string, escontext))
663 tgl@sss.pgh.pa.us 256 :UBC 0 : return (Datum) 0;
257 : :
10226 bruce@momjian.us 258 [ + + ]:CBC 116645 : if (ndim == 0)
259 : : {
260 : : /* No array dimensions, so next character should be a left brace */
9062 tgl@sss.pgh.pa.us 261 [ + + ]: 116571 : if (*p != '{')
1002 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 */
9176 270 [ - + ]: 74 : if (strncmp(p, ASSGN, strlen(ASSGN)) != 0)
1002 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)));
10226 bruce@momjian.us 276 :CBC 74 : p += strlen(ASSGN);
277 : : /* Allow whitespace after it */
793 michael@paquier.xyz 278 [ - + ]: 74 : while (scanner_isspace(*p))
10226 bruce@momjian.us 279 :UBC 0 : p++;
280 : :
7702 mail@joeconway.com 281 [ - + ]:CBC 74 : if (*p != '{')
1002 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: { ... } */
663 tgl@sss.pgh.pa.us 289 [ + + ]:CBC 116638 : 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 : 21 : return (Datum) 0;
300 : :
301 : : /* only whitespace is allowed after the closing brace */
302 [ + + ]: 116542 : while (*p)
303 : : {
304 [ + - ]: 6 : if (!scanner_isspace(*p++))
1002 305 [ + - ]: 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? */
10226 bruce@momjian.us 312 [ + + ]: 116536 : if (nitems == 0)
7233 tgl@sss.pgh.pa.us 313 : 2194 : PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
314 : :
315 : : /*
316 : : * Check for nulls, compute total data space needed
317 : : */
663 318 : 114342 : hasnulls = false;
319 : 114342 : nbytes = 0;
320 [ + + ]: 720331 : for (int i = 0; i < nitems; i++)
321 : : {
322 [ + + ]: 605989 : if (nulls[i])
323 : 267 : hasnulls = true;
324 : : else
325 : : {
326 : : /* let's just make sure data is not toasted */
327 [ + + ]: 605722 : if (typlen == -1)
328 : 349684 : values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
329 [ + + + - : 605722 : nbytes = att_addlength_datum(nbytes, typlen, values[i]);
- - - - -
- - - - +
- - ]
330 [ + + + + : 605722 : nbytes = att_align_nominal(nbytes, typalign);
+ + - + ]
331 : : /* check for overflow of total request */
332 [ - + ]: 605722 : if (!AllocSizeIsValid(nbytes))
663 tgl@sss.pgh.pa.us 333 [ # # ]:UBC 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 : : }
7233 tgl@sss.pgh.pa.us 339 [ + + ]:CBC 114342 : if (hasnulls)
340 : : {
341 : 236 : dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
342 : 236 : nbytes += dataoffset;
343 : : }
344 : : else
345 : : {
346 : 114106 : dataoffset = 0; /* marker for no null bitmap */
347 : 114106 : nbytes += ARR_OVERHEAD_NONULLS(ndim);
348 : : }
349 : :
350 : : /*
351 : : * Construct the final array datum
352 : : */
6357 353 : 114342 : retval = (ArrayType *) palloc0(nbytes);
6766 354 : 114342 : SET_VARSIZE(retval, nbytes);
9177 355 : 114342 : retval->ndim = ndim;
7233 356 : 114342 : 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 : : */
8412 363 : 114342 : retval->elemtype = element_type;
7913 neilc@samurai.com 364 : 114342 : memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
365 : 114342 : memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
366 : :
7233 tgl@sss.pgh.pa.us 367 : 114342 : CopyArrayEls(retval,
368 : : values, nulls, nitems,
369 : : typlen, typbyval, typalign,
370 : : true);
371 : :
663 372 : 114342 : pfree(values);
373 : 114342 : pfree(nulls);
374 : :
9182 375 : 114342 : 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
663 402 : 116666 : ReadArrayDimensions(char **srcptr, int *ndim_p, int *dim, int *lBound,
403 : : const char *origStr, Node *escontext)
404 : : {
405 : 116666 : 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 : 116666 : ndim = 0;
413 : : for (;;)
10226 bruce@momjian.us 414 : 102 : {
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 : : */
663 tgl@sss.pgh.pa.us 423 [ + + ]: 116774 : while (scanner_isspace(*p))
424 : 6 : p++;
425 [ + + ]: 116768 : if (*p != '[')
426 : 116645 : break; /* no more dimension items */
427 : 123 : p++;
428 [ - + ]: 123 : if (ndim >= MAXDIM)
663 tgl@sss.pgh.pa.us 429 [ # # ]:UBC 0 : ereturn(escontext, false,
430 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
431 : : errmsg("number of array dimensions exceeds the maximum allowed (%d)",
432 : : MAXDIM)));
433 : :
663 tgl@sss.pgh.pa.us 434 :CBC 123 : q = p;
435 [ - + ]: 123 : if (!ReadDimensionInt(&p, &i, origStr, escontext))
663 tgl@sss.pgh.pa.us 436 :UBC 0 : return false;
663 tgl@sss.pgh.pa.us 437 [ + + ]:CBC 117 : 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 [ + + ]: 114 : if (*p == ':')
444 : : {
445 : : /* [m:n] format */
446 : 108 : lBound[ndim] = i;
447 : 108 : p++;
448 : 108 : q = p;
449 [ - + ]: 108 : if (!ReadDimensionInt(&p, &ub, origStr, escontext))
663 tgl@sss.pgh.pa.us 450 :UBC 0 : return false;
663 tgl@sss.pgh.pa.us 451 [ + + ]:CBC 108 : 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 [ - + ]: 111 : if (*p != ']')
663 tgl@sss.pgh.pa.us 464 [ # # ]:UBC 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 : : "]")));
663 tgl@sss.pgh.pa.us 469 :CBC 111 : 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 [ + + ]: 111 : 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 [ + + ]: 105 : 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 [ + - - + ]: 204 : if (pg_sub_s32_overflow(ub, lBound[ndim], &ub) ||
491 : 102 : pg_add_s32_overflow(ub, 1, &ub))
663 tgl@sss.pgh.pa.us 492 [ # # ]:UBC 0 : ereturn(escontext, false,
493 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
494 : : errmsg("array size exceeds the maximum allowed (%d)",
495 : : (int) MaxArraySize)));
496 : :
663 tgl@sss.pgh.pa.us 497 :CBC 102 : dim[ndim] = ub;
498 : 102 : ndim++;
499 : : }
500 : :
501 : 116645 : *srcptr = p;
502 : 116645 : *ndim_p = ndim;
503 : 116645 : 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 : 231 : ReadDimensionInt(char **srcptr, int *result,
520 : : const char *origStr, Node *escontext)
521 : : {
522 : 231 : char *p = *srcptr;
523 : : long l;
524 : :
525 : : /* don't accept leading whitespace */
526 [ + + + + : 231 : if (!isdigit((unsigned char) *p) && *p != '-' && *p != '+')
+ - ]
527 : : {
528 : 6 : *result = 0;
529 : 6 : return true;
530 : : }
531 : :
532 : 225 : errno = 0;
533 : 225 : l = strtol(p, srcptr, 10);
534 : :
535 [ + - + + : 225 : 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 : 219 : *result = (int) l;
541 : 219 : 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 : 116638 : 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 : 116638 : int ndim = *ndim_p;
596 : 116638 : 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 : 116638 : maxitems = 16;
609 : 116638 : values = palloc_array(Datum, maxitems);
610 : 116638 : nulls = palloc_array(bool, maxitems);
611 : :
612 : : /* Allocate workspace to hold (string representation of) one element */
613 : 116638 : initStringInfo(&elembuf);
614 : :
615 : : /* Loop below assumes first token is ATOK_LEVEL_START */
616 [ - + ]: 116638 : Assert(**srcptr == '{');
617 : :
618 : : /* Parse tokens until we reach the matching right brace */
619 : 116638 : nest_level = 0;
620 : 116638 : nitems = 0;
621 : 116638 : ndim_frozen = dimensions_specified;
622 : 116638 : expect_delim = false;
623 : : do
624 : : {
625 : : ArrayToken tok;
626 : :
627 : 1332469 : tok = ReadArrayToken(srcptr, &elembuf, typdelim, origStr, escontext);
628 : :
629 [ + + + + : 1332454 : switch (tok)
+ - ]
630 : : {
631 : 117383 : case ATOK_LEVEL_START:
632 : : /* Can't write left brace where delim is expected */
633 [ + + ]: 117383 : if (expect_delim)
1002 634 [ + - ]: 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 */
663 640 [ + + ]: 117380 : 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 : 117379 : nelems[nest_level] = 0;
647 : 117379 : nest_level++;
648 [ + + ]: 117379 : if (nest_level > ndim)
649 : : {
650 : : /* Can't increase ndim once it's frozen */
651 [ + + ]: 116832 : if (ndim_frozen)
652 : 6 : goto dimension_error;
653 : 116826 : ndim = nest_level;
654 : : }
655 : 117373 : break;
656 : :
657 : 117265 : case ATOK_LEVEL_END:
658 : : /* Can't get here with nest_level == 0 */
659 [ - + ]: 117265 : 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 [ + + + + ]: 117265 : 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 : 117253 : nest_level--;
672 : : /* Nested sub-arrays count as elements of outer level */
673 [ + + ]: 117253 : if (nest_level > 0)
674 : 708 : 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 [ + + ]: 117253 : if (dim[nest_level] < 0)
682 : : {
683 : : /* Save length of first sub-array of this level */
684 : 116712 : dim[nest_level] = nelems[nest_level];
685 : : }
686 [ + + ]: 541 : 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 : 117237 : expect_delim = true;
697 : 117237 : break;
698 : :
699 : 491713 : case ATOK_DELIM:
700 [ + + ]: 491713 : 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 : 491710 : expect_delim = false;
707 : 491710 : break;
708 : :
709 : 606090 : case ATOK_ELEM:
710 : : case ATOK_ELEM_NULL:
711 : : /* Can't get here with nest_level == 0 */
712 [ - + ]: 606090 : Assert(nest_level > 0);
713 : :
714 : : /* Disallow consecutive ELEM tokens */
715 [ + + ]: 606090 : 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 [ + + ]: 606087 : if (nitems >= maxitems)
723 : : {
724 [ - + ]: 3184 : if (maxitems >= MaxArraySize)
1002 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)));
663 tgl@sss.pgh.pa.us 729 [ + - ]:CBC 3184 : maxitems = Min(maxitems * 2, MaxArraySize);
730 : 3184 : values = repalloc_array(values, Datum, maxitems);
731 : 3184 : nulls = repalloc_array(nulls, bool, maxitems);
732 : : }
733 : :
734 : : /* Read the element's value, or check that NULL is allowed */
735 [ + + ]: 606087 : if (!InputFunctionCallSafe(inputproc,
736 : : (tok == ATOK_ELEM_NULL) ? NULL : elembuf.data,
737 : : typioparam, typmod,
738 : : escontext,
739 [ + + ]: 606087 : &values[nitems]))
740 : 18 : return false;
741 : 606059 : nulls[nitems] = (tok == ATOK_ELEM_NULL);
742 : 606059 : 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 : 606059 : ndim_frozen = true;
750 [ + + ]: 606059 : if (nest_level != ndim)
751 : 6 : goto dimension_error;
752 : : /* Count the new element */
753 : 606053 : nelems[nest_level - 1]++;
754 : :
755 : : /* Must have a delim or a right brace following */
756 : 606053 : expect_delim = true;
757 : 606053 : break;
758 : :
759 : 3 : case ATOK_ERROR:
760 : 3 : return false;
761 : : }
762 [ + + ]: 1332373 : } while (nest_level > 0);
763 : :
764 : : /* Clean up and return results */
765 : 116542 : pfree(elembuf.data);
766 : :
767 : 116542 : *ndim_p = ndim;
768 : 116542 : *nitems_p = nitems;
769 : 116542 : *values_p = values;
770 : 116542 : *nulls_p = nulls;
771 : 116542 : return true;
772 : :
773 : 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 : 1332469 : ReadArrayToken(char **srcptr, StringInfo elembuf, char typdelim,
797 : : const char *origStr, Node *escontext)
798 : : {
799 : 1332469 : char *p = *srcptr;
800 : : int dstlen;
801 : : bool has_escapes;
802 : :
803 : 1332469 : resetStringInfo(elembuf);
804 : :
805 : : /* Identify token type. Loop advances over leading whitespace. */
806 : : for (;;)
807 : : {
808 [ - + + + : 1344739 : switch (*p)
+ ]
809 : : {
663 tgl@sss.pgh.pa.us 810 :UBC 0 : case '\0':
811 : 0 : goto ending_error;
663 tgl@sss.pgh.pa.us 812 :CBC 117383 : case '{':
813 : 117383 : *srcptr = p + 1;
814 : 117383 : return ATOK_LEVEL_START;
815 : 117265 : case '}':
816 : 117265 : *srcptr = p + 1;
817 : 117265 : return ATOK_LEVEL_END;
818 : 280817 : case '"':
819 : 280817 : p++;
820 : 280817 : goto quoted_element;
821 : 829274 : default:
822 [ + + ]: 829274 : if (*p == typdelim)
823 : : {
824 : 491713 : *srcptr = p + 1;
825 : 491713 : return ATOK_DELIM;
826 : : }
827 [ + + ]: 337561 : if (scanner_isspace(*p))
828 : : {
829 : 12270 : p++;
830 : 12270 : continue;
831 : : }
832 : 325291 : goto unquoted_element;
833 : : }
834 : : }
835 : :
836 : 280817 : quoted_element:
837 : : for (;;)
838 : : {
839 [ - + + + ]: 2092732 : switch (*p)
840 : : {
663 tgl@sss.pgh.pa.us 841 :UBC 0 : case '\0':
842 : 0 : goto ending_error;
663 tgl@sss.pgh.pa.us 843 :CBC 313 : case '\\':
844 : : /* Skip backslash, copy next character as-is. */
845 : 313 : p++;
846 [ - + ]: 313 : if (*p == '\0')
663 tgl@sss.pgh.pa.us 847 :UBC 0 : goto ending_error;
663 tgl@sss.pgh.pa.us 848 :CBC 313 : appendStringInfoChar(elembuf, *p++);
849 : 313 : break;
850 : 280817 : 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 [ + - ]: 280835 : while (*(++p) != '\0')
861 : : {
862 [ + + + + : 280835 : if (*p == typdelim || *p == '}' || *p == '{')
+ + ]
863 : : {
864 : 280808 : *srcptr = p;
865 : 280808 : 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 : : }
663 tgl@sss.pgh.pa.us 873 :UBC 0 : goto ending_error;
663 tgl@sss.pgh.pa.us 874 :CBC 1811602 : default:
875 : 1811602 : appendStringInfoChar(elembuf, *p++);
876 : 1811602 : break;
877 : : }
878 : : }
879 : :
880 : 325291 : 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 : 325291 : dstlen = 0;
887 : 325291 : has_escapes = false;
888 : : for (;;)
889 : : {
890 [ + + + + : 1874813 : 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')
663 tgl@sss.pgh.pa.us 910 :UBC 0 : goto ending_error;
663 tgl@sss.pgh.pa.us 911 :CBC 9 : appendStringInfoChar(elembuf, *p++);
912 : 9 : dstlen = elembuf->len; /* treat it as non-whitespace */
913 : 9 : has_escapes = true;
914 : 9 : break;
915 : 1874795 : default:
916 : : /* End of elem? */
917 [ + + + + ]: 1874795 : if (*p == typdelim || *p == '}')
918 : : {
919 : : /* hack: truncate the output string to dstlen */
920 : 325282 : elembuf->data[dstlen] = '\0';
921 : 325282 : elembuf->len = dstlen;
922 : 325282 : *srcptr = p;
923 : : /* Check if it's unquoted "NULL" */
924 [ + - + + : 650555 : if (Array_nulls && !has_escapes &&
+ + ]
925 : 325273 : pg_strcasecmp(elembuf->data, "NULL") == 0)
926 : 267 : return ATOK_ELEM_NULL;
927 : : else
928 : 325015 : return ATOK_ELEM;
929 : : }
930 : 1549513 : appendStringInfoChar(elembuf, *p);
931 [ + + ]: 1549513 : if (!scanner_isspace(*p))
932 : 1549107 : dstlen = elembuf->len;
933 : 1549513 : p++;
934 : 1549513 : 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
7233 961 : 888746 : 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 [ + + ]: 888746 : char *p = ARR_DATA_PTR(array);
971 [ + + ]: 888746 : bits8 *bitmap = ARR_NULLBITMAP(array);
972 : 888746 : int bitval = 0;
973 : 888746 : int bitmask = 1;
974 : : int i;
975 : :
9182 976 [ + + ]: 888746 : if (typbyval)
977 : 601777 : freedata = false;
978 : :
10226 bruce@momjian.us 979 [ + + ]: 6449516 : for (i = 0; i < nitems; i++)
980 : : {
7233 tgl@sss.pgh.pa.us 981 [ + + + + ]: 5560770 : if (nulls && nulls[i])
982 : : {
7228 bruce@momjian.us 983 [ - + ]: 16652 : if (!bitmap) /* shouldn't happen */
7233 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 : : {
7233 tgl@sss.pgh.pa.us 989 :CBC 5544118 : bitval |= bitmask;
990 : 5544118 : p += ArrayCastAndSet(values[i], typlen, typbyval, typalign, p);
991 [ + + ]: 5544118 : if (freedata)
992 : 359714 : pfree(DatumGetPointer(values[i]));
993 : : }
994 [ + + ]: 5560770 : if (bitmap)
995 : : {
996 : 393133 : bitmask <<= 1;
997 [ + + ]: 393133 : if (bitmask == 0x100)
998 : : {
999 : 47570 : *bitmap++ = bitval;
1000 : 47570 : bitval = 0;
1001 : 47570 : bitmask = 1;
1002 : : }
1003 : : }
1004 : : }
1005 : :
1006 [ + + + + ]: 888746 : if (bitmap && bitmask != 1)
1007 : 8955 : *bitmap = bitval;
10651 scrappy@hub.org 1008 : 888746 : }
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
9216 tgl@sss.pgh.pa.us 1016 : 392261 : array_out(PG_FUNCTION_ARGS)
1017 : : {
2910 1018 : 392261 : AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
3768 1019 [ + + ]: 392261 : 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,
7702 mail@joeconway.com 1036 : 392261 : 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 : : */
8107 tgl@sss.pgh.pa.us 1054 : 392261 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1055 [ + + ]: 392261 : if (my_extra == NULL)
1056 : : {
1057 : 12691 : fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1058 : : sizeof(ArrayMetaState));
1059 : 12691 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
7233 1060 : 12691 : my_extra->element_type = ~element_type;
1061 : : }
1062 : :
8107 1063 [ + + ]: 392261 : if (my_extra->element_type != element_type)
1064 : : {
1065 : : /*
1066 : : * Get info about element type, including its output conversion proc
1067 : : */
1068 : 15026 : 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 : 15026 : fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1073 : 15026 : fcinfo->flinfo->fn_mcxt);
1074 : 15026 : my_extra->element_type = element_type;
1075 : : }
1076 : 392261 : typlen = my_extra->typlen;
1077 : 392261 : typbyval = my_extra->typbyval;
1078 : 392261 : typalign = my_extra->typalign;
1079 : 392261 : typdelim = my_extra->typdelim;
1080 : :
3768 1081 [ + + ]: 392261 : ndim = AARR_NDIM(v);
1082 [ + + ]: 392261 : dims = AARR_DIMS(v);
1083 [ + + ]: 392261 : lb = AARR_LBOUND(v);
7660 neilc@samurai.com 1084 : 392261 : nitems = ArrayGetNItems(ndim, dims);
1085 : :
10226 bruce@momjian.us 1086 [ + + ]: 392261 : if (nitems == 0)
1087 : : {
8682 tgl@sss.pgh.pa.us 1088 : 1372 : retval = pstrdup("{}");
9216 1089 : 1372 : 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 : : */
7702 mail@joeconway.com 1096 [ + + ]: 782096 : for (i = 0; i < ndim; i++)
1097 : : {
1098 [ + + ]: 391361 : if (lb[i] != 1)
1099 : : {
1100 : 154 : needdims = true;
1101 : 154 : 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 : : */
8682 tgl@sss.pgh.pa.us 1110 : 390889 : values = (char **) palloc(nitems * sizeof(char *));
1111 : 390889 : needquotes = (bool *) palloc(nitems * sizeof(bool));
2539 1112 : 390889 : overall_length = 0;
1113 : :
3768 1114 : 390889 : array_iter_setup(&iter, v);
1115 : :
10226 bruce@momjian.us 1116 [ + + ]: 1451784 : for (i = 0; i < nitems; i++)
1117 : : {
1118 : : Datum itemvalue;
1119 : : bool isnull;
1120 : : bool needquote;
1121 : :
1122 : : /* Get source element, checking for NULL */
3768 tgl@sss.pgh.pa.us 1123 : 1060895 : itemvalue = array_iter_next(&iter, &isnull, i,
1124 : : typlen, typbyval, typalign);
1125 : :
1126 [ + + ]: 1060895 : if (isnull)
1127 : : {
7233 1128 : 1174 : values[i] = pstrdup("NULL");
1129 : 1174 : overall_length += 4;
7660 neilc@samurai.com 1130 : 1174 : needquote = false;
1131 : : }
1132 : : else
1133 : : {
7095 tgl@sss.pgh.pa.us 1134 : 1059721 : values[i] = OutputFunctionCall(&my_extra->proc, itemvalue);
1135 : :
1136 : : /* count data plus backslashes; detect chars needing quotes */
7233 1137 [ + + ]: 1059721 : if (values[i][0] == '\0')
2999 1138 : 270 : needquote = true; /* force quotes for empty string */
7233 1139 [ + + ]: 1059451 : else if (pg_strcasecmp(values[i], "NULL") == 0)
2999 1140 : 10 : needquote = true; /* force quotes for literal NULL */
1141 : : else
7233 1142 : 1059441 : needquote = false;
1143 : :
1144 [ + + ]: 14307277 : for (tmp = values[i]; *tmp != '\0'; tmp++)
1145 : : {
1146 : 13247556 : char ch = *tmp;
1147 : :
9867 bruce@momjian.us 1148 : 13247556 : overall_length += 1;
7233 tgl@sss.pgh.pa.us 1149 [ + + + + ]: 13247556 : if (ch == '"' || ch == '\\')
1150 : : {
1151 : 5534 : needquote = true;
1152 : 5534 : overall_length += 1;
1153 : : }
1154 [ + + + + : 26457318 : else if (ch == '{' || ch == '}' || ch == typdelim ||
+ + + + ]
793 michael@paquier.xyz 1155 : 13215296 : scanner_isspace(ch))
7233 tgl@sss.pgh.pa.us 1156 : 118585 : needquote = true;
1157 : : }
1158 : : }
1159 : :
7660 neilc@samurai.com 1160 : 1060895 : needquotes[i] = needquote;
1161 : :
1162 : : /* Count the pair of double quotes, if needed */
1163 [ + + ]: 1060895 : if (needquote)
8682 tgl@sss.pgh.pa.us 1164 : 15628 : overall_length += 2;
1165 : : /* and the comma (or other typdelim delimiter) */
10032 bruce@momjian.us 1166 : 1060895 : 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 : : */
7660 neilc@samurai.com 1175 [ + + ]: 782283 : for (i = j = 0, k = 1; i < ndim; i++)
1176 : : {
2539 tgl@sss.pgh.pa.us 1177 : 391394 : j += k, k *= dims[i];
1178 : : }
1179 : 390889 : overall_length += 2 * j;
1180 : :
1181 : : /* Format explicit dimensions if required */
7660 neilc@samurai.com 1182 : 390889 : dims_str[0] = '\0';
7702 mail@joeconway.com 1183 [ + + ]: 390889 : if (needdims)
1184 : : {
7678 bruce@momjian.us 1185 : 154 : char *ptr = dims_str;
1186 : :
7702 mail@joeconway.com 1187 [ + + ]: 342 : for (i = 0; i < ndim; i++)
1188 : : {
7660 neilc@samurai.com 1189 : 188 : sprintf(ptr, "[%d:%d]", lb[i], lb[i] + dims[i] - 1);
7702 mail@joeconway.com 1190 : 188 : ptr += strlen(ptr);
1191 : : }
1192 : 154 : *ptr++ = *ASSGN;
1193 : 154 : *ptr = '\0';
2539 tgl@sss.pgh.pa.us 1194 : 154 : overall_length += ptr - dims_str;
1195 : : }
1196 : :
1197 : : /* Now construct the output string */
1198 : 390889 : retval = (char *) palloc(overall_length);
8682 1199 : 390889 : p = retval;
1200 : :
1201 : : #define APPENDSTR(str) (strcpy(p, (str)), p += strlen(p))
1202 : : #define APPENDCHAR(ch) (*p++ = (ch), *p = '\0')
1203 : :
7702 mail@joeconway.com 1204 [ + + ]: 390889 : if (needdims)
1205 : 154 : APPENDSTR(dims_str);
8682 tgl@sss.pgh.pa.us 1206 : 390889 : APPENDCHAR('{');
7660 neilc@samurai.com 1207 [ + + ]: 782283 : for (i = 0; i < ndim; i++)
1208 : 391394 : indx[i] = 0;
10226 bruce@momjian.us 1209 : 390889 : j = 0;
1210 : 390889 : k = 0;
1211 : : do
1212 : : {
1213 [ + + ]: 1062155 : for (i = j; i < ndim - 1; i++)
8682 tgl@sss.pgh.pa.us 1214 : 1260 : APPENDCHAR('{');
1215 : :
1216 [ + + ]: 1060895 : if (needquotes[k])
1217 : : {
1218 : 15628 : APPENDCHAR('"');
9867 bruce@momjian.us 1219 [ + + ]: 592351 : for (tmp = values[k]; *tmp; tmp++)
1220 : : {
8403 1221 : 576723 : char ch = *tmp;
1222 : :
8682 tgl@sss.pgh.pa.us 1223 [ + + + + ]: 576723 : if (ch == '"' || ch == '\\')
1224 : 5534 : *p++ = '\\';
1225 : 576723 : *p++ = ch;
1226 : : }
1227 : 15628 : *p = '\0';
1228 : 15628 : APPENDCHAR('"');
1229 : : }
1230 : : else
1231 : 1045267 : APPENDSTR(values[k]);
10226 bruce@momjian.us 1232 : 1060895 : pfree(values[k++]);
1233 : :
1234 [ + + ]: 1453044 : for (i = ndim - 1; i >= 0; i--)
1235 : : {
2539 tgl@sss.pgh.pa.us 1236 [ + + ]: 1062155 : if (++(indx[i]) < dims[i])
1237 : : {
8682 1238 : 670006 : APPENDCHAR(typdelim);
10226 bruce@momjian.us 1239 : 670006 : break;
1240 : : }
1241 : : else
1242 : : {
2539 tgl@sss.pgh.pa.us 1243 : 392149 : indx[i] = 0;
8682 1244 : 392149 : APPENDCHAR('}');
1245 : : }
1246 : : }
10226 bruce@momjian.us 1247 : 1060895 : j = i;
1248 [ + + ]: 1060895 : } while (j != -1);
1249 : :
1250 : : #undef APPENDSTR
1251 : : #undef APPENDCHAR
1252 : :
1253 : : /* Assert that we calculated the string length accurately */
2539 tgl@sss.pgh.pa.us 1254 [ - + ]: 390889 : Assert(overall_length == (p - retval + 1));
1255 : :
10226 bruce@momjian.us 1256 : 390889 : pfree(values);
8682 tgl@sss.pgh.pa.us 1257 : 390889 : pfree(needquotes);
1258 : :
9216 1259 : 390889 : 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
8157 1271 : 31 : array_recv(PG_FUNCTION_ARGS)
1272 : : {
8156 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 */
7266 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 */
8156 tgl@sss.pgh.pa.us 1297 : 31 : ndim = pq_getmsgint(buf, 4);
8077 1298 [ - + ]: 31 : if (ndim < 0) /* we do allow zero-dimension arrays */
8077 tgl@sss.pgh.pa.us 1299 [ # # ]:UBC 0 : ereport(ERROR,
1300 : : (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1301 : : errmsg("invalid number of dimensions: %d", ndim)));
8077 tgl@sss.pgh.pa.us 1302 [ - + ]:CBC 31 : if (ndim > MAXDIM)
8077 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 : :
8156 tgl@sss.pgh.pa.us 1308 :CBC 31 : flags = pq_getmsgint(buf, 4);
7233 1309 [ - + - - ]: 31 : if (flags != 0 && flags != 1)
8077 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 */
8156 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 : : {
1873 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 : :
8156 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);
1580 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 : : */
8107 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;
7327 1365 : 28 : my_extra->element_type = ~element_type;
1366 : : }
1367 : :
8107 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))
8077 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))));
8107 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 : :
7327 1385 [ - + ]: 31 : if (nitems == 0)
1386 : : {
1387 : : /* Return empty array ... but not till we've validated element_type */
7233 tgl@sss.pgh.pa.us 1388 :UBC 0 : PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
1389 : : }
1390 : :
8107 tgl@sss.pgh.pa.us 1391 :CBC 31 : typlen = my_extra->typlen;
1392 : 31 : typbyval = my_extra->typbyval;
1393 : 31 : typalign = my_extra->typalign;
7762 1394 : 31 : typioparam = my_extra->typioparam;
1395 : :
7233 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 : : {
7233 tgl@sss.pgh.pa.us 1405 :UBC 0 : dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
1406 : 0 : nbytes += dataoffset;
1407 : : }
1408 : : else
1409 : : {
7233 tgl@sss.pgh.pa.us 1410 :CBC 31 : dataoffset = 0; /* marker for no null bitmap */
1411 : 31 : nbytes += ARR_OVERHEAD_NONULLS(ndim);
1412 : : }
5246 1413 : 31 : retval = (ArrayType *) palloc0(nbytes);
6766 1414 : 31 : SET_VARSIZE(retval, nbytes);
8156 1415 : 31 : retval->ndim = ndim;
7233 1416 : 31 : retval->dataoffset = dataoffset;
8156 1417 : 31 : retval->elemtype = element_type;
7913 neilc@samurai.com 1418 : 31 : memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
1419 : 31 : memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
1420 : :
7233 tgl@sss.pgh.pa.us 1421 : 31 : CopyArrayEls(retval,
1422 : : dataPtr, nullsPtr, nitems,
1423 : : typlen, typbyval, typalign,
1424 : : true);
1425 : :
8156 1426 : 31 : pfree(dataPtr);
7233 1427 : 31 : pfree(nullsPtr);
1428 : :
8156 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);
7233 1478 [ + - - + ]: 93 : if (itemlen < -1 || itemlen > (buf->len - buf->cursor))
8077 tgl@sss.pgh.pa.us 1479 [ # # ]:UBC 0 : ereport(ERROR,
1480 : : (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1481 : : errmsg("insufficient data left in message")));
1482 : :
7233 tgl@sss.pgh.pa.us 1483 [ - + ]:CBC 93 : if (itemlen == -1)
1484 : : {
1485 : : /* -1 length means NULL */
7095 tgl@sss.pgh.pa.us 1486 :UBC 0 : values[i] = ReceiveFunctionCall(receiveproc, NULL,
1487 : : typioparam, typmod);
7233 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 : : */
681 drowley@postgresql.o 1496 :CBC 93 : initReadOnlyStringInfo(&elem_buf, &buf->data[buf->cursor], itemlen);
1497 : :
8156 tgl@sss.pgh.pa.us 1498 : 93 : buf->cursor += itemlen;
1499 : :
1500 : : /* Now call the element's receiveproc */
7095 1501 : 93 : values[i] = ReceiveFunctionCall(receiveproc, &elem_buf,
1502 : : typioparam, typmod);
7233 1503 : 93 : nulls[i] = false;
1504 : :
1505 : : /* Trouble if it didn't eat the whole buffer */
8156 1506 [ - + ]: 93 : if (elem_buf.cursor != itemlen)
8077 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 : : */
7233 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])
7233 tgl@sss.pgh.pa.us 1521 :UBC 0 : hasnull = true;
1522 : : else
1523 : : {
1524 : : /* let's just make sure data is not toasted */
8156 tgl@sss.pgh.pa.us 1525 [ + + ]:CBC 93 : if (typlen == -1)
1526 : 54 : values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
6728 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 */
7233 1530 [ - + ]: 93 : if (!AllocSizeIsValid(totbytes))
7233 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 : : }
7233 tgl@sss.pgh.pa.us 1537 :CBC 31 : *hasnulls = hasnull;
1538 : 31 : *nbytes = totbytes;
8156 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
8157 1548 : 23 : array_send(PG_FUNCTION_ARGS)
1549 : : {
2910 1550 : 23 : AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
3768 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 : : */
8107 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;
7233 1575 : 20 : my_extra->element_type = ~element_type;
1576 : : }
1577 : :
8107 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))
8077 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))));
8107 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 : :
3768 1598 [ - + ]: 23 : ndim = AARR_NDIM(v);
1599 [ - + ]: 23 : dim = AARR_DIMS(v);
1600 [ - + ]: 23 : lb = AARR_LBOUND(v);
8156 1601 : 23 : nitems = ArrayGetNItems(ndim, dim);
1602 : :
1603 : 23 : pq_begintypsend(&buf);
1604 : :
1605 : : /* Send the array header information */
2887 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);
8156 tgl@sss.pgh.pa.us 1609 [ + + ]: 46 : for (i = 0; i < ndim; i++)
1610 : : {
2887 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 */
3768 tgl@sss.pgh.pa.us 1616 : 23 : array_iter_setup(&iter, v);
1617 : :
8156 1618 [ + + ]: 92 : for (i = 0; i < nitems; i++)
1619 : : {
1620 : : Datum itemvalue;
1621 : : bool isnull;
1622 : :
1623 : : /* Get source element, checking for NULL */
3768 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 */
2887 andres@anarazel.de 1630 :UBC 0 : pq_sendint32(&buf, -1);
1631 : : }
1632 : : else
1633 : : {
1634 : : bytea *outputbytes;
1635 : :
7095 tgl@sss.pgh.pa.us 1636 :CBC 69 : outputbytes = SendFunctionCall(&my_extra->proc, itemvalue);
2887 andres@anarazel.de 1637 : 69 : pq_sendint32(&buf, VARSIZE(outputbytes) - VARHDRSZ);
7233 tgl@sss.pgh.pa.us 1638 : 69 : pq_sendbytes(&buf, VARDATA(outputbytes),
1639 : 69 : VARSIZE(outputbytes) - VARHDRSZ);
1640 : 69 : pfree(outputbytes);
1641 : : }
1642 : : }
1643 : :
8156 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
6150 peter_e@gmx.net 1652 : 1203 : array_ndims(PG_FUNCTION_ARGS)
1653 : : {
2910 tgl@sss.pgh.pa.us 1654 : 1203 : AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
1655 : :
1656 : : /* Sanity check: does it look like an array at all? */
3768 1657 [ - + - - : 1203 : if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
+ + - + -
- - + ]
6150 peter_e@gmx.net 1658 : 6 : PG_RETURN_NULL();
1659 : :
3768 tgl@sss.pgh.pa.us 1660 [ - + ]: 1197 : 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
9216 1668 : 4462 : array_dims(PG_FUNCTION_ARGS)
1669 : : {
2910 1670 : 4462 : 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? */
3768 1684 [ - + - - : 4462 : if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
+ + - + -
- - + ]
6374 1685 : 34 : PG_RETURN_NULL();
1686 : :
3768 1687 [ - + ]: 4428 : dimv = AARR_DIMS(v);
1688 [ - + ]: 4428 : lb = AARR_LBOUND(v);
1689 : :
6374 1690 : 4428 : p = buf;
3768 1691 [ - + + + ]: 8910 : for (i = 0; i < AARR_NDIM(v); i++)
1692 : : {
10226 bruce@momjian.us 1693 : 4482 : sprintf(p, "[%d:%d]", lb[i], dimv[i] + lb[i] - 1);
1694 : 4482 : p += strlen(p);
1695 : : }
1696 : :
6374 tgl@sss.pgh.pa.us 1697 : 4428 : 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
8338 bruce@momjian.us 1706 : 12861 : array_lower(PG_FUNCTION_ARGS)
1707 : : {
2910 tgl@sss.pgh.pa.us 1708 : 12861 : AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
8338 bruce@momjian.us 1709 : 12861 : int reqdim = PG_GETARG_INT32(1);
1710 : : int *lb;
1711 : : int result;
1712 : :
1713 : : /* Sanity check: does it look like an array at all? */
3768 tgl@sss.pgh.pa.us 1714 [ + + + - : 12861 : if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
+ - + + -
+ - + ]
8338 bruce@momjian.us 1715 :UBC 0 : PG_RETURN_NULL();
1716 : :
1717 : : /* Sanity check: was the requested dim valid */
3768 tgl@sss.pgh.pa.us 1718 [ + - + + :CBC 12861 : if (reqdim <= 0 || reqdim > AARR_NDIM(v))
- + ]
8338 bruce@momjian.us 1719 :UBC 0 : PG_RETURN_NULL();
1720 : :
3768 tgl@sss.pgh.pa.us 1721 [ + + ]:CBC 12861 : lb = AARR_LBOUND(v);
8338 bruce@momjian.us 1722 : 12861 : result = lb[reqdim - 1];
1723 : :
1724 : 12861 : 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 : 13218 : array_upper(PG_FUNCTION_ARGS)
1734 : : {
2910 tgl@sss.pgh.pa.us 1735 : 13218 : AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
8338 bruce@momjian.us 1736 : 13218 : 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? */
3768 tgl@sss.pgh.pa.us 1742 [ + + + - : 13218 : if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
+ + + + -
+ - + ]
8338 bruce@momjian.us 1743 : 15 : PG_RETURN_NULL();
1744 : :
1745 : : /* Sanity check: was the requested dim valid */
3768 tgl@sss.pgh.pa.us 1746 [ + - + + : 13203 : if (reqdim <= 0 || reqdim > AARR_NDIM(v))
- + ]
8338 bruce@momjian.us 1747 :UBC 0 : PG_RETURN_NULL();
1748 : :
3768 tgl@sss.pgh.pa.us 1749 [ + + ]:CBC 13203 : lb = AARR_LBOUND(v);
1750 [ + + ]: 13203 : dimv = AARR_DIMS(v);
1751 : :
8338 bruce@momjian.us 1752 : 13203 : result = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
1753 : :
1754 : 13203 : 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
6142 peter_e@gmx.net 1763 : 56165 : array_length(PG_FUNCTION_ARGS)
1764 : : {
2910 tgl@sss.pgh.pa.us 1765 : 56165 : AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
6142 peter_e@gmx.net 1766 : 56165 : int reqdim = PG_GETARG_INT32(1);
1767 : : int *dimv;
1768 : : int result;
1769 : :
1770 : : /* Sanity check: does it look like an array at all? */
3768 tgl@sss.pgh.pa.us 1771 [ + + + - : 56165 : if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
+ - + + -
+ - + ]
6142 peter_e@gmx.net 1772 :UBC 0 : PG_RETURN_NULL();
1773 : :
1774 : : /* Sanity check: was the requested dim valid */
3768 tgl@sss.pgh.pa.us 1775 [ + + + + :CBC 56165 : if (reqdim <= 0 || reqdim > AARR_NDIM(v))
+ + ]
6142 peter_e@gmx.net 1776 : 6 : PG_RETURN_NULL();
1777 : :
3768 tgl@sss.pgh.pa.us 1778 [ + + ]: 56159 : dimv = AARR_DIMS(v);
1779 : :
6142 peter_e@gmx.net 1780 : 56159 : result = dimv[reqdim - 1];
1781 : :
1782 : 56159 : PG_RETURN_INT32(result);
1783 : : }
1784 : :
1785 : : /*
1786 : : * array_cardinality:
1787 : : * returns the total number of elements in an array
1788 : : */
1789 : : Datum
4246 rhaas@postgresql.org 1790 : 1266 : array_cardinality(PG_FUNCTION_ARGS)
1791 : : {
2910 tgl@sss.pgh.pa.us 1792 : 1266 : AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
1793 : :
3768 1794 [ - + - + ]: 1266 : 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
3855 1820 : 426556 : 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 : :
7233 1840 [ + + ]: 426556 : if (arraytyplen > 0)
1841 : : {
1842 : : /*
1843 : : * fixed-length arrays -- these are assumed to be 1-d, 0-based
1844 : : */
9176 1845 : 171728 : ndim = 1;
7233 1846 : 171728 : fixedDim[0] = arraytyplen / elmlen;
9176 1847 : 171728 : fixedLb[0] = 0;
1848 : 171728 : dim = fixedDim;
1849 : 171728 : lb = fixedLb;
3855 1850 : 171728 : arraydataptr = (char *) DatumGetPointer(arraydatum);
7233 1851 : 171728 : arraynullsptr = NULL;
1852 : : }
3768 1853 [ + + + + ]: 254828 : else if (VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(arraydatum)))
1854 : : {
1855 : : /* expanded array: let's do this in a separate function */
1856 : 2495 : 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 : 252333 : ArrayType *array = DatumGetArrayTypeP(arraydatum);
1869 : :
9176 1870 : 252333 : ndim = ARR_NDIM(array);
1871 : 252333 : dim = ARR_DIMS(array);
1872 : 252333 : lb = ARR_LBOUND(array);
1873 [ + + ]: 252333 : arraydataptr = ARR_DATA_PTR(array);
7233 1874 [ + + ]: 252333 : arraynullsptr = ARR_NULLBITMAP(array);
1875 : : }
1876 : :
1877 : : /*
1878 : : * Return NULL for invalid subscript
1879 : : */
9176 1880 [ + + + - : 424061 : if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
- + ]
1881 : : {
7233 1882 : 48 : *isNull = true;
1883 : 48 : return (Datum) 0;
1884 : : }
9176 1885 [ + + ]: 821461 : for (i = 0; i < ndim; i++)
1886 : : {
1887 [ + + + + ]: 424061 : if (indx[i] < lb[i] || indx[i] >= (dim[i] + lb[i]))
1888 : : {
7233 1889 : 26613 : *isNull = true;
1890 : 26613 : return (Datum) 0;
1891 : : }
1892 : : }
1893 : :
1894 : : /*
1895 : : * Calculate the element number
1896 : : */
9177 1897 : 397400 : offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
1898 : :
1899 : : /*
1900 : : * Check for NULL array element
1901 : : */
7233 1902 [ + + ]: 397400 : 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 : : */
8590 peter_e@gmx.net 1911 : 397364 : *isNull = false;
7233 tgl@sss.pgh.pa.us 1912 : 397364 : retptr = array_seek(arraydataptr, 0, arraynullsptr, offset,
1913 : : elmlen, elmbyval, elmalign);
9177 1914 : 397364 : return ArrayCast(retptr, elmbyval, elmlen);
1915 : : }
1916 : :
1917 : : /*
1918 : : * Implementation of array_get_element() for an expanded array
1919 : : */
1920 : : static Datum
3768 1921 : 2495 : 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 : 2495 : eah = (ExpandedArrayHeader *) DatumGetEOHP(arraydatum);
1937 [ - + ]: 2495 : Assert(eah->ea_magic == EA_MAGIC);
1938 : :
1939 : : /* sanity-check caller's info against object */
1940 [ - + ]: 2495 : Assert(arraytyplen == -1);
1941 [ - + ]: 2495 : Assert(elmlen == eah->typlen);
1942 [ - + ]: 2495 : Assert(elmbyval == eah->typbyval);
1943 [ - + ]: 2495 : Assert(elmalign == eah->typalign);
1944 : :
1945 : 2495 : ndim = eah->ndims;
1946 : 2495 : dim = eah->dims;
1947 : 2495 : lb = eah->lbound;
1948 : :
1949 : : /*
1950 : : * Return NULL for invalid subscript
1951 : : */
1952 [ + + + - : 2495 : if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
- + ]
1953 : : {
1954 : 3 : *isNull = true;
1955 : 3 : return (Datum) 0;
1956 : : }
1957 [ + + ]: 4984 : for (i = 0; i < ndim; i++)
1958 : : {
1959 [ + - - + ]: 2492 : if (indx[i] < lb[i] || indx[i] >= (dim[i] + lb[i]))
1960 : : {
3768 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 : : */
3768 tgl@sss.pgh.pa.us 1969 :CBC 2492 : 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 : 2492 : deconstruct_expanded_array(eah);
1976 : :
1977 : 2492 : dvalues = eah->dvalues;
1978 : 2492 : dnulls = eah->dnulls;
1979 : :
1980 : : /*
1981 : : * Check for NULL array element
1982 : : */
1983 [ - + - - ]: 2492 : if (dnulls && dnulls[offset])
1984 : : {
3768 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 : : */
3768 tgl@sss.pgh.pa.us 1995 :CBC 2492 : *isNull = false;
1996 : 2492 : 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
3855 2030 : 198 : 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 : :
7233 2057 [ + + ]: 198 : 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 : : */
8077 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 */
3855 2086 : 186 : array = DatumGetArrayTypeP(arraydatum);
2087 : :
9176 2088 : 186 : ndim = ARR_NDIM(array);
2089 : 186 : dim = ARR_DIMS(array);
2090 : 186 : lb = ARR_LBOUND(array);
7233 2091 : 186 : elemtype = ARR_ELEMTYPE(array);
9176 2092 [ + + ]: 186 : arraydataptr = ARR_DATA_PTR(array);
7233 2093 [ + + ]: 186 : 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 : : */
8571 2101 [ + + + - : 186 : if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
- + ]
3855 2102 : 48 : return PointerGetDatum(construct_empty_array(elemtype));
2103 : :
8571 2104 [ + + ]: 318 : for (i = 0; i < nSubscripts; i++)
2105 : : {
3546 2106 [ + + + + ]: 186 : if (!lowerProvided[i] || lowerIndx[i] < lb[i])
9176 2107 : 63 : lowerIndx[i] = lb[i];
3546 2108 [ + + + + ]: 186 : if (!upperProvided[i] || upperIndx[i] >= (dim[i] + lb[i]))
9176 2109 : 36 : upperIndx[i] = dim[i] + lb[i] - 1;
10226 bruce@momjian.us 2110 [ + + ]: 186 : if (lowerIndx[i] > upperIndx[i])
3855 tgl@sss.pgh.pa.us 2111 : 6 : return PointerGetDatum(construct_empty_array(elemtype));
2112 : : }
2113 : : /* fill any missing subscript positions with full array range */
8571 2114 [ + + ]: 156 : for (; i < ndim; i++)
2115 : : {
2116 : 24 : lowerIndx[i] = lb[i];
2117 : 24 : upperIndx[i] = dim[i] + lb[i] - 1;
2118 [ - + ]: 24 : if (lowerIndx[i] > upperIndx[i])
3855 tgl@sss.pgh.pa.us 2119 :UBC 0 : return PointerGetDatum(construct_empty_array(elemtype));
2120 : : }
2121 : :
8571 tgl@sss.pgh.pa.us 2122 :CBC 132 : mda_get_range(ndim, span, lowerIndx, upperIndx);
2123 : :
7233 2124 : 132 : 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 [ + + ]: 132 : if (arraynullsptr)
2134 : : {
2135 : 18 : dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, ArrayGetNItems(ndim, span));
2136 : 18 : bytes += dataoffset;
2137 : : }
2138 : : else
2139 : : {
2140 : 114 : dataoffset = 0; /* marker for no null bitmap */
2141 : 114 : bytes += ARR_OVERHEAD_NONULLS(ndim);
2142 : : }
2143 : :
5246 2144 : 132 : newarray = (ArrayType *) palloc0(bytes);
6766 2145 : 132 : SET_VARSIZE(newarray, bytes);
9176 2146 : 132 : newarray->ndim = ndim;
7233 2147 : 132 : newarray->dataoffset = dataoffset;
2148 : 132 : newarray->elemtype = elemtype;
9176 2149 : 132 : 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 : : */
8589 2155 : 132 : newlb = ARR_LBOUND(newarray);
2156 [ + + ]: 336 : for (i = 0; i < ndim; i++)
2157 : 204 : newlb[i] = 1;
2158 : :
7233 2159 : 132 : array_extract_slice(newarray,
2160 : : ndim, dim, lb,
2161 : : arraydataptr, arraynullsptr,
2162 : : lowerIndx, upperIndx,
2163 : : elmlen, elmbyval, elmalign);
2164 : :
3855 2165 : 132 : 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 : 2025 : 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 : :
7233 2234 [ + + ]: 2025 : 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 : :
9177 2242 [ - + ]: 9 : if (nSubscripts != 1)
8077 tgl@sss.pgh.pa.us 2243 [ # # ]:UBC 0 : ereport(ERROR,
2244 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2245 : : errmsg("wrong number of array subscripts")));
2246 : :
1580 tgl@sss.pgh.pa.us 2247 [ + - + + ]:CBC 9 : if (indx[0] < 0 || indx[0] >= arraytyplen / elmlen)
8077 2248 [ + - ]: 3 : ereport(ERROR,
2249 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2250 : : errmsg("array subscript out of range")));
2251 : :
7233 2252 [ - + ]: 6 : if (isNull)
7233 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 : :
3855 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;
8412 2260 : 6 : ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign, elt_ptr);
3855 2261 : 6 : return PointerGetDatum(resultarray);
2262 : : }
2263 : :
7233 2264 [ + - - + ]: 2016 : if (nSubscripts <= 0 || nSubscripts > MAXDIM)
7233 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 */
7233 tgl@sss.pgh.pa.us 2270 [ + + + + ]:CBC 2016 : if (elmlen == -1 && !isNull)
9172 2271 : 1249 : dataValue = PointerGetDatum(PG_DETOAST_DATUM(dataValue));
2272 : :
3768 2273 [ + + + - ]: 2016 : if (VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(arraydatum)))
2274 : : {
2275 : : /* expanded array: let's do this in a separate function */
2276 : 1008 : 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 */
3855 2288 : 1008 : array = DatumGetArrayTypeP(arraydatum);
2289 : :
9177 2290 : 1008 : 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 : : */
8107 2297 [ + + ]: 1008 : if (ndim == 0)
2298 : : {
8069 bruce@momjian.us 2299 : 174 : Oid elmtype = ARR_ELEMTYPE(array);
2300 : :
8107 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 : :
3855 2307 : 174 : return PointerGetDatum(construct_md_array(&dataValue, &isNull,
2308 : : nSubscripts, dim, lb,
2309 : : elmtype,
2310 : : elmlen, elmbyval, elmalign));
2311 : : }
2312 : :
7233 2313 [ + + ]: 834 : if (ndim != nSubscripts)
8077 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 */
9176 2319 : 831 : memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
2320 : 831 : memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
2321 : :
6917 2322 [ + + + + ]: 831 : newhasnulls = (ARR_HASNULL(array) || isNull);
2323 : 831 : 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 [ + + ]: 831 : if (ndim == 1)
2332 : : {
2333 [ + + ]: 828 : if (indx[0] < lb[0])
2334 : : {
2335 : : /* addedbefore = lb[0] - indx[0]; */
2336 : : /* dim[0] += addedbefore; */
670 2337 [ + - - + ]: 24 : if (pg_sub_s32_overflow(lb[0], indx[0], &addedbefore) ||
2338 : 12 : pg_add_s32_overflow(dim[0], addedbefore, &dim[0]))
670 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)));
6917 tgl@sss.pgh.pa.us 2343 :CBC 12 : lb[0] = indx[0];
2344 [ + + ]: 12 : if (addedbefore > 1)
2999 2345 : 6 : newhasnulls = true; /* will insert nulls */
2346 : : }
6917 2347 [ + + ]: 828 : if (indx[0] >= (dim[0] + lb[0]))
2348 : : {
2349 : : /* addedafter = indx[0] - (dim[0] + lb[0]) + 1; */
2350 : : /* dim[0] += addedafter; */
670 2351 [ + + + - ]: 1285 : if (pg_sub_s32_overflow(indx[0], dim[0] + lb[0], &addedafter) ||
2352 [ - + ]: 1282 : pg_add_s32_overflow(addedafter, 1, &addedafter) ||
2353 : 641 : 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)));
6917 2358 [ + + ]: 641 : if (addedafter > 1)
2999 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 : : */
6917 2368 [ + + ]: 9 : for (i = 0; i < ndim; i++)
2369 : : {
2370 [ + - ]: 6 : if (indx[i] < lb[i] ||
2371 [ - + ]: 6 : indx[i] >= (dim[i] + lb[i]))
8077 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 */
1580 tgl@sss.pgh.pa.us 2379 :CBC 828 : newnitems = ArrayGetNItems(ndim, dim);
2380 : 828 : ArrayCheckBounds(ndim, dim, lb);
2381 : :
2382 : : /*
2383 : : * Compute sizes of items and areas to copy
2384 : : */
6917 2385 [ + + ]: 828 : if (newhasnulls)
2386 : 67 : overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, newnitems);
2387 : : else
7233 2388 : 761 : overheadlen = ARR_OVERHEAD_NONULLS(ndim);
2389 : 828 : oldnitems = ArrayGetNItems(ndim, ARR_DIMS(array));
2390 [ + + ]: 828 : oldnullbitmap = ARR_NULLBITMAP(array);
2391 [ + + ]: 828 : oldoverheadlen = ARR_DATA_OFFSET(array);
2392 : 828 : olddatasize = ARR_SIZE(array) - oldoverheadlen;
6917 2393 [ + + ]: 828 : if (addedbefore)
2394 : : {
7233 2395 : 12 : offset = 0;
9176 2396 : 12 : lenbefore = 0;
2397 : 12 : olditemlen = 0;
2398 : 12 : lenafter = olddatasize;
2399 : : }
6917 2400 [ + + ]: 816 : else if (addedafter)
2401 : : {
7233 2402 : 641 : offset = oldnitems;
9176 2403 : 641 : lenbefore = olddatasize;
2404 : 641 : olditemlen = 0;
2405 : 641 : lenafter = 0;
2406 : : }
2407 : : else
2408 : : {
2409 : 175 : offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
7233 2410 [ + + ]: 175 : elt_ptr = array_seek(ARR_DATA_PTR(array), 0, oldnullbitmap, offset,
2411 : : elmlen, elmbyval, elmalign);
9176 2412 [ + + ]: 175 : lenbefore = (int) (elt_ptr - ARR_DATA_PTR(array));
7233 2413 [ + + ]: 175 : if (array_get_isnull(oldnullbitmap, offset))
2414 : 12 : olditemlen = 0;
2415 : : else
2416 : : {
6728 2417 [ + + + - : 163 : olditemlen = att_addlength_pointer(0, elmlen, elt_ptr);
- - - - -
- - - - +
- - ]
2418 [ + + + - : 163 : olditemlen = att_align_nominal(olditemlen, elmalign);
+ - - - ]
2419 : : }
9176 2420 : 175 : lenafter = (int) (olddatasize - lenbefore - olditemlen);
2421 : : }
2422 : :
7233 2423 [ + + ]: 828 : if (isNull)
2424 : 10 : newitemlen = 0;
2425 : : else
2426 : : {
6728 2427 [ + + + - : 818 : newitemlen = att_addlength_datum(0, elmlen, dataValue);
- - - - -
- - - - +
- - ]
2428 [ + + + - : 818 : newitemlen = att_align_nominal(newitemlen, elmalign);
+ - - - ]
2429 : : }
2430 : :
9176 2431 : 828 : newsize = overheadlen + lenbefore + newitemlen + lenafter;
2432 : :
2433 : : /*
2434 : : * OK, create the new array and fill in header/dimensions
2435 : : */
5246 2436 : 828 : newarray = (ArrayType *) palloc0(newsize);
6766 2437 : 828 : SET_VARSIZE(newarray, newsize);
9176 2438 : 828 : newarray->ndim = ndim;
7233 2439 [ + + ]: 828 : newarray->dataoffset = newhasnulls ? overheadlen : 0;
8412 2440 : 828 : newarray->elemtype = ARR_ELEMTYPE(array);
9176 2441 : 828 : memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
2442 : 828 : memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
2443 : :
2444 : : /*
2445 : : * Fill in data
2446 : : */
2447 : 828 : memcpy((char *) newarray + overheadlen,
2448 : : (char *) array + oldoverheadlen,
2449 : : lenbefore);
7233 2450 [ + + ]: 828 : if (!isNull)
2451 : 818 : ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign,
2452 : 818 : (char *) newarray + overheadlen + lenbefore);
9176 2453 : 828 : memcpy((char *) newarray + overheadlen + lenbefore + newitemlen,
7233 2454 : 828 : (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 [ + + ]: 828 : if (newhasnulls)
2464 : : {
7228 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 */
6917 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 : : {
7233 2480 : 55 : array_bitmap_copy(newnullbitmap, 0,
2481 : : oldnullbitmap, 0,
2482 : : offset);
6917 2483 [ + + ]: 55 : if (addedafter == 0)
7228 bruce@momjian.us 2484 : 27 : array_bitmap_copy(newnullbitmap, offset + 1,
2485 : : oldnullbitmap, offset + 1,
7233 tgl@sss.pgh.pa.us 2486 : 27 : oldnitems - offset - 1);
2487 : : }
2488 : : }
2489 : :
3855 2490 : 828 : 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
3768 2501 : 1008 : 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 : 1008 : eah = DatumGetExpandedArray(arraydatum);
2523 : :
2524 : : /* Sanity-check caller's info against object; we don't use it otherwise */
2525 [ - + ]: 1008 : Assert(arraytyplen == -1);
2526 [ - + ]: 1008 : Assert(elmlen == eah->typlen);
2527 [ - + ]: 1008 : Assert(elmbyval == eah->typbyval);
2528 [ - + ]: 1008 : 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 : 1008 : ndim = eah->ndims;
2536 [ + - - + ]: 1008 : Assert(ndim >= 0 && ndim <= MAXDIM);
2537 : 1008 : memcpy(dim, eah->dims, ndim * sizeof(int));
2538 : 1008 : memcpy(lb, eah->lbound, ndim * sizeof(int));
2539 : 1008 : 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 [ + + ]: 1008 : 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 [ - + ]: 800 : else if (ndim != nSubscripts)
3768 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 : : */
3768 tgl@sss.pgh.pa.us 2578 :CBC 1008 : 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 [ + + + - ]: 1008 : 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 : 1008 : dvalues = eah->dvalues;
2597 : 1008 : dnulls = eah->dnulls;
2598 : :
2599 [ + - - + ]: 1008 : newhasnulls = ((dnulls != NULL) || isNull);
2600 : 1008 : 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 [ + - ]: 1008 : if (ndim == 1)
2609 : : {
2610 [ + + ]: 1008 : if (indx[0] < lb[0])
2611 : : {
2612 : : /* addedbefore = lb[0] - indx[0]; */
2613 : : /* dim[0] += addedbefore; */
670 2614 [ + - - + ]: 80 : if (pg_sub_s32_overflow(lb[0], indx[0], &addedbefore) ||
2615 : 40 : pg_add_s32_overflow(dim[0], addedbefore, &dim[0]))
670 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)));
3768 tgl@sss.pgh.pa.us 2620 :CBC 40 : lb[0] = indx[0];
2621 : 40 : dimschanged = true;
2622 [ - + ]: 40 : if (addedbefore > 1)
2999 tgl@sss.pgh.pa.us 2623 :UBC 0 : newhasnulls = true; /* will insert nulls */
2624 : : }
3768 tgl@sss.pgh.pa.us 2625 [ + + ]:CBC 1008 : if (indx[0] >= (dim[0] + lb[0]))
2626 : : {
2627 : : /* addedafter = indx[0] - (dim[0] + lb[0]) + 1; */
2628 : : /* dim[0] += addedafter; */
670 2629 [ + + + - ]: 1883 : if (pg_sub_s32_overflow(indx[0], dim[0] + lb[0], &addedafter) ||
2630 [ - + ]: 1880 : pg_add_s32_overflow(addedafter, 1, &addedafter) ||
2631 : 940 : 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)));
3768 2636 : 940 : dimschanged = true;
2637 [ - + ]: 940 : if (addedafter > 1)
2999 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 : : */
3768 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 */
1580 tgl@sss.pgh.pa.us 2658 [ + + ]:CBC 1005 : if (dimschanged)
2659 : : {
2660 : 980 : (void) ArrayGetNItems(ndim, dim);
2661 : 980 : ArrayCheckBounds(ndim, dim, lb);
2662 : : }
2663 : :
2664 : : /* Now we can calculate linear offset of target item in array */
3768 2665 : 1005 : offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
2666 : :
2667 : : /* Physically enlarge existing dvalues/dnulls arrays if needed */
2668 [ + + ]: 1005 : if (dim[0] > eah->dvalueslen)
2669 : : {
2670 : : /* We want some extra space if we're enlarging */
2671 : 974 : int newlen = dim[0] + dim[0] / 8;
2672 : :
2673 : 974 : newlen = Max(newlen, dim[0]); /* integer overflow guard */
2674 : 974 : eah->dvalues = dvalues = (Datum *)
2675 : 974 : repalloc(dvalues, newlen * sizeof(Datum));
2676 [ - + ]: 974 : if (dnulls)
3768 tgl@sss.pgh.pa.us 2677 :UBC 0 : eah->dnulls = dnulls = (bool *)
2678 : 0 : repalloc(dnulls, newlen * sizeof(bool));
3768 tgl@sss.pgh.pa.us 2679 :CBC 974 : 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 [ - + - - ]: 1005 : if (newhasnulls && dnulls == NULL)
3768 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 */
3768 tgl@sss.pgh.pa.us 2697 :CBC 1005 : eah->fvalue = NULL;
2698 : : /* And we don't know the flattened size either */
2699 : 1005 : eah->flat_size = 0;
2700 : :
2701 : : /* Update dimensionality info if needed */
2702 [ + + ]: 1005 : if (dimschanged)
2703 : : {
2704 : 980 : eah->ndims = ndim;
2705 : 980 : memcpy(eah->dims, dim, ndim * sizeof(int));
2706 : 980 : memcpy(eah->lbound, lb, ndim * sizeof(int));
2707 : : }
2708 : :
2709 : : /* Reposition items if needed, and fill addedbefore items with nulls */
2710 [ + + ]: 1005 : if (addedbefore > 0)
2711 : : {
2712 : 40 : memmove(dvalues + addedbefore, dvalues, eah->nelems * sizeof(Datum));
2713 [ + + ]: 80 : for (i = 0; i < addedbefore; i++)
2714 : 40 : dvalues[i] = (Datum) 0;
2715 [ - + ]: 40 : if (dnulls)
2716 : : {
3768 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 : : }
3768 tgl@sss.pgh.pa.us 2721 :CBC 40 : eah->nelems += addedbefore;
2722 : : }
2723 : :
2724 : : /* fill addedafter items with nulls */
2725 [ + + ]: 1005 : if (addedafter > 0)
2726 : : {
2727 [ + + ]: 1880 : for (i = 0; i < addedafter; i++)
2728 : 940 : dvalues[eah->nelems + i] = (Datum) 0;
2729 [ - + ]: 940 : if (dnulls)
2730 : : {
3768 tgl@sss.pgh.pa.us 2731 [ # # ]:UBC 0 : for (i = 0; i < addedafter; i++)
2732 : 0 : dnulls[eah->nelems + i] = true;
2733 : : }
3768 tgl@sss.pgh.pa.us 2734 :CBC 940 : eah->nelems += addedafter;
2735 : : }
2736 : :
2737 : : /* Grab old element value for pfree'ing, if needed. */
2738 [ + + - + : 1005 : if (!eah->typbyval && (dnulls == NULL || !dnulls[offset]))
- - ]
2739 : 449 : oldValue = (char *) DatumGetPointer(dvalues[offset]);
2740 : : else
2741 : 556 : oldValue = NULL;
2742 : :
2743 : : /* And finally we can insert the new element. */
2744 : 1005 : dvalues[offset] = dataValue;
2745 [ - + ]: 1005 : if (dnulls)
3768 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 : : */
3768 tgl@sss.pgh.pa.us 2753 [ + + ]:CBC 1005 : 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)
3768 tgl@sss.pgh.pa.us 2757 :UBC 0 : pfree(oldValue);
2758 : : }
2759 : :
2760 : : /* Done, return standard TOAST pointer for object */
3768 tgl@sss.pgh.pa.us 2761 :CBC 1005 : 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
3855 2806 : 131 : 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 */
7233 2845 [ - + ]: 131 : if (isNull)
3855 tgl@sss.pgh.pa.us 2846 :UBC 0 : return arraydatum;
2847 : :
7233 tgl@sss.pgh.pa.us 2848 [ - + ]:CBC 131 : if (arraytyplen > 0)
2849 : : {
2850 : : /*
2851 : : * fixed-length arrays -- not got round to doing this...
2852 : : */
8077 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 */
3855 tgl@sss.pgh.pa.us 2859 :CBC 131 : array = DatumGetArrayTypeP(arraydatum);
2860 : 131 : srcArray = DatumGetArrayTypeP(srcArrayDatum);
2861 : :
2862 : : /* note: we assume srcArray contains no toasted elements */
2863 : :
9177 2864 : 131 : 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 : : */
8107 2871 [ + + ]: 131 : if (ndim == 0)
2872 : : {
2873 : : Datum *dvalues;
2874 : : bool *dnulls;
2875 : : int nelems;
8069 bruce@momjian.us 2876 : 28 : Oid elmtype = ARR_ELEMTYPE(array);
2877 : :
8107 tgl@sss.pgh.pa.us 2878 : 28 : deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
2879 : : &dvalues, &dnulls, &nelems);
2880 : :
2881 [ + + ]: 56 : for (i = 0; i < nSubscripts; i++)
2882 : : {
3546 2883 [ + + - + ]: 37 : 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 : :
2890 : : /* compute "upperIndx[i] - lowerIndx[i] + 1", detecting overflow */
410 nathan@postgresql.or 2891 [ + + + + ]: 65 : if (pg_sub_s32_overflow(upperIndx[i], lowerIndx[i], &dim[i]) ||
2892 : 31 : pg_add_s32_overflow(dim[i], 1, &dim[i]))
2893 [ + - ]: 6 : ereport(ERROR,
2894 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2895 : : errmsg("array size exceeds the maximum allowed (%d)",
2896 : : (int) MaxArraySize)));
2897 : :
8107 tgl@sss.pgh.pa.us 2898 : 28 : lb[i] = lowerIndx[i];
2899 : : }
2900 : :
2901 : : /* complain if too few source items; we ignore extras, however */
7760 2902 [ - + ]: 19 : if (nelems < ArrayGetNItems(nSubscripts, dim))
7760 tgl@sss.pgh.pa.us 2903 [ # # ]:UBC 0 : ereport(ERROR,
2904 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2905 : : errmsg("source array too small")));
2906 : :
3855 tgl@sss.pgh.pa.us 2907 :CBC 19 : return PointerGetDatum(construct_md_array(dvalues, dnulls, nSubscripts,
2908 : : dim, lb, elmtype,
2909 : : elmlen, elmbyval, elmalign));
2910 : : }
2911 : :
8571 2912 [ + - + - : 103 : if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
- + ]
8077 tgl@sss.pgh.pa.us 2913 [ # # ]:UBC 0 : ereport(ERROR,
2914 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2915 : : errmsg("wrong number of array subscripts")));
2916 : :
2917 : : /* copy dim/lb since we may modify them */
9176 tgl@sss.pgh.pa.us 2918 :CBC 103 : memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
2919 : 103 : memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
2920 : :
6917 2921 [ + + - + ]: 103 : newhasnulls = (ARR_HASNULL(array) || ARR_HASNULL(srcArray));
2922 : 103 : addedbefore = addedafter = 0;
2923 : :
2924 : : /*
2925 : : * Check subscripts. We assume the existing subscripts passed
2926 : : * ArrayCheckBounds, so that dim[i] + lb[i] can be computed without
2927 : : * overflow. But we must beware of other overflows in our calculations of
2928 : : * new dim[] values.
2929 : : */
2930 [ + + ]: 103 : if (ndim == 1)
2931 : : {
2932 [ - + ]: 88 : Assert(nSubscripts == 1);
3546 2933 [ + + ]: 88 : if (!lowerProvided[0])
2934 : 18 : lowerIndx[0] = lb[0];
2935 [ + + ]: 88 : if (!upperProvided[0])
2936 : 21 : upperIndx[0] = dim[0] + lb[0] - 1;
6917 2937 [ - + ]: 88 : if (lowerIndx[0] > upperIndx[0])
8077 tgl@sss.pgh.pa.us 2938 [ # # ]:UBC 0 : ereport(ERROR,
2939 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2940 : : errmsg("upper bound cannot be less than lower bound")));
6917 tgl@sss.pgh.pa.us 2941 [ + + ]:CBC 88 : if (lowerIndx[0] < lb[0])
2942 : : {
2943 : : /* addedbefore = lb[0] - lowerIndx[0]; */
2944 : : /* dim[0] += addedbefore; */
670 2945 [ + - - + ]: 48 : if (pg_sub_s32_overflow(lb[0], lowerIndx[0], &addedbefore) ||
2946 : 24 : pg_add_s32_overflow(dim[0], addedbefore, &dim[0]))
670 tgl@sss.pgh.pa.us 2947 [ # # ]:UBC 0 : ereport(ERROR,
2948 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2949 : : errmsg("array size exceeds the maximum allowed (%d)",
2950 : : (int) MaxArraySize)));
6917 tgl@sss.pgh.pa.us 2951 :CBC 24 : lb[0] = lowerIndx[0];
670 2952 [ + + ]: 24 : if (addedbefore > 1)
2953 : 18 : newhasnulls = true; /* will insert nulls */
2954 : : }
6917 2955 [ + + ]: 88 : if (upperIndx[0] >= (dim[0] + lb[0]))
2956 : : {
2957 : : /* addedafter = upperIndx[0] - (dim[0] + lb[0]) + 1; */
2958 : : /* dim[0] += addedafter; */
670 2959 [ + + + - ]: 59 : if (pg_sub_s32_overflow(upperIndx[0], dim[0] + lb[0], &addedafter) ||
2960 [ - + ]: 56 : pg_add_s32_overflow(addedafter, 1, &addedafter) ||
2961 : 28 : pg_add_s32_overflow(dim[0], addedafter, &dim[0]))
2962 [ + - ]: 3 : ereport(ERROR,
2963 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2964 : : errmsg("array size exceeds the maximum allowed (%d)",
2965 : : (int) MaxArraySize)));
2966 [ + + ]: 28 : if (addedafter > 1)
2999 2967 : 18 : newhasnulls = true; /* will insert nulls */
2968 : : }
2969 : : }
2970 : : else
2971 : : {
2972 : : /*
2973 : : * XXX currently we do not support extending multi-dimensional arrays
2974 : : * during assignment
2975 : : */
6917 2976 [ + + ]: 51 : for (i = 0; i < nSubscripts; i++)
2977 : : {
3546 2978 [ + + ]: 36 : if (!lowerProvided[i])
2979 : 6 : lowerIndx[i] = lb[i];
2980 [ + + ]: 36 : if (!upperProvided[i])
2981 : 12 : upperIndx[i] = dim[i] + lb[i] - 1;
6917 2982 [ - + ]: 36 : if (lowerIndx[i] > upperIndx[i])
6917 tgl@sss.pgh.pa.us 2983 [ # # ]:UBC 0 : ereport(ERROR,
2984 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2985 : : errmsg("upper bound cannot be less than lower bound")));
6917 tgl@sss.pgh.pa.us 2986 [ + - ]:CBC 36 : if (lowerIndx[i] < lb[i] ||
2987 [ - + ]: 36 : upperIndx[i] >= (dim[i] + lb[i]))
8077 tgl@sss.pgh.pa.us 2988 [ # # ]:UBC 0 : ereport(ERROR,
2989 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2990 : : errmsg("array subscript out of range")));
2991 : : }
2992 : : /* fill any missing subscript positions with full array range */
6917 tgl@sss.pgh.pa.us 2993 [ - + ]:CBC 15 : for (; i < ndim; i++)
2994 : : {
6917 tgl@sss.pgh.pa.us 2995 :UBC 0 : lowerIndx[i] = lb[i];
2996 : 0 : upperIndx[i] = dim[i] + lb[i] - 1;
2997 [ # # ]: 0 : if (lowerIndx[i] > upperIndx[i])
8077 2998 [ # # ]: 0 : ereport(ERROR,
2999 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
3000 : : errmsg("upper bound cannot be less than lower bound")));
3001 : : }
3002 : : }
3003 : :
3004 : : /* Do this mainly to check for overflow */
7233 tgl@sss.pgh.pa.us 3005 :CBC 100 : nitems = ArrayGetNItems(ndim, dim);
1580 3006 : 100 : ArrayCheckBounds(ndim, dim, lb);
3007 : :
3008 : : /*
3009 : : * Make sure source array has enough entries. Note we ignore the shape of
3010 : : * the source array and just read entries serially.
3011 : : */
9177 3012 : 100 : mda_get_range(ndim, span, lowerIndx, upperIndx);
9176 3013 : 100 : nsrcitems = ArrayGetNItems(ndim, span);
3014 [ + + ]: 100 : if (nsrcitems > ArrayGetNItems(ARR_NDIM(srcArray), ARR_DIMS(srcArray)))
8077 3015 [ + - ]: 3 : ereport(ERROR,
3016 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
3017 : : errmsg("source array too small")));
3018 : :
3019 : : /*
3020 : : * Compute space occupied by new entries, space occupied by replaced
3021 : : * entries, and required space for new array.
3022 : : */
6917 3023 [ + + ]: 97 : if (newhasnulls)
7233 3024 : 48 : overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
3025 : : else
3026 : 49 : overheadlen = ARR_OVERHEAD_NONULLS(ndim);
3027 [ + + ]: 97 : newitemsize = array_nelems_size(ARR_DATA_PTR(srcArray), 0,
3028 [ + + ]: 97 : ARR_NULLBITMAP(srcArray), nsrcitems,
3029 : : elmlen, elmbyval, elmalign);
3030 [ + + ]: 97 : oldoverheadlen = ARR_DATA_OFFSET(array);
3031 : 97 : olddatasize = ARR_SIZE(array) - oldoverheadlen;
9176 3032 [ + + ]: 97 : if (ndim > 1)
3033 : : {
3034 : : /*
3035 : : * here we do not need to cope with extension of the array; it would
3036 : : * be a lot more complicated if we had to do so...
3037 : : */
7233 3038 [ - + ]: 15 : olditemsize = array_slice_size(ARR_DATA_PTR(array),
3039 [ - + ]: 15 : ARR_NULLBITMAP(array),
3040 : : ndim, dim, lb,
3041 : : lowerIndx, upperIndx,
3042 : : elmlen, elmbyval, elmalign);
2999 3043 : 15 : lenbefore = lenafter = 0; /* keep compiler quiet */
7233 3044 : 15 : itemsbefore = itemsafter = nolditems = 0;
3045 : : }
3046 : : else
3047 : : {
3048 : : /*
3049 : : * here we must allow for possibility of slice larger than orig array
3050 : : * and/or not adjacent to orig array subscripts
3051 : : */
8934 bruce@momjian.us 3052 : 82 : int oldlb = ARR_LBOUND(array)[0];
3053 : 82 : int oldub = oldlb + ARR_DIMS(array)[0] - 1;
8601 3054 : 82 : int slicelb = Max(oldlb, lowerIndx[0]);
3055 : 82 : int sliceub = Min(oldub, upperIndx[0]);
8934 3056 [ + + ]: 82 : char *oldarraydata = ARR_DATA_PTR(array);
7233 tgl@sss.pgh.pa.us 3057 [ + + ]: 82 : bits8 *oldarraybitmap = ARR_NULLBITMAP(array);
3058 : :
3059 : : /* count/size of old array entries that will go before the slice */
6917 3060 : 82 : itemsbefore = Min(slicelb, oldub + 1) - oldlb;
7233 3061 : 82 : lenbefore = array_nelems_size(oldarraydata, 0, oldarraybitmap,
3062 : : itemsbefore,
3063 : : elmlen, elmbyval, elmalign);
3064 : : /* count/size of old array entries that will be replaced by slice */
9176 3065 [ + + ]: 82 : if (slicelb > sliceub)
3066 : : {
7233 3067 : 27 : nolditems = 0;
9176 3068 : 27 : olditemsize = 0;
3069 : : }
3070 : : else
3071 : : {
7233 3072 : 55 : nolditems = sliceub - slicelb + 1;
9176 3073 : 55 : olditemsize = array_nelems_size(oldarraydata + lenbefore,
3074 : : itemsbefore, oldarraybitmap,
3075 : : nolditems,
3076 : : elmlen, elmbyval, elmalign);
3077 : : }
3078 : : /* count/size of old array entries that will go after the slice */
5346 3079 : 82 : itemsafter = oldub + 1 - Max(sliceub + 1, oldlb);
9176 3080 : 82 : lenafter = olddatasize - lenbefore - olditemsize;
3081 : : }
3082 : :
3083 : 97 : newsize = overheadlen + olddatasize - olditemsize + newitemsize;
3084 : :
5246 3085 : 97 : newarray = (ArrayType *) palloc0(newsize);
6766 3086 : 97 : SET_VARSIZE(newarray, newsize);
9176 3087 : 97 : newarray->ndim = ndim;
7233 3088 [ + + ]: 97 : newarray->dataoffset = newhasnulls ? overheadlen : 0;
8412 3089 : 97 : newarray->elemtype = ARR_ELEMTYPE(array);
9176 3090 : 97 : memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
3091 : 97 : memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
3092 : :
3093 [ + + ]: 97 : if (ndim > 1)
3094 : : {
3095 : : /*
3096 : : * here we do not need to cope with extension of the array; it would
3097 : : * be a lot more complicated if we had to do so...
3098 : : */
7233 3099 : 15 : array_insert_slice(newarray, array, srcArray,
3100 : : ndim, dim, lb,
3101 : : lowerIndx, upperIndx,
3102 : : elmlen, elmbyval, elmalign);
3103 : : }
3104 : : else
3105 : : {
3106 : : /* fill in data */
9176 3107 : 82 : memcpy((char *) newarray + overheadlen,
3108 : : (char *) array + oldoverheadlen,
3109 : : lenbefore);
3110 : 164 : memcpy((char *) newarray + overheadlen + lenbefore,
3111 [ + + ]: 82 : ARR_DATA_PTR(srcArray),
3112 : : newitemsize);
3113 : 82 : memcpy((char *) newarray + overheadlen + lenbefore + newitemsize,
7233 3114 : 82 : (char *) array + oldoverheadlen + lenbefore + olditemsize,
3115 : : lenafter);
3116 : : /* fill in nulls bitmap if needed */
3117 [ + + ]: 82 : if (newhasnulls)
3118 : : {
7228 bruce@momjian.us 3119 [ + - ]: 48 : bits8 *newnullbitmap = ARR_NULLBITMAP(newarray);
3120 [ + - ]: 48 : bits8 *oldnullbitmap = ARR_NULLBITMAP(array);
3121 : :
3122 : : /* palloc0 above already marked any inserted positions as nulls */
6917 tgl@sss.pgh.pa.us 3123 : 48 : array_bitmap_copy(newnullbitmap, addedbefore,
3124 : : oldnullbitmap, 0,
3125 : : itemsbefore);
3126 : 48 : array_bitmap_copy(newnullbitmap, lowerIndx[0] - lb[0],
7233 3127 [ + + ]: 48 : ARR_NULLBITMAP(srcArray), 0,
3128 : : nsrcitems);
6917 3129 : 48 : array_bitmap_copy(newnullbitmap, addedbefore + itemsbefore + nolditems,
3130 : : oldnullbitmap, itemsbefore + nolditems,
3131 : : itemsafter);
3132 : : }
3133 : : }
3134 : :
3855 3135 : 97 : return PointerGetDatum(newarray);
3136 : : }
3137 : :
3138 : : /*
3139 : : * array_ref : backwards compatibility wrapper for array_get_element
3140 : : *
3141 : : * This only works for detoasted/flattened varlena arrays, since the array
3142 : : * argument is declared as "ArrayType *". However there's enough code like
3143 : : * that to justify preserving this API.
3144 : : */
3145 : : Datum
3146 : 21033 : array_ref(ArrayType *array, int nSubscripts, int *indx,
3147 : : int arraytyplen, int elmlen, bool elmbyval, char elmalign,
3148 : : bool *isNull)
3149 : : {
3150 : 21033 : return array_get_element(PointerGetDatum(array), nSubscripts, indx,
3151 : : arraytyplen, elmlen, elmbyval, elmalign,
3152 : : isNull);
3153 : : }
3154 : :
3155 : : /*
3156 : : * array_set : backwards compatibility wrapper for array_set_element
3157 : : *
3158 : : * This only works for detoasted/flattened varlena arrays, since the array
3159 : : * argument and result are declared as "ArrayType *". However there's enough
3160 : : * code like that to justify preserving this API.
3161 : : */
3162 : : ArrayType *
3163 : 509 : array_set(ArrayType *array, int nSubscripts, int *indx,
3164 : : Datum dataValue, bool isNull,
3165 : : int arraytyplen, int elmlen, bool elmbyval, char elmalign)
3166 : : {
3167 : 509 : return DatumGetArrayTypeP(array_set_element(PointerGetDatum(array),
3168 : : nSubscripts, indx,
3169 : : dataValue, isNull,
3170 : : arraytyplen,
3171 : : elmlen, elmbyval, elmalign));
3172 : : }
3173 : :
3174 : : /*
3175 : : * array_map()
3176 : : *
3177 : : * Map an array through an arbitrary expression. Return a new array with
3178 : : * the same dimensions and each source element transformed by the given,
3179 : : * already-compiled expression. Each source element is placed in the
3180 : : * innermost_caseval/innermost_casenull fields of the ExprState.
3181 : : *
3182 : : * Parameters are:
3183 : : * * arrayd: Datum representing array argument.
3184 : : * * exprstate: ExprState representing the per-element transformation.
3185 : : * * econtext: context for expression evaluation.
3186 : : * * retType: OID of element type of output array. This must be the same as,
3187 : : * or binary-compatible with, the result type of the expression. It might
3188 : : * be different from the input array's element type.
3189 : : * * amstate: workspace for array_map. Must be zeroed by caller before
3190 : : * first call, and not touched after that.
3191 : : *
3192 : : * It is legitimate to pass a freshly-zeroed ArrayMapState on each call,
3193 : : * but better performance can be had if the state can be preserved across
3194 : : * a series of calls.
3195 : : *
3196 : : * NB: caller must assure that input array is not NULL. NULL elements in
3197 : : * the array are OK however.
3198 : : * NB: caller should be running in econtext's per-tuple memory context.
3199 : : */
3200 : : Datum
2898 3201 : 20517 : array_map(Datum arrayd,
3202 : : ExprState *exprstate, ExprContext *econtext,
3203 : : Oid retType, ArrayMapState *amstate)
3204 : : {
3205 : 20517 : AnyArrayType *v = DatumGetAnyArrayP(arrayd);
3206 : : ArrayType *result;
3207 : : Datum *values;
3208 : : bool *nulls;
3209 : : int *dim;
3210 : : int ndim;
3211 : : int nitems;
3212 : : int i;
7233 3213 : 20517 : int32 nbytes = 0;
3214 : : int32 dataoffset;
3215 : : bool hasnulls;
3216 : : Oid inpType;
3217 : : int inp_typlen;
3218 : : bool inp_typbyval;
3219 : : char inp_typalign;
3220 : : int typlen;
3221 : : bool typbyval;
3222 : : char typalign;
3223 : : array_iter iter;
3224 : : ArrayMetaState *inp_extra;
3225 : : ArrayMetaState *ret_extra;
2898 3226 : 20517 : Datum *transform_source = exprstate->innermost_caseval;
3227 : 20517 : bool *transform_source_isnull = exprstate->innermost_casenull;
3228 : :
3768 3229 [ - + ]: 20517 : inpType = AARR_ELEMTYPE(v);
3230 [ - + ]: 20517 : ndim = AARR_NDIM(v);
3231 [ - + ]: 20517 : dim = AARR_DIMS(v);
9177 3232 : 20517 : nitems = ArrayGetNItems(ndim, dim);
3233 : :
3234 : : /* Check for empty array */
9601 bruce@momjian.us 3235 [ + + ]: 20517 : if (nitems <= 0)
3236 : : {
3237 : : /* Return empty array */
2898 tgl@sss.pgh.pa.us 3238 : 6 : return PointerGetDatum(construct_empty_array(retType));
3239 : : }
3240 : :
3241 : : /*
3242 : : * We arrange to look up info about input and return element types only
3243 : : * once per series of calls, assuming the element type doesn't change
3244 : : * underneath us.
3245 : : */
7471 3246 : 20511 : inp_extra = &amstate->inp_extra;
3247 : 20511 : ret_extra = &amstate->ret_extra;
3248 : :
8107 3249 [ + + ]: 20511 : if (inp_extra->element_type != inpType)
3250 : : {
3251 : 211 : get_typlenbyvalalign(inpType,
3252 : : &inp_extra->typlen,
3253 : : &inp_extra->typbyval,
3254 : : &inp_extra->typalign);
3255 : 211 : inp_extra->element_type = inpType;
3256 : : }
3257 : 20511 : inp_typlen = inp_extra->typlen;
3258 : 20511 : inp_typbyval = inp_extra->typbyval;
3259 : 20511 : inp_typalign = inp_extra->typalign;
3260 : :
3261 [ + + ]: 20511 : if (ret_extra->element_type != retType)
3262 : : {
3263 : 211 : get_typlenbyvalalign(retType,
3264 : : &ret_extra->typlen,
3265 : : &ret_extra->typbyval,
3266 : : &ret_extra->typalign);
3267 : 211 : ret_extra->element_type = retType;
3268 : : }
3269 : 20511 : typlen = ret_extra->typlen;
3270 : 20511 : typbyval = ret_extra->typbyval;
3271 : 20511 : typalign = ret_extra->typalign;
3272 : :
3273 : : /* Allocate temporary arrays for new values */
9182 3274 : 20511 : values = (Datum *) palloc(nitems * sizeof(Datum));
7233 3275 : 20511 : nulls = (bool *) palloc(nitems * sizeof(bool));
3276 : :
3277 : : /* Loop over source data */
3768 3278 : 20511 : array_iter_setup(&iter, v);
7233 3279 : 20511 : hasnulls = false;
3280 : :
9601 bruce@momjian.us 3281 [ + + ]: 231316 : for (i = 0; i < nitems; i++)
3282 : : {
3283 : : /* Get source element, checking for NULL */
2898 tgl@sss.pgh.pa.us 3284 : 210821 : *transform_source =
3285 : 210821 : array_iter_next(&iter, transform_source_isnull, i,
3286 : : inp_typlen, inp_typbyval, inp_typalign);
3287 : :
3288 : : /* Apply the given expression to source element */
3289 : 210821 : values[i] = ExecEvalExpr(exprstate, econtext, &nulls[i]);
3290 : :
3291 [ + + ]: 210805 : if (nulls[i])
7233 3292 : 6 : hasnulls = true;
3293 : : else
3294 : : {
3295 : : /* Ensure data is not toasted */
3296 [ + + ]: 210799 : if (typlen == -1)
3297 : 202 : values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
3298 : : /* Update total result size */
6728 3299 [ + + + - : 210799 : nbytes = att_addlength_datum(nbytes, typlen, values[i]);
- - - - -
- - - - +
- - ]
3300 [ + + + + : 210799 : nbytes = att_align_nominal(nbytes, typalign);
+ + - + ]
3301 : : /* check for overflow of total request */
7233 3302 [ - + ]: 210799 : if (!AllocSizeIsValid(nbytes))
7233 tgl@sss.pgh.pa.us 3303 [ # # ]:UBC 0 : ereport(ERROR,
3304 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3305 : : errmsg("array size exceeds the maximum allowed (%d)",
3306 : : (int) MaxAllocSize)));
3307 : : }
3308 : : }
3309 : :
3310 : : /* Allocate and fill the result array */
7233 tgl@sss.pgh.pa.us 3311 [ + + ]:CBC 20495 : if (hasnulls)
3312 : : {
3313 : 3 : dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
3314 : 3 : nbytes += dataoffset;
3315 : : }
3316 : : else
3317 : : {
3318 : 20492 : dataoffset = 0; /* marker for no null bitmap */
3319 : 20492 : nbytes += ARR_OVERHEAD_NONULLS(ndim);
3320 : : }
5246 3321 : 20495 : result = (ArrayType *) palloc0(nbytes);
6766 3322 : 20495 : SET_VARSIZE(result, nbytes);
9182 3323 : 20495 : result->ndim = ndim;
7233 3324 : 20495 : result->dataoffset = dataoffset;
8412 3325 : 20495 : result->elemtype = retType;
3768 3326 [ - + ]: 20495 : memcpy(ARR_DIMS(result), AARR_DIMS(v), ndim * sizeof(int));
3327 [ - + ]: 20495 : memcpy(ARR_LBOUND(result), AARR_LBOUND(v), ndim * sizeof(int));
3328 : :
7233 3329 : 20495 : CopyArrayEls(result,
3330 : : values, nulls, nitems,
3331 : : typlen, typbyval, typalign,
3332 : : false);
3333 : :
3334 : : /*
3335 : : * Note: do not risk trying to pfree the results of the called expression
3336 : : */
9182 3337 : 20495 : pfree(values);
7233 3338 : 20495 : pfree(nulls);
3339 : :
2898 3340 : 20495 : return PointerGetDatum(result);
3341 : : }
3342 : :
3343 : : /*
3344 : : * construct_array --- simple method for constructing an array object
3345 : : *
3346 : : * elems: array of Datum items to become the array contents
3347 : : * (NULL element values are not supported).
3348 : : * nelems: number of items
3349 : : * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
3350 : : *
3351 : : * A palloc'd 1-D array object is constructed and returned. Note that
3352 : : * elem values will be copied into the object even if pass-by-ref type.
3353 : : * Also note the result will be 0-D not 1-D if nelems = 0.
3354 : : *
3355 : : * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
3356 : : * from the system catalogs, given the elmtype. However, the caller is
3357 : : * in a better position to cache this info across multiple uses, or even
3358 : : * to hard-wire values if the element type is hard-wired.
3359 : : */
3360 : : ArrayType *
9182 3361 : 167715 : construct_array(Datum *elems, int nelems,
3362 : : Oid elmtype,
3363 : : int elmlen, bool elmbyval, char elmalign)
3364 : : {
3365 : : int dims[1];
3366 : : int lbs[1];
3367 : :
8187 3368 : 167715 : dims[0] = nelems;
3369 : 167715 : lbs[0] = 1;
3370 : :
7233 3371 : 167715 : return construct_md_array(elems, NULL, 1, dims, lbs,
3372 : : elmtype, elmlen, elmbyval, elmalign);
3373 : : }
3374 : :
3375 : : /*
3376 : : * Like construct_array(), where elmtype must be a built-in type, and
3377 : : * elmlen/elmbyval/elmalign is looked up from hardcoded data. This is often
3378 : : * useful when manipulating arrays from/for system catalogs.
3379 : : */
3380 : : ArrayType *
1163 peter@eisentraut.org 3381 : 120173 : construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
3382 : : {
3383 : : int elmlen;
3384 : : bool elmbyval;
3385 : : char elmalign;
3386 : :
3387 [ + + + + : 120173 : switch (elmtype)
+ + + + +
+ + + - ]
3388 : : {
3389 : 1654 : case CHAROID:
3390 : 1654 : elmlen = 1;
3391 : 1654 : elmbyval = true;
3392 : 1654 : elmalign = TYPALIGN_CHAR;
3393 : 1654 : break;
3394 : :
3395 : 3869 : case CSTRINGOID:
3396 : 3869 : elmlen = -2;
3397 : 3869 : elmbyval = false;
3398 : 3869 : elmalign = TYPALIGN_CHAR;
3399 : 3869 : break;
3400 : :
3401 : 67532 : case FLOAT4OID:
3402 : 67532 : elmlen = sizeof(float4);
3403 : 67532 : elmbyval = true;
3404 : 67532 : elmalign = TYPALIGN_INT;
3405 : 67532 : break;
3406 : :
327 msawada@postgresql.o 3407 : 24 : case FLOAT8OID:
3408 : 24 : elmlen = sizeof(float8);
24 tgl@sss.pgh.pa.us 3409 :GNC 24 : elmbyval = true;
327 msawada@postgresql.o 3410 :CBC 24 : elmalign = TYPALIGN_DOUBLE;
3411 : 24 : break;
3412 : :
1163 peter@eisentraut.org 3413 : 28176 : case INT2OID:
3414 : 28176 : elmlen = sizeof(int16);
3415 : 28176 : elmbyval = true;
3416 : 28176 : elmalign = TYPALIGN_SHORT;
3417 : 28176 : break;
3418 : :
3419 : 3501 : case INT4OID:
3420 : 3501 : elmlen = sizeof(int32);
3421 : 3501 : elmbyval = true;
3422 : 3501 : elmalign = TYPALIGN_INT;
3423 : 3501 : break;
3424 : :
3425 : 2 : case INT8OID:
3426 : 2 : elmlen = sizeof(int64);
24 tgl@sss.pgh.pa.us 3427 :GNC 2 : elmbyval = true;
1163 peter@eisentraut.org 3428 :CBC 2 : elmalign = TYPALIGN_DOUBLE;
3429 : 2 : break;
3430 : :
3431 : 358 : case NAMEOID:
3432 : 358 : elmlen = NAMEDATALEN;
3433 : 358 : elmbyval = false;
3434 : 358 : elmalign = TYPALIGN_CHAR;
3435 : 358 : break;
3436 : :
3437 : 8551 : case OIDOID:
3438 : : case REGTYPEOID:
3439 : 8551 : elmlen = sizeof(Oid);
3440 : 8551 : elmbyval = true;
3441 : 8551 : elmalign = TYPALIGN_INT;
3442 : 8551 : break;
3443 : :
3444 : 6483 : case TEXTOID:
3445 : 6483 : elmlen = -1;
3446 : 6483 : elmbyval = false;
3447 : 6483 : elmalign = TYPALIGN_INT;
3448 : 6483 : break;
3449 : :
3450 : 21 : case TIDOID:
3451 : 21 : elmlen = sizeof(ItemPointerData);
3452 : 21 : elmbyval = false;
3453 : 21 : elmalign = TYPALIGN_SHORT;
3454 : 21 : break;
3455 : :
327 msawada@postgresql.o 3456 : 2 : case XIDOID:
3457 : 2 : elmlen = sizeof(TransactionId);
3458 : 2 : elmbyval = true;
3459 : 2 : elmalign = TYPALIGN_INT;
3460 : 2 : break;
3461 : :
1163 peter@eisentraut.org 3462 :UBC 0 : default:
3463 [ # # ]: 0 : elog(ERROR, "type %u not supported by construct_array_builtin()", elmtype);
3464 : : /* keep compiler quiet */
3465 : : elmlen = 0;
3466 : : elmbyval = false;
3467 : : elmalign = 0;
3468 : : }
3469 : :
1163 peter@eisentraut.org 3470 :CBC 120173 : return construct_array(elems, nelems, elmtype, elmlen, elmbyval, elmalign);
3471 : : }
3472 : :
3473 : : /*
3474 : : * construct_md_array --- simple method for constructing an array object
3475 : : * with arbitrary dimensions and possible NULLs
3476 : : *
3477 : : * elems: array of Datum items to become the array contents
3478 : : * nulls: array of is-null flags (can be NULL if no nulls)
3479 : : * ndims: number of dimensions
3480 : : * dims: integer array with size of each dimension
3481 : : * lbs: integer array with lower bound of each dimension
3482 : : * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
3483 : : *
3484 : : * A palloc'd ndims-D array object is constructed and returned. Note that
3485 : : * elem values will be copied into the object even if pass-by-ref type.
3486 : : * Also note the result will be 0-D not ndims-D if any dims[i] = 0.
3487 : : *
3488 : : * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
3489 : : * from the system catalogs, given the elmtype. However, the caller is
3490 : : * in a better position to cache this info across multiple uses, or even
3491 : : * to hard-wire values if the element type is hard-wired.
3492 : : */
3493 : : ArrayType *
8187 tgl@sss.pgh.pa.us 3494 : 781267 : construct_md_array(Datum *elems,
3495 : : bool *nulls,
3496 : : int ndims,
3497 : : int *dims,
3498 : : int *lbs,
3499 : : Oid elmtype, int elmlen, bool elmbyval, char elmalign)
3500 : : {
3501 : : ArrayType *result;
3502 : : bool hasnulls;
3503 : : int32 nbytes;
3504 : : int32 dataoffset;
3505 : : int i;
3506 : : int nelems;
3507 : :
8077 3508 [ - + ]: 781267 : if (ndims < 0) /* we do allow zero-dimension arrays */
8077 tgl@sss.pgh.pa.us 3509 [ # # ]:UBC 0 : ereport(ERROR,
3510 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3511 : : errmsg("invalid number of dimensions: %d", ndims)));
8077 tgl@sss.pgh.pa.us 3512 [ - + ]:CBC 781267 : if (ndims > MAXDIM)
8077 tgl@sss.pgh.pa.us 3513 [ # # ]:UBC 0 : ereport(ERROR,
3514 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3515 : : errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
3516 : : ndims, MAXDIM)));
3517 : :
3518 : : /* This checks for overflow of the array dimensions */
8187 tgl@sss.pgh.pa.us 3519 :CBC 781267 : nelems = ArrayGetNItems(ndims, dims);
1580 3520 : 781267 : ArrayCheckBounds(ndims, dims, lbs);
3521 : :
3522 : : /* if ndims <= 0 or any dims[i] == 0, return empty array */
2903 3523 [ + + ]: 781267 : if (nelems <= 0)
3524 : 29050 : return construct_empty_array(elmtype);
3525 : :
3526 : : /* compute required space */
7233 3527 : 752217 : nbytes = 0;
3528 : 752217 : hasnulls = false;
3529 [ + + ]: 5491584 : for (i = 0; i < nelems; i++)
3530 : : {
3531 [ + + + + ]: 4739367 : if (nulls && nulls[i])
3532 : : {
3533 : 16376 : hasnulls = true;
3534 : 16376 : continue;
3535 : : }
3536 : : /* make sure data is not toasted */
3537 [ + + ]: 4722991 : if (elmlen == -1)
3538 : 1344118 : elems[i] = PointerGetDatum(PG_DETOAST_DATUM(elems[i]));
6728 3539 [ + + + + : 4722991 : nbytes = att_addlength_datum(nbytes, elmlen, elems[i]);
- + - - -
- - - - +
- + ]
3540 [ + + + + : 4722991 : nbytes = att_align_nominal(nbytes, elmalign);
+ + - + ]
3541 : : /* check for overflow of total request */
7233 3542 [ - + ]: 4722991 : if (!AllocSizeIsValid(nbytes))
7233 tgl@sss.pgh.pa.us 3543 [ # # ]:UBC 0 : ereport(ERROR,
3544 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3545 : : errmsg("array size exceeds the maximum allowed (%d)",
3546 : : (int) MaxAllocSize)));
3547 : : }
3548 : :
3549 : : /* Allocate and initialize result array */
7233 tgl@sss.pgh.pa.us 3550 [ + + ]:CBC 752217 : if (hasnulls)
3551 : : {
3552 : 8765 : dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nelems);
3553 : 8765 : nbytes += dataoffset;
3554 : : }
3555 : : else
3556 : : {
3557 : 743452 : dataoffset = 0; /* marker for no null bitmap */
3558 : 743452 : nbytes += ARR_OVERHEAD_NONULLS(ndims);
3559 : : }
5246 3560 : 752217 : result = (ArrayType *) palloc0(nbytes);
6766 3561 : 752217 : SET_VARSIZE(result, nbytes);
8187 3562 : 752217 : result->ndim = ndims;
7233 3563 : 752217 : result->dataoffset = dataoffset;
8412 3564 : 752217 : result->elemtype = elmtype;
7913 neilc@samurai.com 3565 : 752217 : memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
3566 : 752217 : memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
3567 : :
7233 tgl@sss.pgh.pa.us 3568 : 752217 : CopyArrayEls(result,
3569 : : elems, nulls, nelems,
3570 : : elmlen, elmbyval, elmalign,
3571 : : false);
3572 : :
3573 : 752217 : return result;
3574 : : }
3575 : :
3576 : : /*
3577 : : * construct_empty_array --- make a zero-dimensional array of given type
3578 : : */
3579 : : ArrayType *
3580 : 1433551 : construct_empty_array(Oid elmtype)
3581 : : {
3582 : : ArrayType *result;
3583 : :
5246 3584 : 1433551 : result = (ArrayType *) palloc0(sizeof(ArrayType));
6766 3585 : 1433551 : SET_VARSIZE(result, sizeof(ArrayType));
7233 3586 : 1433551 : result->ndim = 0;
3587 : 1433551 : result->dataoffset = 0;
3588 : 1433551 : result->elemtype = elmtype;
9182 3589 : 1433551 : return result;
3590 : : }
3591 : :
3592 : : /*
3593 : : * construct_empty_expanded_array: make an empty expanded array
3594 : : * given only type information. (metacache can be NULL if not needed.)
3595 : : */
3596 : : ExpandedArrayHeader *
3768 3597 : 12 : construct_empty_expanded_array(Oid element_type,
3598 : : MemoryContext parentcontext,
3599 : : ArrayMetaState *metacache)
3600 : : {
3601 : 12 : ArrayType *array = construct_empty_array(element_type);
3602 : : Datum d;
3603 : :
3604 : 12 : d = expand_array(PointerGetDatum(array), parentcontext, metacache);
3605 : 12 : pfree(array);
3606 : 12 : return (ExpandedArrayHeader *) DatumGetEOHP(d);
3607 : : }
3608 : :
3609 : : /*
3610 : : * deconstruct_array --- simple method for extracting data from an array
3611 : : *
3612 : : * array: array object to examine (must not be NULL)
3613 : : * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
3614 : : * elemsp: return value, set to point to palloc'd array of Datum values
3615 : : * nullsp: return value, set to point to palloc'd array of isnull markers
3616 : : * nelemsp: return value, set to number of extracted values
3617 : : *
3618 : : * The caller may pass nullsp == NULL if it does not support NULLs in the
3619 : : * array. Note that this produces a very uninformative error message,
3620 : : * so do it only in cases where a NULL is really not expected.
3621 : : *
3622 : : * If array elements are pass-by-ref data type, the returned Datums will
3623 : : * be pointers into the array object.
3624 : : *
3625 : : * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
3626 : : * from the system catalogs, given the elmtype. However, the caller is
3627 : : * in a better position to cache this info across multiple uses, or even
3628 : : * to hard-wire values if the element type is hard-wired.
3629 : : */
3630 : : void
9182 3631 : 1531004 : deconstruct_array(ArrayType *array,
3632 : : Oid elmtype,
3633 : : int elmlen, bool elmbyval, char elmalign,
3634 : : Datum **elemsp, bool **nullsp, int *nelemsp)
3635 : : {
3636 : : Datum *elems;
3637 : : bool *nulls;
3638 : : int nelems;
3639 : : char *p;
3640 : : bits8 *bitmap;
3641 : : int bitmask;
3642 : : int i;
3643 : :
8412 3644 [ - + ]: 1531004 : Assert(ARR_ELEMTYPE(array) == elmtype);
3645 : :
9177 3646 : 1531004 : nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
9182 3647 : 1531004 : *elemsp = elems = (Datum *) palloc(nelems * sizeof(Datum));
7233 3648 [ + + ]: 1531004 : if (nullsp)
5355 3649 : 932627 : *nullsp = nulls = (bool *) palloc0(nelems * sizeof(bool));
3650 : : else
7233 3651 : 598377 : nulls = NULL;
9182 3652 : 1531004 : *nelemsp = nelems;
3653 : :
3654 [ + + ]: 1531004 : p = ARR_DATA_PTR(array);
7233 3655 [ + + ]: 1531004 : bitmap = ARR_NULLBITMAP(array);
3656 : 1531004 : bitmask = 1;
3657 : :
9182 3658 [ + + ]: 26639424 : for (i = 0; i < nelems; i++)
3659 : : {
3660 : : /* Get source element, checking for NULL */
7233 3661 [ + + + + ]: 25108420 : if (bitmap && (*bitmap & bitmask) == 0)
3662 : : {
3663 : 1623 : elems[i] = (Datum) 0;
3664 [ + - ]: 1623 : if (nulls)
3665 : 1623 : nulls[i] = true;
3666 : : else
7233 tgl@sss.pgh.pa.us 3667 [ # # ]:UBC 0 : ereport(ERROR,
3668 : : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
3669 : : errmsg("null array element not allowed in this context")));
3670 : : }
3671 : : else
3672 : : {
7233 tgl@sss.pgh.pa.us 3673 :CBC 25106797 : elems[i] = fetch_att(p, elmbyval, elmlen);
6728 3674 [ + + + + : 25106797 : p = att_addlength_pointer(p, elmlen, p);
- + - - -
- - - - +
- + ]
3675 [ + + + + : 25106797 : p = (char *) att_align_nominal(p, elmalign);
+ + - + ]
3676 : : }
3677 : :
3678 : : /* advance bitmap pointer if any */
7233 3679 [ + + ]: 25108420 : if (bitmap)
3680 : : {
3681 : 2540 : bitmask <<= 1;
3682 [ + + ]: 2540 : if (bitmask == 0x100)
3683 : : {
3684 : 33 : bitmap++;
3685 : 33 : bitmask = 1;
3686 : : }
3687 : : }
3688 : : }
9182 3689 : 1531004 : }
3690 : :
3691 : : /*
3692 : : * Like deconstruct_array(), where elmtype must be a built-in type, and
3693 : : * elmlen/elmbyval/elmalign is looked up from hardcoded data. This is often
3694 : : * useful when manipulating arrays from/for system catalogs.
3695 : : */
3696 : : void
1163 peter@eisentraut.org 3697 : 223819 : deconstruct_array_builtin(ArrayType *array,
3698 : : Oid elmtype,
3699 : : Datum **elemsp, bool **nullsp, int *nelemsp)
3700 : : {
3701 : : int elmlen;
3702 : : bool elmbyval;
3703 : : char elmalign;
3704 : :
3705 [ + + + + : 223819 : switch (elmtype)
+ + + - ]
3706 : : {
3707 : 9 : case CHAROID:
3708 : 9 : elmlen = 1;
3709 : 9 : elmbyval = true;
3710 : 9 : elmalign = TYPALIGN_CHAR;
3711 : 9 : break;
3712 : :
3713 : 3869 : case CSTRINGOID:
3714 : 3869 : elmlen = -2;
3715 : 3869 : elmbyval = false;
3716 : 3869 : elmalign = TYPALIGN_CHAR;
3717 : 3869 : break;
3718 : :
3719 : 15 : case FLOAT8OID:
3720 : 15 : elmlen = sizeof(float8);
24 tgl@sss.pgh.pa.us 3721 :GNC 15 : elmbyval = true;
1163 peter@eisentraut.org 3722 :CBC 15 : elmalign = TYPALIGN_DOUBLE;
3723 : 15 : break;
3724 : :
3725 : 2839 : case INT2OID:
3726 : 2839 : elmlen = sizeof(int16);
3727 : 2839 : elmbyval = true;
3728 : 2839 : elmalign = TYPALIGN_SHORT;
3729 : 2839 : break;
3730 : :
3731 : 154 : case OIDOID:
3732 : 154 : elmlen = sizeof(Oid);
3733 : 154 : elmbyval = true;
3734 : 154 : elmalign = TYPALIGN_INT;
3735 : 154 : break;
3736 : :
3737 : 216911 : case TEXTOID:
3738 : 216911 : elmlen = -1;
3739 : 216911 : elmbyval = false;
3740 : 216911 : elmalign = TYPALIGN_INT;
3741 : 216911 : break;
3742 : :
3743 : 22 : case TIDOID:
3744 : 22 : elmlen = sizeof(ItemPointerData);
3745 : 22 : elmbyval = false;
3746 : 22 : elmalign = TYPALIGN_SHORT;
3747 : 22 : break;
3748 : :
1163 peter@eisentraut.org 3749 :UBC 0 : default:
3750 [ # # ]: 0 : elog(ERROR, "type %u not supported by deconstruct_array_builtin()", elmtype);
3751 : : /* keep compiler quiet */
3752 : : elmlen = 0;
3753 : : elmbyval = false;
3754 : : elmalign = 0;
3755 : : }
3756 : :
1163 peter@eisentraut.org 3757 :CBC 223819 : deconstruct_array(array, elmtype, elmlen, elmbyval, elmalign, elemsp, nullsp, nelemsp);
3758 : 223819 : }
3759 : :
3760 : : /*
3761 : : * array_contains_nulls --- detect whether an array has any null elements
3762 : : *
3763 : : * This gives an accurate answer, whereas testing ARR_HASNULL only tells
3764 : : * if the array *might* contain a null.
3765 : : */
3766 : : bool
5355 tgl@sss.pgh.pa.us 3767 : 38270 : array_contains_nulls(ArrayType *array)
3768 : : {
3769 : : int nelems;
3770 : : bits8 *bitmap;
3771 : : int bitmask;
3772 : :
3773 : : /* Easy answer if there's no null bitmap */
3774 [ + + ]: 38270 : if (!ARR_HASNULL(array))
3775 : 38239 : return false;
3776 : :
3777 : 31 : nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
3778 : :
3779 [ + - ]: 31 : bitmap = ARR_NULLBITMAP(array);
3780 : :
3781 : : /* check whole bytes of the bitmap byte-at-a-time */
3782 [ + + ]: 31 : while (nelems >= 8)
3783 : : {
3784 [ + - ]: 9 : if (*bitmap != 0xFF)
3785 : 9 : return true;
5355 tgl@sss.pgh.pa.us 3786 :UBC 0 : bitmap++;
3787 : 0 : nelems -= 8;
3788 : : }
3789 : :
3790 : : /* check last partial byte */
5355 tgl@sss.pgh.pa.us 3791 :CBC 22 : bitmask = 1;
3792 [ + - ]: 49 : while (nelems > 0)
3793 : : {
3794 [ + + ]: 49 : if ((*bitmap & bitmask) == 0)
3795 : 22 : return true;
3796 : 27 : bitmask <<= 1;
3797 : 27 : nelems--;
3798 : : }
3799 : :
5355 tgl@sss.pgh.pa.us 3800 :UBC 0 : return false;
3801 : : }
3802 : :
3803 : :
3804 : : /*
3805 : : * array_eq :
3806 : : * compares two arrays for equality
3807 : : * result :
3808 : : * returns true if the arrays are equal, false otherwise.
3809 : : *
3810 : : * Note: we do not use array_cmp here, since equality may be meaningful in
3811 : : * datatypes that don't have a total ordering (and hence no btree support).
3812 : : */
3813 : : Datum
9216 tgl@sss.pgh.pa.us 3814 :CBC 123936 : array_eq(PG_FUNCTION_ARGS)
3815 : : {
2415 andres@anarazel.de 3816 : 123936 : LOCAL_FCINFO(locfcinfo, 2);
2910 tgl@sss.pgh.pa.us 3817 : 123936 : AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
3818 : 123936 : AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
5261 3819 : 123936 : Oid collation = PG_GET_COLLATION();
3768 3820 [ + + ]: 123936 : int ndims1 = AARR_NDIM(array1);
3821 [ + + ]: 123936 : int ndims2 = AARR_NDIM(array2);
3822 [ + + ]: 123936 : int *dims1 = AARR_DIMS(array1);
3823 [ + + ]: 123936 : int *dims2 = AARR_DIMS(array2);
3824 [ + + ]: 123936 : int *lbs1 = AARR_LBOUND(array1);
3825 [ + + ]: 123936 : int *lbs2 = AARR_LBOUND(array2);
3826 [ + + ]: 123936 : Oid element_type = AARR_ELEMTYPE(array1);
9177 3827 : 123936 : bool result = true;
3828 : : int nitems;
3829 : : TypeCacheEntry *typentry;
3830 : : int typlen;
3831 : : bool typbyval;
3832 : : char typalign;
3833 : : array_iter it1;
3834 : : array_iter it2;
3835 : : int i;
3836 : :
3768 3837 [ + + - + ]: 123936 : if (element_type != AARR_ELEMTYPE(array2))
8077 tgl@sss.pgh.pa.us 3838 [ # # ]:UBC 0 : ereport(ERROR,
3839 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
3840 : : errmsg("cannot compare arrays of different element types")));
3841 : :
3842 : : /* fast path if the arrays do not have the same dimensionality */
7231 tgl@sss.pgh.pa.us 3843 [ + + ]:CBC 123936 : if (ndims1 != ndims2 ||
3768 3844 [ + + ]: 116546 : memcmp(dims1, dims2, ndims1 * sizeof(int)) != 0 ||
3845 [ - + ]: 86447 : memcmp(lbs1, lbs2, ndims1 * sizeof(int)) != 0)
9177 3846 : 37489 : result = false;
3847 : : else
3848 : : {
3849 : : /*
3850 : : * We arrange to look up the equality function only once per series of
3851 : : * calls, assuming the element type doesn't change underneath us. The
3852 : : * typcache is used so that we have no memory leakage when being used
3853 : : * as an index support function.
3854 : : */
8056 3855 : 86447 : typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3856 [ + + ]: 86447 : if (typentry == NULL ||
3857 [ - + ]: 85053 : typentry->type_id != element_type)
3858 : : {
3859 : 1394 : typentry = lookup_type_cache(element_type,
3860 : : TYPECACHE_EQ_OPR_FINFO);
3861 [ - + ]: 1394 : if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
8056 tgl@sss.pgh.pa.us 3862 [ # # ]:UBC 0 : ereport(ERROR,
3863 : : (errcode(ERRCODE_UNDEFINED_FUNCTION),
3864 : : errmsg("could not identify an equality operator for type %s",
3865 : : format_type_be(element_type))));
282 peter@eisentraut.org 3866 :CBC 1394 : fcinfo->flinfo->fn_extra = typentry;
3867 : : }
8056 tgl@sss.pgh.pa.us 3868 : 86447 : typlen = typentry->typlen;
3869 : 86447 : typbyval = typentry->typbyval;
3870 : 86447 : typalign = typentry->typalign;
3871 : :
3872 : : /*
3873 : : * apply the operator to each pair of array elements.
3874 : : */
2415 andres@anarazel.de 3875 : 86447 : InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
3876 : : collation, NULL, NULL);
3877 : :
3878 : : /* Loop over source data */
7231 tgl@sss.pgh.pa.us 3879 : 86447 : nitems = ArrayGetNItems(ndims1, dims1);
3768 3880 : 86447 : array_iter_setup(&it1, array1);
3881 : 86447 : array_iter_setup(&it2, array2);
3882 : :
7231 3883 [ + + ]: 202557 : for (i = 0; i < nitems; i++)
3884 : : {
3885 : : Datum elt1;
3886 : : Datum elt2;
3887 : : bool isnull1;
3888 : : bool isnull2;
3889 : : bool oprresult;
3890 : :
3891 : : /* Get elements, checking for NULL */
3768 3892 : 132464 : elt1 = array_iter_next(&it1, &isnull1, i,
3893 : : typlen, typbyval, typalign);
3894 : 132464 : elt2 = array_iter_next(&it2, &isnull2, i,
3895 : : typlen, typbyval, typalign);
3896 : :
3897 : : /*
3898 : : * We consider two NULLs equal; NULL and not-NULL are unequal.
3899 : : */
7233 3900 [ + + + - ]: 132464 : if (isnull1 && isnull2)
3901 : 11 : continue;
3902 [ + - + + ]: 132453 : if (isnull1 || isnull2)
3903 : : {
3904 : 54 : result = false;
3905 : 16354 : break;
3906 : : }
3907 : :
3908 : : /*
3909 : : * Apply the operator to the element pair; treat NULL as false
3910 : : */
2415 andres@anarazel.de 3911 : 132399 : locfcinfo->args[0].value = elt1;
3912 : 132399 : locfcinfo->args[0].isnull = false;
3913 : 132399 : locfcinfo->args[1].value = elt2;
3914 : 132399 : locfcinfo->args[1].isnull = false;
3915 : 132399 : locfcinfo->isnull = false;
3916 : 132399 : oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
1964 tgl@sss.pgh.pa.us 3917 [ + - + + ]: 132399 : if (locfcinfo->isnull || !oprresult)
3918 : : {
8107 3919 : 16300 : result = false;
3920 : 16300 : break;
3921 : : }
3922 : : }
3923 : : }
3924 : :
3925 : : /* Avoid leaking memory when handed toasted input. */
3768 3926 [ + + + + ]: 123936 : AARR_FREE_IF_COPY(array1, 0);
3927 [ + + + + ]: 123936 : AARR_FREE_IF_COPY(array2, 1);
3928 : :
9177 3929 : 123936 : PG_RETURN_BOOL(result);
3930 : : }
3931 : :
3932 : :
3933 : : /*-----------------------------------------------------------------------------
3934 : : * array-array bool operators:
3935 : : * Given two arrays, iterate comparison operators
3936 : : * over the array. Uses logic similar to text comparison
3937 : : * functions, except element-by-element instead of
3938 : : * character-by-character.
3939 : : *----------------------------------------------------------------------------
3940 : : */
3941 : :
3942 : : Datum
8107 3943 : 489 : array_ne(PG_FUNCTION_ARGS)
3944 : : {
3945 : 489 : PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo)));
3946 : : }
3947 : :
3948 : : Datum
3949 : 3773 : array_lt(PG_FUNCTION_ARGS)
3950 : : {
3951 : 3773 : PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
3952 : : }
3953 : :
3954 : : Datum
3955 : 9 : array_gt(PG_FUNCTION_ARGS)
3956 : : {
3957 : 9 : PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
3958 : : }
3959 : :
3960 : : Datum
3961 : 9 : array_le(PG_FUNCTION_ARGS)
3962 : : {
3963 : 9 : PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
3964 : : }
3965 : :
3966 : : Datum
3967 : 9 : array_ge(PG_FUNCTION_ARGS)
3968 : : {
3969 : 9 : PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
3970 : : }
3971 : :
3972 : : Datum
3973 : 4301384 : btarraycmp(PG_FUNCTION_ARGS)
3974 : : {
3975 : 4301384 : PG_RETURN_INT32(array_cmp(fcinfo));
3976 : : }
3977 : :
3978 : : /*
3979 : : * array_cmp()
3980 : : * Internal comparison function for arrays.
3981 : : *
3982 : : * Returns -1, 0 or 1
3983 : : */
3984 : : static int
3985 : 4305457 : array_cmp(FunctionCallInfo fcinfo)
3986 : : {
2415 andres@anarazel.de 3987 : 4305457 : LOCAL_FCINFO(locfcinfo, 2);
2910 tgl@sss.pgh.pa.us 3988 : 4305457 : AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
3989 : 4305457 : AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
5324 peter_e@gmx.net 3990 : 4305457 : Oid collation = PG_GET_COLLATION();
3768 tgl@sss.pgh.pa.us 3991 [ - + ]: 4305457 : int ndims1 = AARR_NDIM(array1);
3992 [ - + ]: 4305457 : int ndims2 = AARR_NDIM(array2);
3993 [ - + ]: 4305457 : int *dims1 = AARR_DIMS(array1);
3994 [ - + ]: 4305457 : int *dims2 = AARR_DIMS(array2);
8058 3995 : 4305457 : int nitems1 = ArrayGetNItems(ndims1, dims1);
3996 : 4305457 : int nitems2 = ArrayGetNItems(ndims2, dims2);
3768 3997 [ - + ]: 4305457 : Oid element_type = AARR_ELEMTYPE(array1);
8107 3998 : 4305457 : int result = 0;
3999 : : TypeCacheEntry *typentry;
4000 : : int typlen;
4001 : : bool typbyval;
4002 : : char typalign;
4003 : : int min_nitems;
4004 : : array_iter it1;
4005 : : array_iter it2;
4006 : : int i;
4007 : :
3768 4008 [ - + + + ]: 4305457 : if (element_type != AARR_ELEMTYPE(array2))
8077 4009 [ + - ]: 3 : ereport(ERROR,
4010 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
4011 : : errmsg("cannot compare arrays of different element types")));
4012 : :
4013 : : /*
4014 : : * We arrange to look up the comparison function only once per series of
4015 : : * calls, assuming the element type doesn't change underneath us. The
4016 : : * typcache is used so that we have no memory leakage when being used as
4017 : : * an index support function.
4018 : : */
8056 4019 : 4305454 : typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
4020 [ + + ]: 4305454 : if (typentry == NULL ||
5261 4021 [ - + ]: 4304150 : typentry->type_id != element_type)
4022 : : {
8056 4023 : 1304 : typentry = lookup_type_cache(element_type,
4024 : : TYPECACHE_CMP_PROC_FINFO);
4025 [ + + ]: 1304 : if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
4026 [ + - ]: 3 : ereport(ERROR,
4027 : : (errcode(ERRCODE_UNDEFINED_FUNCTION),
4028 : : errmsg("could not identify a comparison function for type %s",
4029 : : format_type_be(element_type))));
282 peter@eisentraut.org 4030 : 1301 : fcinfo->flinfo->fn_extra = typentry;
4031 : : }
8056 tgl@sss.pgh.pa.us 4032 : 4305451 : typlen = typentry->typlen;
4033 : 4305451 : typbyval = typentry->typbyval;
4034 : 4305451 : typalign = typentry->typalign;
4035 : :
4036 : : /*
4037 : : * apply the operator to each pair of array elements.
4038 : : */
2415 andres@anarazel.de 4039 : 4305451 : InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
4040 : : collation, NULL, NULL);
4041 : :
4042 : : /* Loop over source data */
7231 tgl@sss.pgh.pa.us 4043 : 4305451 : min_nitems = Min(nitems1, nitems2);
3768 4044 : 4305451 : array_iter_setup(&it1, array1);
4045 : 4305451 : array_iter_setup(&it2, array2);
4046 : :
8058 4047 [ + + ]: 9080114 : for (i = 0; i < min_nitems; i++)
4048 : : {
4049 : : Datum elt1;
4050 : : Datum elt2;
4051 : : bool isnull1;
4052 : : bool isnull2;
4053 : : int32 cmpresult;
4054 : :
4055 : : /* Get elements, checking for NULL */
3768 4056 : 7735652 : elt1 = array_iter_next(&it1, &isnull1, i, typlen, typbyval, typalign);
4057 : 7735652 : elt2 = array_iter_next(&it2, &isnull2, i, typlen, typbyval, typalign);
4058 : :
4059 : : /*
4060 : : * We consider two NULLs equal; NULL > not-NULL.
4061 : : */
7233 4062 [ + + + + ]: 7735652 : if (isnull1 && isnull2)
4063 : 4774663 : continue;
4064 [ + + ]: 7735647 : if (isnull1)
4065 : : {
4066 : : /* arg1 is greater than arg2 */
4067 : 96 : result = 1;
4068 : 2960989 : break;
4069 : : }
4070 [ + + ]: 7735551 : if (isnull2)
4071 : : {
4072 : : /* arg1 is less than arg2 */
4073 : 180 : result = -1;
4074 : 180 : break;
4075 : : }
4076 : :
4077 : : /* Compare the pair of elements */
2415 andres@anarazel.de 4078 : 7735371 : locfcinfo->args[0].value = elt1;
4079 : 7735371 : locfcinfo->args[0].isnull = false;
4080 : 7735371 : locfcinfo->args[1].value = elt2;
4081 : 7735371 : locfcinfo->args[1].isnull = false;
4082 : 7735371 : cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
4083 : :
4084 : : /* We don't expect comparison support functions to return null */
1964 tgl@sss.pgh.pa.us 4085 [ - + ]: 7735371 : Assert(!locfcinfo->isnull);
4086 : :
8056 4087 [ + + ]: 7735371 : if (cmpresult == 0)
4088 : 4774658 : continue; /* equal */
4089 : :
4090 [ + + ]: 2960713 : if (cmpresult < 0)
4091 : : {
4092 : : /* arg1 is less than arg2 */
8058 4093 : 1709010 : result = -1;
4094 : 1709010 : break;
4095 : : }
4096 : : else
4097 : : {
4098 : : /* arg1 is greater than arg2 */
4099 : 1251703 : result = 1;
4100 : 1251703 : break;
4101 : : }
4102 : : }
4103 : :
4104 : : /*
4105 : : * If arrays contain same data (up to end of shorter one), apply
4106 : : * additional rules to sort by dimensionality. The relative significance
4107 : : * of the different bits of information is historical; mainly we just care
4108 : : * that we don't say "equal" for arrays of different dimensionality.
4109 : : */
7231 4110 [ + + ]: 4305451 : if (result == 0)
4111 : : {
4112 [ + + ]: 1344462 : if (nitems1 != nitems2)
4113 [ + + ]: 100760 : result = (nitems1 < nitems2) ? -1 : 1;
4114 [ - + ]: 1243702 : else if (ndims1 != ndims2)
7231 tgl@sss.pgh.pa.us 4115 [ # # ]:UBC 0 : result = (ndims1 < ndims2) ? -1 : 1;
4116 : : else
4117 : : {
3768 tgl@sss.pgh.pa.us 4118 [ + + ]:CBC 2487378 : for (i = 0; i < ndims1; i++)
4119 : : {
7231 4120 [ - + ]: 1243676 : if (dims1[i] != dims2[i])
4121 : : {
7231 tgl@sss.pgh.pa.us 4122 [ # # ]:UBC 0 : result = (dims1[i] < dims2[i]) ? -1 : 1;
4123 : 0 : break;
4124 : : }
4125 : : }
3768 tgl@sss.pgh.pa.us 4126 [ + - ]:CBC 1243702 : if (result == 0)
4127 : : {
4128 [ - + ]: 1243702 : int *lbound1 = AARR_LBOUND(array1);
4129 [ - + ]: 1243702 : int *lbound2 = AARR_LBOUND(array2);
4130 : :
4131 [ + + ]: 2487378 : for (i = 0; i < ndims1; i++)
4132 : : {
4133 [ - + ]: 1243676 : if (lbound1[i] != lbound2[i])
4134 : : {
3768 tgl@sss.pgh.pa.us 4135 [ # # ]:UBC 0 : result = (lbound1[i] < lbound2[i]) ? -1 : 1;
4136 : 0 : break;
4137 : : }
4138 : : }
4139 : : }
4140 : : }
4141 : : }
4142 : :
4143 : : /* Avoid leaking memory when handed toasted input. */
3768 tgl@sss.pgh.pa.us 4144 [ + - + + ]:CBC 4305451 : AARR_FREE_IF_COPY(array1, 0);
4145 [ + - + + ]: 4305451 : AARR_FREE_IF_COPY(array2, 1);
4146 : :
8107 4147 : 4305451 : return result;
4148 : : }
4149 : :
4150 : :
4151 : : /*-----------------------------------------------------------------------------
4152 : : * array hashing
4153 : : * Hash the elements and combine the results.
4154 : : *----------------------------------------------------------------------------
4155 : : */
4156 : :
4157 : : Datum
5425 4158 : 29838 : hash_array(PG_FUNCTION_ARGS)
4159 : : {
2415 andres@anarazel.de 4160 : 29838 : LOCAL_FCINFO(locfcinfo, 1);
2910 tgl@sss.pgh.pa.us 4161 : 29838 : AnyArrayType *array = PG_GETARG_ANY_ARRAY_P(0);
3768 4162 [ + + ]: 29838 : int ndims = AARR_NDIM(array);
4163 [ + + ]: 29838 : int *dims = AARR_DIMS(array);
4164 [ + + ]: 29838 : Oid element_type = AARR_ELEMTYPE(array);
5220 rhaas@postgresql.org 4165 : 29838 : uint32 result = 1;
4166 : : int nitems;
4167 : : TypeCacheEntry *typentry;
4168 : : int typlen;
4169 : : bool typbyval;
4170 : : char typalign;
4171 : : int i;
4172 : : array_iter iter;
4173 : :
4174 : : /*
4175 : : * We arrange to look up the hash function only once per series of calls,
4176 : : * assuming the element type doesn't change underneath us. The typcache
4177 : : * is used so that we have no memory leakage when being used as an index
4178 : : * support function.
4179 : : */
5425 tgl@sss.pgh.pa.us 4180 : 29838 : typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
4181 [ + + ]: 29838 : if (typentry == NULL ||
4182 [ - + ]: 29543 : typentry->type_id != element_type)
4183 : : {
4184 : 295 : typentry = lookup_type_cache(element_type,
4185 : : TYPECACHE_HASH_PROC_FINFO);
1459 peter@eisentraut.org 4186 [ + + + + ]: 295 : if (!OidIsValid(typentry->hash_proc_finfo.fn_oid) && element_type != RECORDOID)
5425 tgl@sss.pgh.pa.us 4187 [ + - ]: 3 : ereport(ERROR,
4188 : : (errcode(ERRCODE_UNDEFINED_FUNCTION),
4189 : : errmsg("could not identify a hash function for type %s",
4190 : : format_type_be(element_type))));
4191 : :
4192 : : /*
4193 : : * The type cache doesn't believe that record is hashable (see
4194 : : * cache_record_field_properties()), but since we're here, we're
4195 : : * committed to hashing, so we can assume it does. Worst case, if any
4196 : : * components of the record don't support hashing, we will fail at
4197 : : * execution.
4198 : : */
1459 peter@eisentraut.org 4199 [ + + ]: 292 : if (element_type == RECORDOID)
4200 : : {
4201 : : MemoryContext oldcontext;
4202 : : TypeCacheEntry *record_typentry;
4203 : :
1452 4204 : 9 : oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
4205 : :
4206 : : /*
4207 : : * Make fake type cache entry structure. Note that we can't just
4208 : : * modify typentry, since that points directly into the type
4209 : : * cache.
4210 : : */
4211 : 9 : record_typentry = palloc0(sizeof(*record_typentry));
4212 : 9 : record_typentry->type_id = element_type;
4213 : :
4214 : : /* fill in what we need below */
1459 4215 : 9 : record_typentry->typlen = typentry->typlen;
4216 : 9 : record_typentry->typbyval = typentry->typbyval;
4217 : 9 : record_typentry->typalign = typentry->typalign;
4218 : 9 : fmgr_info(F_HASH_RECORD, &record_typentry->hash_proc_finfo);
4219 : :
4220 : 9 : MemoryContextSwitchTo(oldcontext);
4221 : :
4222 : 9 : typentry = record_typentry;
4223 : : }
4224 : :
282 4225 : 292 : fcinfo->flinfo->fn_extra = typentry;
4226 : : }
4227 : :
5425 tgl@sss.pgh.pa.us 4228 : 29835 : typlen = typentry->typlen;
4229 : 29835 : typbyval = typentry->typbyval;
4230 : 29835 : typalign = typentry->typalign;
4231 : :
4232 : : /*
4233 : : * apply the hash function to each array element.
4234 : : */
2415 andres@anarazel.de 4235 : 29835 : InitFunctionCallInfoData(*locfcinfo, &typentry->hash_proc_finfo, 1,
4236 : : PG_GET_COLLATION(), NULL, NULL);
4237 : :
4238 : : /* Loop over source data */
5425 tgl@sss.pgh.pa.us 4239 : 29835 : nitems = ArrayGetNItems(ndims, dims);
3768 4240 : 29835 : array_iter_setup(&iter, array);
4241 : :
5425 4242 [ + + ]: 77632 : for (i = 0; i < nitems; i++)
4243 : : {
4244 : : Datum elt;
4245 : : bool isnull;
4246 : : uint32 elthash;
4247 : :
4248 : : /* Get element, checking for NULL */
3768 4249 : 47797 : elt = array_iter_next(&iter, &isnull, i, typlen, typbyval, typalign);
4250 : :
4251 [ - + ]: 47797 : if (isnull)
4252 : : {
4253 : : /* Treat nulls as having hashvalue 0 */
5425 tgl@sss.pgh.pa.us 4254 :UBC 0 : elthash = 0;
4255 : : }
4256 : : else
4257 : : {
4258 : : /* Apply the hash function */
2415 andres@anarazel.de 4259 :CBC 47797 : locfcinfo->args[0].value = elt;
4260 : 47797 : locfcinfo->args[0].isnull = false;
4261 : 47797 : elthash = DatumGetUInt32(FunctionCallInvoke(locfcinfo));
4262 : : /* We don't expect hash functions to return null */
1964 tgl@sss.pgh.pa.us 4263 [ - + ]: 47797 : Assert(!locfcinfo->isnull);
4264 : : }
4265 : :
4266 : : /*
4267 : : * Combine hash values of successive elements by multiplying the
4268 : : * current value by 31 and adding on the new element's hash value.
4269 : : *
4270 : : * The result is a sum in which each element's hash value is
4271 : : * multiplied by a different power of 31. This is modulo 2^32
4272 : : * arithmetic, and the powers of 31 modulo 2^32 form a cyclic group of
4273 : : * order 2^27. So for arrays of up to 2^27 elements, each element's
4274 : : * hash value is multiplied by a different (odd) number, resulting in
4275 : : * a good mixing of all the elements' hash values.
4276 : : */
5220 rhaas@postgresql.org 4277 : 47797 : result = (result << 5) - result + elthash;
4278 : : }
4279 : :
4280 : : /* Avoid leaking memory when handed toasted input. */
3768 tgl@sss.pgh.pa.us 4281 [ + + + + ]: 29835 : AARR_FREE_IF_COPY(array, 0);
4282 : :
5425 4283 : 29835 : PG_RETURN_UINT32(result);
4284 : : }
4285 : :
4286 : : /*
4287 : : * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
4288 : : * Otherwise, similar to hash_array.
4289 : : */
4290 : : Datum
2928 rhaas@postgresql.org 4291 : 72 : hash_array_extended(PG_FUNCTION_ARGS)
4292 : : {
2415 andres@anarazel.de 4293 : 72 : LOCAL_FCINFO(locfcinfo, 2);
2910 tgl@sss.pgh.pa.us 4294 : 72 : AnyArrayType *array = PG_GETARG_ANY_ARRAY_P(0);
2928 rhaas@postgresql.org 4295 : 72 : uint64 seed = PG_GETARG_INT64(1);
4296 [ - + ]: 72 : int ndims = AARR_NDIM(array);
4297 [ - + ]: 72 : int *dims = AARR_DIMS(array);
4298 [ - + ]: 72 : Oid element_type = AARR_ELEMTYPE(array);
4299 : 72 : uint64 result = 1;
4300 : : int nitems;
4301 : : TypeCacheEntry *typentry;
4302 : : int typlen;
4303 : : bool typbyval;
4304 : : char typalign;
4305 : : int i;
4306 : : array_iter iter;
4307 : :
4308 : 72 : typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
4309 [ + + ]: 72 : if (typentry == NULL ||
4310 [ - + ]: 42 : typentry->type_id != element_type)
4311 : : {
4312 : 30 : typentry = lookup_type_cache(element_type,
4313 : : TYPECACHE_HASH_EXTENDED_PROC_FINFO);
4314 [ + + ]: 30 : if (!OidIsValid(typentry->hash_extended_proc_finfo.fn_oid))
4315 [ + - ]: 3 : ereport(ERROR,
4316 : : (errcode(ERRCODE_UNDEFINED_FUNCTION),
4317 : : errmsg("could not identify an extended hash function for type %s",
4318 : : format_type_be(element_type))));
282 peter@eisentraut.org 4319 : 27 : fcinfo->flinfo->fn_extra = typentry;
4320 : : }
2928 rhaas@postgresql.org 4321 : 69 : typlen = typentry->typlen;
4322 : 69 : typbyval = typentry->typbyval;
4323 : 69 : typalign = typentry->typalign;
4324 : :
2415 andres@anarazel.de 4325 : 69 : InitFunctionCallInfoData(*locfcinfo, &typentry->hash_extended_proc_finfo, 2,
4326 : : PG_GET_COLLATION(), NULL, NULL);
4327 : :
4328 : : /* Loop over source data */
2928 rhaas@postgresql.org 4329 : 69 : nitems = ArrayGetNItems(ndims, dims);
4330 : 69 : array_iter_setup(&iter, array);
4331 : :
4332 [ + + ]: 228 : for (i = 0; i < nitems; i++)
4333 : : {
4334 : : Datum elt;
4335 : : bool isnull;
4336 : : uint64 elthash;
4337 : :
4338 : : /* Get element, checking for NULL */
4339 : 159 : elt = array_iter_next(&iter, &isnull, i, typlen, typbyval, typalign);
4340 : :
4341 [ - + ]: 159 : if (isnull)
4342 : : {
2928 rhaas@postgresql.org 4343 :UBC 0 : elthash = 0;
4344 : : }
4345 : : else
4346 : : {
4347 : : /* Apply the hash function */
2415 andres@anarazel.de 4348 :CBC 159 : locfcinfo->args[0].value = elt;
4349 : 159 : locfcinfo->args[0].isnull = false;
4350 : 159 : locfcinfo->args[1].value = Int64GetDatum(seed);
4351 : 159 : locfcinfo->args[1].isnull = false;
4352 : 159 : elthash = DatumGetUInt64(FunctionCallInvoke(locfcinfo));
4353 : : /* We don't expect hash functions to return null */
1964 tgl@sss.pgh.pa.us 4354 [ - + ]: 159 : Assert(!locfcinfo->isnull);
4355 : : }
4356 : :
2928 rhaas@postgresql.org 4357 : 159 : result = (result << 5) - result + elthash;
4358 : : }
4359 : :
4360 [ + - - + ]: 69 : AARR_FREE_IF_COPY(array, 0);
4361 : :
4362 : 69 : PG_RETURN_UINT64(result);
4363 : : }
4364 : :
4365 : :
4366 : : /*-----------------------------------------------------------------------------
4367 : : * array overlap/containment comparisons
4368 : : * These use the same methods of comparing array elements as array_eq.
4369 : : * We consider only the elements of the arrays, ignoring dimensionality.
4370 : : *----------------------------------------------------------------------------
4371 : : */
4372 : :
4373 : : /*
4374 : : * array_contain_compare :
4375 : : * compares two arrays for overlap/containment
4376 : : *
4377 : : * When matchall is true, return true if all members of array1 are in array2.
4378 : : * When matchall is false, return true if any members of array1 are in array2.
4379 : : */
4380 : : static bool
3768 tgl@sss.pgh.pa.us 4381 : 11229 : array_contain_compare(AnyArrayType *array1, AnyArrayType *array2, Oid collation,
4382 : : bool matchall, void **fn_extra)
4383 : : {
2415 andres@anarazel.de 4384 : 11229 : LOCAL_FCINFO(locfcinfo, 2);
6936 tgl@sss.pgh.pa.us 4385 : 11229 : bool result = matchall;
3768 4386 [ + + ]: 11229 : Oid element_type = AARR_ELEMTYPE(array1);
4387 : : TypeCacheEntry *typentry;
4388 : : int nelems1;
4389 : : Datum *values2;
4390 : : bool *nulls2;
4391 : : int nelems2;
4392 : : int typlen;
4393 : : bool typbyval;
4394 : : char typalign;
4395 : : int i;
4396 : : int j;
4397 : : array_iter it1;
4398 : :
4399 [ + + - + ]: 11229 : if (element_type != AARR_ELEMTYPE(array2))
6936 tgl@sss.pgh.pa.us 4400 [ # # ]:UBC 0 : ereport(ERROR,
4401 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
4402 : : errmsg("cannot compare arrays of different element types")));
4403 : :
4404 : : /*
4405 : : * We arrange to look up the equality function only once per series of
4406 : : * calls, assuming the element type doesn't change underneath us. The
4407 : : * typcache is used so that we have no memory leakage when being used as
4408 : : * an index support function.
4409 : : */
6936 tgl@sss.pgh.pa.us 4410 :CBC 11229 : typentry = (TypeCacheEntry *) *fn_extra;
4411 [ + + ]: 11229 : if (typentry == NULL ||
4412 [ - + ]: 10953 : typentry->type_id != element_type)
4413 : : {
4414 : 276 : typentry = lookup_type_cache(element_type,
4415 : : TYPECACHE_EQ_OPR_FINFO);
4416 [ - + ]: 276 : if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
6936 tgl@sss.pgh.pa.us 4417 [ # # ]:UBC 0 : ereport(ERROR,
4418 : : (errcode(ERRCODE_UNDEFINED_FUNCTION),
4419 : : errmsg("could not identify an equality operator for type %s",
4420 : : format_type_be(element_type))));
282 peter@eisentraut.org 4421 :CBC 276 : *fn_extra = typentry;
4422 : : }
6936 tgl@sss.pgh.pa.us 4423 : 11229 : typlen = typentry->typlen;
4424 : 11229 : typbyval = typentry->typbyval;
4425 : 11229 : typalign = typentry->typalign;
4426 : :
4427 : : /*
4428 : : * Since we probably will need to scan array2 multiple times, it's
4429 : : * worthwhile to use deconstruct_array on it. We scan array1 the hard way
4430 : : * however, since we very likely won't need to look at all of it.
4431 : : */
3768 4432 [ + + ]: 11229 : if (VARATT_IS_EXPANDED_HEADER(array2))
4433 : : {
4434 : : /* This should be safe even if input is read-only */
4435 : 2556 : deconstruct_expanded_array(&(array2->xpn));
4436 : 2556 : values2 = array2->xpn.dvalues;
4437 : 2556 : nulls2 = array2->xpn.dnulls;
4438 : 2556 : nelems2 = array2->xpn.nelems;
4439 : : }
4440 : : else
2260 noah@leadboat.com 4441 : 8673 : deconstruct_array((ArrayType *) array2,
4442 : : element_type, typlen, typbyval, typalign,
4443 : : &values2, &nulls2, &nelems2);
4444 : :
4445 : : /*
4446 : : * Apply the comparison operator to each pair of array elements.
4447 : : */
2415 andres@anarazel.de 4448 : 11229 : InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
4449 : : collation, NULL, NULL);
4450 : :
4451 : : /* Loop over source data */
3768 tgl@sss.pgh.pa.us 4452 [ + + + + ]: 11229 : nelems1 = ArrayGetNItems(AARR_NDIM(array1), AARR_DIMS(array1));
4453 : 11229 : array_iter_setup(&it1, array1);
4454 : :
6936 4455 [ + + ]: 210167 : for (i = 0; i < nelems1; i++)
4456 : : {
4457 : : Datum elt1;
4458 : : bool isnull1;
4459 : :
4460 : : /* Get element, checking for NULL */
3768 4461 : 203661 : elt1 = array_iter_next(&it1, &isnull1, i, typlen, typbyval, typalign);
4462 : :
4463 : : /*
4464 : : * We assume that the comparison operator is strict, so a NULL can't
4465 : : * match anything. XXX this diverges from the "NULL=NULL" behavior of
4466 : : * array_eq, should we act like that?
4467 : : */
6936 4468 [ + + ]: 203661 : if (isnull1)
4469 : : {
4470 [ + + ]: 660 : if (matchall)
4471 : : {
4472 : 630 : result = false;
4473 : 4723 : break;
4474 : : }
4475 : 30 : continue;
4476 : : }
4477 : :
4478 [ + + ]: 9208177 : for (j = 0; j < nelems2; j++)
4479 : : {
4480 : 9188475 : Datum elt2 = values2[j];
3760 4481 [ + + + + ]: 9188475 : bool isnull2 = nulls2 ? nulls2[j] : false;
4482 : : bool oprresult;
4483 : :
6936 4484 [ + + ]: 9188475 : if (isnull2)
4485 : 3606 : continue; /* can't match */
4486 : :
4487 : : /*
4488 : : * Apply the operator to the element pair; treat NULL as false
4489 : : */
2415 andres@anarazel.de 4490 : 9184869 : locfcinfo->args[0].value = elt1;
4491 : 9184869 : locfcinfo->args[0].isnull = false;
4492 : 9184869 : locfcinfo->args[1].value = elt2;
4493 : 9184869 : locfcinfo->args[1].isnull = false;
4494 : 9184869 : locfcinfo->isnull = false;
4495 : 9184869 : oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
1964 tgl@sss.pgh.pa.us 4496 [ + - + + ]: 9184869 : if (!locfcinfo->isnull && oprresult)
6936 4497 : 183299 : break;
4498 : : }
4499 : :
4500 [ + + ]: 203001 : if (j < nelems2)
4501 : : {
4502 : : /* found a match for elt1 */
4503 [ + + ]: 183299 : if (!matchall)
4504 : : {
4505 : 114 : result = true;
4506 : 114 : break;
4507 : : }
4508 : : }
4509 : : else
4510 : : {
4511 : : /* no match for elt1 */
4512 [ + + ]: 19702 : if (matchall)
4513 : : {
4514 : 3979 : result = false;
4515 : 3979 : break;
4516 : : }
4517 : : }
4518 : : }
4519 : :
4520 : 11229 : return result;
4521 : : }
4522 : :
4523 : : Datum
4524 : 3060 : arrayoverlap(PG_FUNCTION_ARGS)
4525 : : {
2910 4526 : 3060 : AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
4527 : 3060 : AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
5261 4528 : 3060 : Oid collation = PG_GET_COLLATION();
4529 : : bool result;
4530 : :
4531 : 3060 : result = array_contain_compare(array1, array2, collation, false,
6936 4532 : 3060 : &fcinfo->flinfo->fn_extra);
4533 : :
4534 : : /* Avoid leaking memory when handed toasted input. */
3768 4535 [ + - + + ]: 3060 : AARR_FREE_IF_COPY(array1, 0);
4536 [ + - - + ]: 3060 : AARR_FREE_IF_COPY(array2, 1);
4537 : :
6936 4538 : 3060 : PG_RETURN_BOOL(result);
4539 : : }
4540 : :
4541 : : Datum
4542 : 4911 : arraycontains(PG_FUNCTION_ARGS)
4543 : : {
2910 4544 : 4911 : AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
4545 : 4911 : AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
5261 4546 : 4911 : Oid collation = PG_GET_COLLATION();
4547 : : bool result;
4548 : :
4549 : 4911 : result = array_contain_compare(array2, array1, collation, true,
6936 4550 : 4911 : &fcinfo->flinfo->fn_extra);
4551 : :
4552 : : /* Avoid leaking memory when handed toasted input. */
3768 4553 [ + + + + ]: 4911 : AARR_FREE_IF_COPY(array1, 0);
4554 [ + + - + ]: 4911 : AARR_FREE_IF_COPY(array2, 1);
4555 : :
6936 4556 : 4911 : PG_RETURN_BOOL(result);
4557 : : }
4558 : :
4559 : : Datum
4560 : 3258 : arraycontained(PG_FUNCTION_ARGS)
4561 : : {
2910 4562 : 3258 : AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
4563 : 3258 : AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
5261 4564 : 3258 : Oid collation = PG_GET_COLLATION();
4565 : : bool result;
4566 : :
4567 : 3258 : result = array_contain_compare(array1, array2, collation, true,
6936 4568 : 3258 : &fcinfo->flinfo->fn_extra);
4569 : :
4570 : : /* Avoid leaking memory when handed toasted input. */
3768 4571 [ + + + + ]: 3258 : AARR_FREE_IF_COPY(array1, 0);
4572 [ + + - + ]: 3258 : AARR_FREE_IF_COPY(array2, 1);
4573 : :
6936 4574 : 3258 : PG_RETURN_BOOL(result);
4575 : : }
4576 : :
4577 : :
4578 : : /*-----------------------------------------------------------------------------
4579 : : * Array iteration functions
4580 : : * These functions are used to iterate efficiently through arrays
4581 : : *-----------------------------------------------------------------------------
4582 : : */
4583 : :
4584 : : /*
4585 : : * array_create_iterator --- set up to iterate through an array
4586 : : *
4587 : : * If slice_ndim is zero, we will iterate element-by-element; the returned
4588 : : * datums are of the array's element type.
4589 : : *
4590 : : * If slice_ndim is 1..ARR_NDIM(arr), we will iterate by slices: the
4591 : : * returned datums are of the same array type as 'arr', but of size
4592 : : * equal to the rightmost N dimensions of 'arr'.
4593 : : *
4594 : : * The passed-in array must remain valid for the lifetime of the iterator.
4595 : : */
4596 : : ArrayIterator
3825 alvherre@alvh.no-ip. 4597 : 228 : array_create_iterator(ArrayType *arr, int slice_ndim, ArrayMetaState *mstate)
4598 : : {
5316 tgl@sss.pgh.pa.us 4599 : 228 : ArrayIterator iterator = palloc0(sizeof(ArrayIteratorData));
4600 : :
4601 : : /*
4602 : : * Sanity-check inputs --- caller should have got this right already
4603 : : */
4604 [ - + ]: 228 : Assert(PointerIsValid(arr));
4605 [ + - - + ]: 228 : if (slice_ndim < 0 || slice_ndim > ARR_NDIM(arr))
5316 tgl@sss.pgh.pa.us 4606 [ # # ]:UBC 0 : elog(ERROR, "invalid arguments to array_create_iterator");
4607 : :
4608 : : /*
4609 : : * Remember basic info about the array and its element type
4610 : : */
5316 tgl@sss.pgh.pa.us 4611 :CBC 228 : iterator->arr = arr;
4612 [ + + ]: 228 : iterator->nullbitmap = ARR_NULLBITMAP(arr);
4613 : 228 : iterator->nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
4614 : :
3825 alvherre@alvh.no-ip. 4615 [ + + ]: 228 : if (mstate != NULL)
4616 : : {
4617 [ - + ]: 192 : Assert(mstate->element_type == ARR_ELEMTYPE(arr));
4618 : :
4619 : 192 : iterator->typlen = mstate->typlen;
4620 : 192 : iterator->typbyval = mstate->typbyval;
4621 : 192 : iterator->typalign = mstate->typalign;
4622 : : }
4623 : : else
4624 : 36 : get_typlenbyvalalign(ARR_ELEMTYPE(arr),
4625 : : &iterator->typlen,
4626 : : &iterator->typbyval,
4627 : : &iterator->typalign);
4628 : :
4629 : : /*
4630 : : * Remember the slicing parameters.
4631 : : */
5316 tgl@sss.pgh.pa.us 4632 : 228 : iterator->slice_ndim = slice_ndim;
4633 : :
4634 [ + + ]: 228 : if (slice_ndim > 0)
4635 : : {
4636 : : /*
4637 : : * Get pointers into the array's dims and lbound arrays to represent
4638 : : * the dims/lbound arrays of a slice. These are the same as the
4639 : : * rightmost N dimensions of the array.
4640 : : */
4641 : 39 : iterator->slice_dims = ARR_DIMS(arr) + ARR_NDIM(arr) - slice_ndim;
4642 : 39 : iterator->slice_lbound = ARR_LBOUND(arr) + ARR_NDIM(arr) - slice_ndim;
4643 : :
4644 : : /*
4645 : : * Compute number of elements in a slice.
4646 : : */
4647 : 78 : iterator->slice_len = ArrayGetNItems(slice_ndim,
4648 : 39 : iterator->slice_dims);
4649 : :
4650 : : /*
4651 : : * Create workspace for building sub-arrays.
4652 : : */
4653 : 39 : iterator->slice_values = (Datum *)
4654 : 39 : palloc(iterator->slice_len * sizeof(Datum));
4655 : 39 : iterator->slice_nulls = (bool *)
4656 : 39 : palloc(iterator->slice_len * sizeof(bool));
4657 : : }
4658 : :
4659 : : /*
4660 : : * Initialize our data pointer and linear element number. These will
4661 : : * advance through the array during array_iterate().
4662 : : */
4663 [ + + ]: 228 : iterator->data_ptr = ARR_DATA_PTR(arr);
4664 : 228 : iterator->current_item = 0;
4665 : :
4666 : 228 : return iterator;
4667 : : }
4668 : :
4669 : : /*
4670 : : * Iterate through the array referenced by 'iterator'.
4671 : : *
4672 : : * As long as there is another element (or slice), return it into
4673 : : * *value / *isnull, and return true. Return false when no more data.
4674 : : */
4675 : : bool
4676 : 1179 : array_iterate(ArrayIterator iterator, Datum *value, bool *isnull)
4677 : : {
4678 : : /* Done if we have reached the end of the array */
4679 [ + + ]: 1179 : if (iterator->current_item >= iterator->nitems)
4680 : 138 : return false;
4681 : :
4682 [ + + ]: 1041 : if (iterator->slice_ndim == 0)
4683 : : {
4684 : : /*
4685 : : * Scalar case: return one element.
4686 : : */
4687 [ + + ]: 963 : if (array_get_isnull(iterator->nullbitmap, iterator->current_item++))
4688 : : {
4689 : 33 : *isnull = true;
4690 : 33 : *value = (Datum) 0;
4691 : : }
4692 : : else
4693 : : {
4694 : : /* non-NULL, so fetch the individual Datum to return */
4695 : 930 : char *p = iterator->data_ptr;
4696 : :
4697 : 930 : *isnull = false;
4698 : 930 : *value = fetch_att(p, iterator->typbyval, iterator->typlen);
4699 : :
4700 : : /* Move our data pointer forward to the next element */
4701 [ + + + - : 930 : p = att_addlength_pointer(p, iterator->typlen, p);
- - - - -
- - - - +
- - ]
4702 [ + + + + : 930 : p = (char *) att_align_nominal(p, iterator->typalign);
+ - - - ]
4703 : 930 : iterator->data_ptr = p;
4704 : : }
4705 : : }
4706 : : else
4707 : : {
4708 : : /*
4709 : : * Slice case: build and return an array of the requested size.
4710 : : */
4711 : : ArrayType *result;
4712 : 78 : Datum *values = iterator->slice_values;
4713 : 78 : bool *nulls = iterator->slice_nulls;
4714 : 78 : char *p = iterator->data_ptr;
4715 : : int i;
4716 : :
4717 [ + + ]: 249 : for (i = 0; i < iterator->slice_len; i++)
4718 : : {
4719 [ - + ]: 171 : if (array_get_isnull(iterator->nullbitmap,
4720 : 171 : iterator->current_item++))
4721 : : {
5316 tgl@sss.pgh.pa.us 4722 :UBC 0 : nulls[i] = true;
4723 : 0 : values[i] = (Datum) 0;
4724 : : }
4725 : : else
4726 : : {
5316 tgl@sss.pgh.pa.us 4727 :CBC 171 : nulls[i] = false;
4728 : 171 : values[i] = fetch_att(p, iterator->typbyval, iterator->typlen);
4729 : :
4730 : : /* Move our data pointer forward to the next element */
4731 [ + + + - : 171 : p = att_addlength_pointer(p, iterator->typlen, p);
- - - - -
- - - - +
- - ]
4732 [ + + + - : 171 : p = (char *) att_align_nominal(p, iterator->typalign);
+ - - - ]
4733 : : }
4734 : : }
4735 : :
4736 : 78 : iterator->data_ptr = p;
4737 : :
4738 : 78 : result = construct_md_array(values,
4739 : : nulls,
4740 : : iterator->slice_ndim,
4741 : : iterator->slice_dims,
4742 : : iterator->slice_lbound,
4743 : 78 : ARR_ELEMTYPE(iterator->arr),
4744 : 78 : iterator->typlen,
4745 : 78 : iterator->typbyval,
4746 : 78 : iterator->typalign);
4747 : :
4748 : 78 : *isnull = false;
4749 : 78 : *value = PointerGetDatum(result);
4750 : : }
4751 : :
4752 : 1041 : return true;
4753 : : }
4754 : :
4755 : : /*
4756 : : * Release an ArrayIterator data structure
4757 : : */
4758 : : void
4759 : 192 : array_free_iterator(ArrayIterator iterator)
4760 : : {
4761 [ + + ]: 192 : if (iterator->slice_ndim > 0)
4762 : : {
4763 : 21 : pfree(iterator->slice_values);
4764 : 21 : pfree(iterator->slice_nulls);
4765 : : }
4766 : 192 : pfree(iterator);
4767 : 192 : }
4768 : :
4769 : :
4770 : : /***************************************************************************/
4771 : : /******************| Support Routines |*****************/
4772 : : /***************************************************************************/
4773 : :
4774 : : /*
4775 : : * Check whether a specific array element is NULL
4776 : : *
4777 : : * nullbitmap: pointer to array's null bitmap (NULL if none)
4778 : : * offset: 0-based linear element number of array element
4779 : : */
4780 : : static bool
7233 4781 : 398838 : array_get_isnull(const bits8 *nullbitmap, int offset)
4782 : : {
4783 [ + + ]: 398838 : if (nullbitmap == NULL)
4784 : 398422 : return false; /* assume not null */
4785 [ + + ]: 416 : if (nullbitmap[offset / 8] & (1 << (offset % 8)))
4786 : 329 : return false; /* not null */
4787 : 87 : return true;
4788 : : }
4789 : :
4790 : : /*
4791 : : * Set a specific array element's null-bitmap entry
4792 : : *
4793 : : * nullbitmap: pointer to array's null bitmap (mustn't be NULL)
4794 : : * offset: 0-based linear element number of array element
4795 : : * isNull: null status to set
4796 : : */
4797 : : static void
4798 : 67 : array_set_isnull(bits8 *nullbitmap, int offset, bool isNull)
4799 : : {
4800 : : int bitmask;
4801 : :
4802 : 67 : nullbitmap += offset / 8;
4803 : 67 : bitmask = 1 << (offset % 8);
4804 [ + + ]: 67 : if (isNull)
4805 : 10 : *nullbitmap &= ~bitmask;
4806 : : else
4807 : 57 : *nullbitmap |= bitmask;
4808 : 67 : }
4809 : :
4810 : : /*
4811 : : * Fetch array element at pointer, converted correctly to a Datum
4812 : : *
4813 : : * Caller must have handled case of NULL element
4814 : : */
4815 : : static Datum
9177 4816 : 397364 : ArrayCast(char *value, bool byval, int len)
4817 : : {
9019 4818 : 397364 : return fetch_att(value, byval, len);
4819 : : }
4820 : :
4821 : : /*
4822 : : * Copy datum to *dest and return total space used (including align padding)
4823 : : *
4824 : : * Caller must have handled case of NULL element
4825 : : */
4826 : : static int
9216 4827 : 7045074 : ArrayCastAndSet(Datum src,
4828 : : int typlen,
4829 : : bool typbyval,
4830 : : char typalign,
4831 : : char *dest)
4832 : : {
4833 : : int inc;
4834 : :
10226 bruce@momjian.us 4835 [ + + ]: 7045074 : if (typlen > 0)
4836 : : {
4837 [ + + ]: 5342966 : if (typbyval)
9019 tgl@sss.pgh.pa.us 4838 : 3378193 : store_att_byval(dest, src, typlen);
4839 : : else
9216 4840 : 1964773 : memmove(dest, DatumGetPointer(src), typlen);
6728 4841 [ + + + + : 5342966 : inc = att_align_nominal(typlen, typalign);
+ + - + ]
4842 : : }
4843 : : else
4844 : : {
8412 4845 [ - + ]: 1702108 : Assert(!typbyval);
6728 4846 [ + - + + : 1702108 : inc = att_addlength_datum(0, typlen, src);
- + - - -
- - - - +
- + ]
8412 4847 : 1702108 : memmove(dest, DatumGetPointer(src), inc);
6728 4848 [ + + + + : 1702108 : inc = att_align_nominal(inc, typalign);
+ - - - ]
4849 : : }
4850 : :
9177 4851 : 7045074 : return inc;
4852 : : }
4853 : :
4854 : : /*
4855 : : * Advance ptr over nitems array elements
4856 : : *
4857 : : * ptr: starting location in array
4858 : : * offset: 0-based linear element number of first element (the one at *ptr)
4859 : : * nullbitmap: start of array's null bitmap, or NULL if none
4860 : : * nitems: number of array elements to advance over (>= 0)
4861 : : * typlen, typbyval, typalign: storage parameters of array element datatype
4862 : : *
4863 : : * It is caller's responsibility to ensure that nitems is within range
4864 : : */
4865 : : static char *
7233 4866 : 398571 : array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
4867 : : int typlen, bool typbyval, char typalign)
4868 : : {
4869 : : int bitmask;
4870 : : int i;
4871 : :
4872 : : /* easy if fixed-size elements and no NULLs */
4873 [ + + + + ]: 398571 : if (typlen > 0 && !nullbitmap)
6728 4874 [ + + + + : 270294 : return ptr + nitems * ((Size) att_align_nominal(typlen, typalign));
+ + - + ]
4875 : :
4876 : : /* seems worth having separate loops for NULL and no-NULLs cases */
7233 4877 [ + + ]: 128277 : if (nullbitmap)
4878 : : {
4879 : 302 : nullbitmap += offset / 8;
4880 : 302 : bitmask = 1 << (offset % 8);
4881 : :
4882 [ + + ]: 926 : for (i = 0; i < nitems; i++)
4883 : : {
4884 [ + + ]: 624 : if (*nullbitmap & bitmask)
4885 : : {
6728 4886 [ + + + - : 444 : ptr = att_addlength_pointer(ptr, typlen, ptr);
- - - - -
- - - - +
- - ]
4887 [ + + - + : 444 : ptr = (char *) att_align_nominal(ptr, typalign);
- - - - ]
4888 : : }
7233 4889 : 624 : bitmask <<= 1;
4890 [ + + ]: 624 : if (bitmask == 0x100)
4891 : : {
4892 : 24 : nullbitmap++;
4893 : 24 : bitmask = 1;
4894 : : }
4895 : : }
4896 : : }
4897 : : else
4898 : : {
4899 [ + + ]: 581895 : for (i = 0; i < nitems; i++)
4900 : : {
6728 4901 [ - + + - : 453920 : ptr = att_addlength_pointer(ptr, typlen, ptr);
- - - - -
- - - - +
- - ]
4902 [ + + + - : 453920 : ptr = (char *) att_align_nominal(ptr, typalign);
+ - - - ]
4903 : : }
4904 : : }
7233 4905 : 128277 : return ptr;
4906 : : }
4907 : :
4908 : : /*
4909 : : * Compute total size of the nitems array elements starting at *ptr
4910 : : *
4911 : : * Parameters same as for array_seek
4912 : : */
4913 : : static int
4914 : 807 : array_nelems_size(char *ptr, int offset, bits8 *nullbitmap, int nitems,
4915 : : int typlen, bool typbyval, char typalign)
4916 : : {
4917 : 807 : return array_seek(ptr, offset, nullbitmap, nitems,
4918 : 807 : typlen, typbyval, typalign) - ptr;
4919 : : }
4920 : :
4921 : : /*
4922 : : * Copy nitems array elements from srcptr to destptr
4923 : : *
4924 : : * destptr: starting destination location (must be enough room!)
4925 : : * nitems: number of array elements to copy (>= 0)
4926 : : * srcptr: starting location in source array
4927 : : * offset: 0-based linear element number of first element (the one at *srcptr)
4928 : : * nullbitmap: start of source array's null bitmap, or NULL if none
4929 : : * typlen, typbyval, typalign: storage parameters of array element datatype
4930 : : *
4931 : : * Returns number of bytes copied
4932 : : *
4933 : : * NB: this does not take care of setting up the destination's null bitmap!
4934 : : */
4935 : : static int
4936 : 573 : array_copy(char *destptr, int nitems,
4937 : : char *srcptr, int offset, bits8 *nullbitmap,
4938 : : int typlen, bool typbyval, char typalign)
4939 : : {
4940 : : int numbytes;
4941 : :
4942 : 573 : numbytes = array_nelems_size(srcptr, offset, nullbitmap, nitems,
4943 : : typlen, typbyval, typalign);
4944 : 573 : memcpy(destptr, srcptr, numbytes);
9176 4945 : 573 : return numbytes;
4946 : : }
4947 : :
4948 : : /*
4949 : : * Copy nitems null-bitmap bits from source to destination
4950 : : *
4951 : : * destbitmap: start of destination array's null bitmap (mustn't be NULL)
4952 : : * destoffset: 0-based linear element number of first dest element
4953 : : * srcbitmap: start of source array's null bitmap, or NULL if none
4954 : : * srcoffset: 0-based linear element number of first source element
4955 : : * nitems: number of bits to copy (>= 0)
4956 : : *
4957 : : * If srcbitmap is NULL then we assume the source is all-non-NULL and
4958 : : * fill 1's into the destination bitmap. Note that only the specified
4959 : : * bits in the destination map are changed, not any before or after.
4960 : : *
4961 : : * Note: this could certainly be optimized using standard bitblt methods.
4962 : : * However, it's not clear that the typical Postgres array has enough elements
4963 : : * to make it worth worrying too much. For the moment, KISS.
4964 : : */
4965 : : void
7233 4966 : 15440 : array_bitmap_copy(bits8 *destbitmap, int destoffset,
4967 : : const bits8 *srcbitmap, int srcoffset,
4968 : : int nitems)
4969 : : {
4970 : : int destbitmask,
4971 : : destbitval,
4972 : : srcbitmask,
4973 : : srcbitval;
4974 : :
4975 [ - + ]: 15440 : Assert(destbitmap);
4976 [ + + ]: 15440 : if (nitems <= 0)
4977 : 82 : return; /* don't risk fetch off end of memory */
4978 : 15358 : destbitmap += destoffset / 8;
4979 : 15358 : destbitmask = 1 << (destoffset % 8);
4980 : 15358 : destbitval = *destbitmap;
4981 [ + + ]: 15358 : if (srcbitmap)
4982 : : {
4983 : 7823 : srcbitmap += srcoffset / 8;
4984 : 7823 : srcbitmask = 1 << (srcoffset % 8);
4985 : 7823 : srcbitval = *srcbitmap;
4986 [ + + ]: 35813 : while (nitems-- > 0)
4987 : : {
4988 [ + + ]: 27990 : if (srcbitval & srcbitmask)
4989 : 10412 : destbitval |= destbitmask;
4990 : : else
4991 : 17578 : destbitval &= ~destbitmask;
4992 : 27990 : destbitmask <<= 1;
4993 [ + + ]: 27990 : if (destbitmask == 0x100)
4994 : : {
4995 : 3266 : *destbitmap++ = destbitval;
4996 : 3266 : destbitmask = 1;
4997 [ + + ]: 3266 : if (nitems > 0)
4998 : 2473 : destbitval = *destbitmap;
4999 : : }
5000 : 27990 : srcbitmask <<= 1;
5001 [ + + ]: 27990 : if (srcbitmask == 0x100)
5002 : : {
5003 : 2467 : srcbitmap++;
5004 : 2467 : srcbitmask = 1;
5005 [ + + ]: 2467 : if (nitems > 0)
5006 : 2446 : srcbitval = *srcbitmap;
5007 : : }
5008 : : }
5009 [ + + ]: 7823 : if (destbitmask != 1)
5010 : 7030 : *destbitmap = destbitval;
5011 : : }
5012 : : else
5013 : : {
5014 [ + + ]: 15132 : while (nitems-- > 0)
5015 : : {
5016 : 7597 : destbitval |= destbitmask;
5017 : 7597 : destbitmask <<= 1;
5018 [ + + ]: 7597 : if (destbitmask == 0x100)
5019 : : {
5020 : 1080 : *destbitmap++ = destbitval;
5021 : 1080 : destbitmask = 1;
5022 [ - + ]: 1080 : if (nitems > 0)
7233 tgl@sss.pgh.pa.us 5023 :UBC 0 : destbitval = *destbitmap;
5024 : : }
5025 : : }
7233 tgl@sss.pgh.pa.us 5026 [ + + ]:CBC 7535 : if (destbitmask != 1)
5027 : 6455 : *destbitmap = destbitval;
5028 : : }
5029 : : }
5030 : :
5031 : : /*
5032 : : * Compute space needed for a slice of an array
5033 : : *
5034 : : * We assume the caller has verified that the slice coordinates are valid.
5035 : : */
5036 : : static int
5037 : 147 : array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
5038 : : int ndim, int *dim, int *lb,
5039 : : int *st, int *endp,
5040 : : int typlen, bool typbyval, char typalign)
5041 : : {
5042 : : int src_offset,
5043 : : span[MAXDIM],
5044 : : prod[MAXDIM],
5045 : : dist[MAXDIM],
5046 : : indx[MAXDIM];
5047 : : char *ptr;
5048 : : int i,
5049 : : j,
5050 : : inc;
10225 bruce@momjian.us 5051 : 147 : int count = 0;
5052 : :
9176 tgl@sss.pgh.pa.us 5053 : 147 : mda_get_range(ndim, span, st, endp);
5054 : :
5055 : : /* Pretty easy for fixed element length without nulls ... */
7233 5056 [ + + + + ]: 147 : if (typlen > 0 && !arraynullsptr)
6728 5057 [ + + + + : 108 : return ArrayGetNItems(ndim, span) * att_align_nominal(typlen, typalign);
+ + - + ]
5058 : :
5059 : : /* Else gotta do it the hard way */
7233 5060 : 39 : src_offset = ArrayGetOffset(ndim, dim, lb, st);
5061 : 39 : ptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
5062 : : typlen, typbyval, typalign);
9176 5063 : 39 : mda_get_prod(ndim, dim, prod);
5064 : 39 : mda_get_offset_values(ndim, dist, prod, span);
5065 [ + + ]: 99 : for (i = 0; i < ndim; i++)
9177 5066 : 60 : indx[i] = 0;
9176 5067 : 39 : j = ndim - 1;
5068 : : do
5069 : : {
7233 5070 [ - + ]: 129 : if (dist[j])
5071 : : {
7233 tgl@sss.pgh.pa.us 5072 :UBC 0 : ptr = array_seek(ptr, src_offset, arraynullsptr, dist[j],
5073 : : typlen, typbyval, typalign);
5074 : 0 : src_offset += dist[j];
5075 : : }
7233 tgl@sss.pgh.pa.us 5076 [ + + ]:CBC 129 : if (!array_get_isnull(arraynullsptr, src_offset))
5077 : : {
6728 5078 [ + + + - : 123 : inc = att_addlength_pointer(0, typlen, ptr);
- - - - -
- - - - +
- - ]
5079 [ + + - + : 123 : inc = att_align_nominal(inc, typalign);
- - - - ]
7233 5080 : 123 : ptr += inc;
5081 : 123 : count += inc;
5082 : : }
5083 : 129 : src_offset++;
9176 5084 [ + + ]: 129 : } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
10226 bruce@momjian.us 5085 : 39 : return count;
5086 : : }
5087 : :
5088 : : /*
5089 : : * Extract a slice of an array into consecutive elements in the destination
5090 : : * array.
5091 : : *
5092 : : * We assume the caller has verified that the slice coordinates are valid,
5093 : : * allocated enough storage for the result, and initialized the header
5094 : : * of the new array.
5095 : : */
5096 : : static void
7233 tgl@sss.pgh.pa.us 5097 : 132 : array_extract_slice(ArrayType *newarray,
5098 : : int ndim,
5099 : : int *dim,
5100 : : int *lb,
5101 : : char *arraydataptr,
5102 : : bits8 *arraynullsptr,
5103 : : int *st,
5104 : : int *endp,
5105 : : int typlen,
5106 : : bool typbyval,
5107 : : char typalign)
5108 : : {
5109 [ + + ]: 132 : char *destdataptr = ARR_DATA_PTR(newarray);
5110 [ + + ]: 132 : bits8 *destnullsptr = ARR_NULLBITMAP(newarray);
5111 : : char *srcdataptr;
5112 : : int src_offset,
5113 : : dest_offset,
5114 : : prod[MAXDIM],
5115 : : span[MAXDIM],
5116 : : dist[MAXDIM],
5117 : : indx[MAXDIM];
5118 : : int i,
5119 : : j,
5120 : : inc;
5121 : :
5122 : 132 : src_offset = ArrayGetOffset(ndim, dim, lb, st);
5123 : 132 : srcdataptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
5124 : : typlen, typbyval, typalign);
9176 5125 : 132 : mda_get_prod(ndim, dim, prod);
5126 : 132 : mda_get_range(ndim, span, st, endp);
5127 : 132 : mda_get_offset_values(ndim, dist, prod, span);
5128 [ + + ]: 336 : for (i = 0; i < ndim; i++)
5129 : 204 : indx[i] = 0;
7233 5130 : 132 : dest_offset = 0;
9176 5131 : 132 : j = ndim - 1;
5132 : : do
5133 : : {
7233 5134 [ + + ]: 495 : if (dist[j])
5135 : : {
5136 : : /* skip unwanted elements */
5137 : 15 : srcdataptr = array_seek(srcdataptr, src_offset, arraynullsptr,
5138 : : dist[j],
5139 : : typlen, typbyval, typalign);
5140 : 15 : src_offset += dist[j];
5141 : : }
5142 : 495 : inc = array_copy(destdataptr, 1,
5143 : : srcdataptr, src_offset, arraynullsptr,
5144 : : typlen, typbyval, typalign);
5145 [ + + ]: 495 : if (destnullsptr)
5146 : 90 : array_bitmap_copy(destnullsptr, dest_offset,
5147 : : arraynullsptr, src_offset,
5148 : : 1);
5149 : 495 : destdataptr += inc;
5150 : 495 : srcdataptr += inc;
5151 : 495 : src_offset++;
5152 : 495 : dest_offset++;
9176 5153 [ + + ]: 495 : } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
10651 scrappy@hub.org 5154 : 132 : }
5155 : :
5156 : : /*
5157 : : * Insert a slice into an array.
5158 : : *
5159 : : * ndim/dim[]/lb[] are dimensions of the original array. A new array with
5160 : : * those same dimensions is to be constructed. destArray must already
5161 : : * have been allocated and its header initialized.
5162 : : *
5163 : : * st[]/endp[] identify the slice to be replaced. Elements within the slice
5164 : : * volume are taken from consecutive elements of the srcArray; elements
5165 : : * outside it are copied from origArray.
5166 : : *
5167 : : * We assume the caller has verified that the slice coordinates are valid.
5168 : : */
5169 : : static void
7233 tgl@sss.pgh.pa.us 5170 : 15 : array_insert_slice(ArrayType *destArray,
5171 : : ArrayType *origArray,
5172 : : ArrayType *srcArray,
5173 : : int ndim,
5174 : : int *dim,
5175 : : int *lb,
5176 : : int *st,
5177 : : int *endp,
5178 : : int typlen,
5179 : : bool typbyval,
5180 : : char typalign)
5181 : : {
5182 [ - + ]: 15 : char *destPtr = ARR_DATA_PTR(destArray);
5183 [ - + ]: 15 : char *origPtr = ARR_DATA_PTR(origArray);
5184 [ - + ]: 15 : char *srcPtr = ARR_DATA_PTR(srcArray);
5185 [ - + ]: 15 : bits8 *destBitmap = ARR_NULLBITMAP(destArray);
5186 [ - + ]: 15 : bits8 *origBitmap = ARR_NULLBITMAP(origArray);
5187 [ - + ]: 15 : bits8 *srcBitmap = ARR_NULLBITMAP(srcArray);
5188 : 15 : int orignitems = ArrayGetNItems(ARR_NDIM(origArray),
5189 : : ARR_DIMS(origArray));
5190 : : int dest_offset,
5191 : : orig_offset,
5192 : : src_offset,
5193 : : prod[MAXDIM],
5194 : : span[MAXDIM],
5195 : : dist[MAXDIM],
5196 : : indx[MAXDIM];
5197 : : int i,
5198 : : j,
5199 : : inc;
5200 : :
5201 : 15 : dest_offset = ArrayGetOffset(ndim, dim, lb, st);
5202 : : /* copy items before the slice start */
5203 : 15 : inc = array_copy(destPtr, dest_offset,
5204 : : origPtr, 0, origBitmap,
5205 : : typlen, typbyval, typalign);
9176 5206 : 15 : destPtr += inc;
5207 : 15 : origPtr += inc;
7233 5208 [ - + ]: 15 : if (destBitmap)
7233 tgl@sss.pgh.pa.us 5209 :UBC 0 : array_bitmap_copy(destBitmap, 0, origBitmap, 0, dest_offset);
7233 tgl@sss.pgh.pa.us 5210 :CBC 15 : orig_offset = dest_offset;
9176 5211 : 15 : mda_get_prod(ndim, dim, prod);
5212 : 15 : mda_get_range(ndim, span, st, endp);
5213 : 15 : mda_get_offset_values(ndim, dist, prod, span);
5214 [ + + ]: 51 : for (i = 0; i < ndim; i++)
5215 : 36 : indx[i] = 0;
7233 5216 : 15 : src_offset = 0;
9176 5217 : 15 : j = ndim - 1;
5218 : : do
5219 : : {
5220 : : /* Copy/advance over elements between here and next part of slice */
7233 5221 [ + + ]: 39 : if (dist[j])
5222 : : {
5223 : 9 : inc = array_copy(destPtr, dist[j],
5224 : : origPtr, orig_offset, origBitmap,
5225 : : typlen, typbyval, typalign);
5226 : 9 : destPtr += inc;
5227 : 9 : origPtr += inc;
5228 [ - + ]: 9 : if (destBitmap)
7233 tgl@sss.pgh.pa.us 5229 :UBC 0 : array_bitmap_copy(destBitmap, dest_offset,
5230 : : origBitmap, orig_offset,
5231 : : dist[j]);
7233 tgl@sss.pgh.pa.us 5232 :CBC 9 : dest_offset += dist[j];
5233 : 9 : orig_offset += dist[j];
5234 : : }
5235 : : /* Copy new element at this slice position */
5236 : 39 : inc = array_copy(destPtr, 1,
5237 : : srcPtr, src_offset, srcBitmap,
5238 : : typlen, typbyval, typalign);
5239 [ - + ]: 39 : if (destBitmap)
7233 tgl@sss.pgh.pa.us 5240 :UBC 0 : array_bitmap_copy(destBitmap, dest_offset,
5241 : : srcBitmap, src_offset,
5242 : : 1);
9176 tgl@sss.pgh.pa.us 5243 :CBC 39 : destPtr += inc;
5244 : 39 : srcPtr += inc;
7233 5245 : 39 : dest_offset++;
5246 : 39 : src_offset++;
5247 : : /* Advance over old element at this slice position */
5248 : 39 : origPtr = array_seek(origPtr, orig_offset, origBitmap, 1,
5249 : : typlen, typbyval, typalign);
5250 : 39 : orig_offset++;
9176 5251 [ + + ]: 39 : } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
5252 : :
5253 : : /* don't miss any data at the end */
7233 5254 : 15 : array_copy(destPtr, orignitems - orig_offset,
5255 : : origPtr, orig_offset, origBitmap,
5256 : : typlen, typbyval, typalign);
5257 [ - + ]: 15 : if (destBitmap)
7233 tgl@sss.pgh.pa.us 5258 :UBC 0 : array_bitmap_copy(destBitmap, dest_offset,
5259 : : origBitmap, orig_offset,
5260 : : orignitems - orig_offset);
10651 scrappy@hub.org 5261 :CBC 15 : }
5262 : :
5263 : : /*
5264 : : * initArrayResult - initialize an empty ArrayBuildState
5265 : : *
5266 : : * element_type is the array element type (must be a valid array element type)
5267 : : * rcontext is where to keep working state
5268 : : * subcontext is a flag determining whether to use a separate memory context
5269 : : *
5270 : : * Note: there are two common schemes for using accumArrayResult().
5271 : : * In the older scheme, you start with a NULL ArrayBuildState pointer, and
5272 : : * call accumArrayResult once per element. In this scheme you end up with
5273 : : * a NULL pointer if there were no elements, which you need to special-case.
5274 : : * In the newer scheme, call initArrayResult and then call accumArrayResult
5275 : : * once per element. In this scheme you always end with a non-NULL pointer
5276 : : * that you can pass to makeArrayResult; you get an empty array if there
5277 : : * were no elements. This is preferred if an empty array is what you want.
5278 : : *
5279 : : * It's possible to choose whether to create a separate memory context for the
5280 : : * array build state, or whether to allocate it directly within rcontext.
5281 : : *
5282 : : * When there are many concurrent small states (e.g. array_agg() using hash
5283 : : * aggregation of many small groups), using a separate memory context for each
5284 : : * one may result in severe memory bloat. In such cases, use the same memory
5285 : : * context to initialize all such array build states, and pass
5286 : : * subcontext=false.
5287 : : *
5288 : : * In cases when the array build states have different lifetimes, using a
5289 : : * single memory context is impractical. Instead, pass subcontext=true so that
5290 : : * the array build states can be freed individually.
5291 : : */
5292 : : ArrayBuildState *
3850 jdavis@postgresql.or 5293 : 201816 : initArrayResult(Oid element_type, MemoryContext rcontext, bool subcontext)
5294 : : {
5295 : : /*
5296 : : * When using a subcontext, we can afford to start with a somewhat larger
5297 : : * initial array size. Without subcontexts, we'd better hope that most of
5298 : : * the states stay small ...
5299 : : */
957 drowley@postgresql.o 5300 [ + + ]: 201816 : return initArrayResultWithSize(element_type, rcontext, subcontext,
5301 : : subcontext ? 64 : 8);
5302 : : }
5303 : :
5304 : : /*
5305 : : * initArrayResultWithSize
5306 : : * As initArrayResult, but allow the initial size of the allocated arrays
5307 : : * to be specified.
5308 : : */
5309 : : ArrayBuildState *
5310 : 201906 : initArrayResultWithSize(Oid element_type, MemoryContext rcontext,
5311 : : bool subcontext, int initsize)
5312 : : {
5313 : : ArrayBuildState *astate;
3850 jdavis@postgresql.or 5314 : 201906 : MemoryContext arr_context = rcontext;
5315 : :
5316 : : /* Make a temporary context to hold all the junk */
5317 [ + + ]: 201906 : if (subcontext)
5318 : 132031 : arr_context = AllocSetContextCreate(rcontext,
5319 : : "accumArrayResult",
5320 : : ALLOCSET_DEFAULT_SIZES);
5321 : :
5322 : : astate = (ArrayBuildState *)
3938 tgl@sss.pgh.pa.us 5323 : 201906 : MemoryContextAlloc(arr_context, sizeof(ArrayBuildState));
5324 : 201906 : astate->mcontext = arr_context;
3850 jdavis@postgresql.or 5325 : 201906 : astate->private_cxt = subcontext;
957 drowley@postgresql.o 5326 : 201906 : astate->alen = initsize;
3938 tgl@sss.pgh.pa.us 5327 : 201906 : astate->dvalues = (Datum *)
5328 : 201906 : MemoryContextAlloc(arr_context, astate->alen * sizeof(Datum));
5329 : 201906 : astate->dnulls = (bool *)
5330 : 201906 : MemoryContextAlloc(arr_context, astate->alen * sizeof(bool));
5331 : 201906 : astate->nelems = 0;
5332 : 201906 : astate->element_type = element_type;
5333 : 201906 : get_typlenbyvalalign(element_type,
5334 : : &astate->typlen,
5335 : : &astate->typbyval,
5336 : : &astate->typalign);
5337 : :
5338 : 201906 : return astate;
5339 : : }
5340 : :
5341 : : /*
5342 : : * accumArrayResult - accumulate one (more) Datum for an array result
5343 : : *
5344 : : * astate is working state (can be NULL on first call)
5345 : : * dvalue/disnull represent the new Datum to append to the array
5346 : : * element_type is the Datum's type (must be a valid array element type)
5347 : : * rcontext is where to keep working state
5348 : : */
5349 : : ArrayBuildState *
8065 bruce@momjian.us 5350 : 1727992 : accumArrayResult(ArrayBuildState *astate,
5351 : : Datum dvalue, bool disnull,
5352 : : Oid element_type,
5353 : : MemoryContext rcontext)
5354 : : {
5355 : : MemoryContext oldcontext;
5356 : :
8107 tgl@sss.pgh.pa.us 5357 [ + + ]: 1727992 : if (astate == NULL)
5358 : : {
5359 : : /* First time through --- initialize */
3850 jdavis@postgresql.or 5360 : 102536 : astate = initArrayResult(element_type, rcontext, true);
5361 : : }
5362 : : else
5363 : : {
8107 tgl@sss.pgh.pa.us 5364 [ - + ]: 1625456 : Assert(astate->element_type == element_type);
5365 : : }
5366 : :
3938 5367 : 1727992 : oldcontext = MemoryContextSwitchTo(astate->mcontext);
5368 : :
5369 : : /* enlarge dvalues[]/dnulls[] if needed */
5370 [ + + ]: 1727992 : if (astate->nelems >= astate->alen)
5371 : : {
5372 : 39856 : astate->alen *= 2;
5373 : : /* give an array-related error if we go past MaxAllocSize */
785 5374 [ - + ]: 39856 : if (!AllocSizeIsValid(astate->alen * sizeof(Datum)))
785 tgl@sss.pgh.pa.us 5375 [ # # ]:UBC 0 : ereport(ERROR,
5376 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
5377 : : errmsg("array size exceeds the maximum allowed (%d)",
5378 : : (int) MaxAllocSize)));
3938 tgl@sss.pgh.pa.us 5379 :CBC 39856 : astate->dvalues = (Datum *)
5380 : 39856 : repalloc(astate->dvalues, astate->alen * sizeof(Datum));
5381 : 39856 : astate->dnulls = (bool *)
5382 : 39856 : repalloc(astate->dnulls, astate->alen * sizeof(bool));
5383 : : }
5384 : :
5385 : : /*
5386 : : * Ensure pass-by-ref stuff is copied into mcontext; and detoast it too if
5387 : : * it's varlena. (You might think that detoasting is not needed here
5388 : : * because construct_md_array can detoast the array elements later.
5389 : : * However, we must not let construct_md_array modify the ArrayBuildState
5390 : : * because that would mean array_agg_finalfn damages its input, which is
5391 : : * verboten. Also, this way frequently saves one copying step.)
5392 : : */
7233 5393 [ + + + + ]: 1727992 : if (!disnull && !astate->typbyval)
5394 : : {
5922 5395 [ + + ]: 1241924 : if (astate->typlen == -1)
5396 : 926116 : dvalue = PointerGetDatum(PG_DETOAST_DATUM_COPY(dvalue));
5397 : : else
5398 : 315808 : dvalue = datumCopy(dvalue, astate->typbyval, astate->typlen);
5399 : : }
5400 : :
7233 5401 : 1727992 : astate->dvalues[astate->nelems] = dvalue;
5402 : 1727992 : astate->dnulls[astate->nelems] = disnull;
5403 : 1727992 : astate->nelems++;
5404 : :
8107 5405 : 1727992 : MemoryContextSwitchTo(oldcontext);
5406 : :
5407 : 1727992 : return astate;
5408 : : }
5409 : :
5410 : : /*
5411 : : * makeArrayResult - produce 1-D final result of accumArrayResult
5412 : : *
5413 : : * Note: only releases astate if it was initialized within a separate memory
5414 : : * context (i.e. using subcontext=true when calling initArrayResult).
5415 : : *
5416 : : * astate is working state (must not be NULL)
5417 : : * rcontext is where to construct result
5418 : : */
5419 : : Datum
8065 bruce@momjian.us 5420 : 102630 : makeArrayResult(ArrayBuildState *astate,
5421 : : MemoryContext rcontext)
5422 : : {
5423 : : int ndims;
5424 : : int dims[1];
5425 : : int lbs[1];
5426 : :
5427 : : /* If no elements were presented, we want to create an empty array */
3938 tgl@sss.pgh.pa.us 5428 : 102630 : ndims = (astate->nelems > 0) ? 1 : 0;
8107 5429 : 102630 : dims[0] = astate->nelems;
5430 : 102630 : lbs[0] = 1;
5431 : :
3850 jdavis@postgresql.or 5432 : 205260 : return makeMdArrayResult(astate, ndims, dims, lbs, rcontext,
5433 : 102630 : astate->private_cxt);
5434 : : }
5435 : :
5436 : : /*
5437 : : * makeMdArrayResult - produce multi-D final result of accumArrayResult
5438 : : *
5439 : : * beware: no check that specified dimensions match the number of values
5440 : : * accumulated.
5441 : : *
5442 : : * Note: if the astate was not initialized within a separate memory context
5443 : : * (that is, initArrayResult was called with subcontext=false), then using
5444 : : * release=true is illegal. Instead, release astate along with the rest of its
5445 : : * context when appropriate.
5446 : : *
5447 : : * astate is working state (must not be NULL)
5448 : : * rcontext is where to construct result
5449 : : * release is true if okay to release working state
5450 : : */
5451 : : Datum
8065 bruce@momjian.us 5452 : 201264 : makeMdArrayResult(ArrayBuildState *astate,
5453 : : int ndims,
5454 : : int *dims,
5455 : : int *lbs,
5456 : : MemoryContext rcontext,
5457 : : bool release)
5458 : : {
5459 : : ArrayType *result;
5460 : : MemoryContext oldcontext;
5461 : :
5462 : : /* Build the final array result in rcontext */
8107 tgl@sss.pgh.pa.us 5463 : 201264 : oldcontext = MemoryContextSwitchTo(rcontext);
5464 : :
5465 : 201264 : result = construct_md_array(astate->dvalues,
5466 : : astate->dnulls,
5467 : : ndims,
5468 : : dims,
5469 : : lbs,
5470 : : astate->element_type,
5471 : 201264 : astate->typlen,
5472 : 201264 : astate->typbyval,
5473 : 201264 : astate->typalign);
5474 : :
5475 : 201264 : MemoryContextSwitchTo(oldcontext);
5476 : :
5477 : : /* Clean up all the junk */
6096 5478 [ + + ]: 201264 : if (release)
5479 : : {
3850 jdavis@postgresql.or 5480 [ - + ]: 131729 : Assert(astate->private_cxt);
6096 tgl@sss.pgh.pa.us 5481 : 131729 : MemoryContextDelete(astate->mcontext);
5482 : : }
5483 : :
8107 5484 : 201264 : return PointerGetDatum(result);
5485 : : }
5486 : :
5487 : : /*
5488 : : * The following three functions provide essentially the same API as
5489 : : * initArrayResult/accumArrayResult/makeArrayResult, but instead of accepting
5490 : : * inputs that are array elements, they accept inputs that are arrays and
5491 : : * produce an output array having N+1 dimensions. The inputs must all have
5492 : : * identical dimensionality as well as element type.
5493 : : */
5494 : :
5495 : : /*
5496 : : * initArrayResultArr - initialize an empty ArrayBuildStateArr
5497 : : *
5498 : : * array_type is the array type (must be a valid varlena array type)
5499 : : * element_type is the type of the array's elements (lookup if InvalidOid)
5500 : : * rcontext is where to keep working state
5501 : : * subcontext is a flag determining whether to use a separate memory context
5502 : : */
5503 : : ArrayBuildStateArr *
3850 jdavis@postgresql.or 5504 : 246 : initArrayResultArr(Oid array_type, Oid element_type, MemoryContext rcontext,
5505 : : bool subcontext)
5506 : : {
5507 : : ArrayBuildStateArr *astate;
2999 tgl@sss.pgh.pa.us 5508 : 246 : MemoryContext arr_context = rcontext; /* by default use the parent ctx */
5509 : :
5510 : : /* Lookup element type, unless element_type already provided */
3768 5511 [ + + ]: 246 : if (!OidIsValid(element_type))
5512 : : {
3850 jdavis@postgresql.or 5513 : 186 : element_type = get_element_type(array_type);
5514 : :
5515 [ - + ]: 186 : if (!OidIsValid(element_type))
3850 jdavis@postgresql.or 5516 [ # # ]:UBC 0 : ereport(ERROR,
5517 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
5518 : : errmsg("data type %s is not an array type",
5519 : : format_type_be(array_type))));
5520 : : }
5521 : :
5522 : : /* Make a temporary context to hold all the junk */
3850 jdavis@postgresql.or 5523 [ + + ]:CBC 246 : if (subcontext)
5524 : 24 : arr_context = AllocSetContextCreate(rcontext,
5525 : : "accumArrayResultArr",
5526 : : ALLOCSET_DEFAULT_SIZES);
5527 : :
5528 : : /* Note we initialize all fields to zero */
5529 : : astate = (ArrayBuildStateArr *)
3938 tgl@sss.pgh.pa.us 5530 : 246 : MemoryContextAllocZero(arr_context, sizeof(ArrayBuildStateArr));
5531 : 246 : astate->mcontext = arr_context;
3850 jdavis@postgresql.or 5532 : 246 : astate->private_cxt = subcontext;
5533 : :
5534 : : /* Save relevant datatype information */
3938 tgl@sss.pgh.pa.us 5535 : 246 : astate->array_type = array_type;
5536 : 246 : astate->element_type = element_type;
5537 : :
5538 : 246 : return astate;
5539 : : }
5540 : :
5541 : : /*
5542 : : * accumArrayResultArr - accumulate one (more) sub-array for an array result
5543 : : *
5544 : : * astate is working state (can be NULL on first call)
5545 : : * dvalue/disnull represent the new sub-array to append to the array
5546 : : * array_type is the array type (must be a valid varlena array type)
5547 : : * rcontext is where to keep working state
5548 : : */
5549 : : ArrayBuildStateArr *
5550 : 30180 : accumArrayResultArr(ArrayBuildStateArr *astate,
5551 : : Datum dvalue, bool disnull,
5552 : : Oid array_type,
5553 : : MemoryContext rcontext)
5554 : : {
5555 : : ArrayType *arg;
5556 : : MemoryContext oldcontext;
5557 : : int *dims,
5558 : : *lbs,
5559 : : ndims,
5560 : : nitems,
5561 : : ndatabytes;
5562 : : char *data;
5563 : : int i;
5564 : :
5565 : : /*
5566 : : * We disallow accumulating null subarrays. Another plausible definition
5567 : : * is to ignore them, but callers that want that can just skip calling
5568 : : * this function.
5569 : : */
5570 [ + + ]: 30180 : if (disnull)
5571 [ + - ]: 3 : ereport(ERROR,
5572 : : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5573 : : errmsg("cannot accumulate null arrays")));
5574 : :
5575 : : /* Detoast input array in caller's context */
5576 : 30177 : arg = DatumGetArrayTypeP(dvalue);
5577 : :
5578 [ - + ]: 30177 : if (astate == NULL)
3850 jdavis@postgresql.or 5579 :UBC 0 : astate = initArrayResultArr(array_type, InvalidOid, rcontext, true);
5580 : : else
3938 tgl@sss.pgh.pa.us 5581 [ - + ]:CBC 30177 : Assert(astate->array_type == array_type);
5582 : :
5583 : 30177 : oldcontext = MemoryContextSwitchTo(astate->mcontext);
5584 : :
5585 : : /* Collect this input's dimensions */
5586 : 30177 : ndims = ARR_NDIM(arg);
5587 : 30177 : dims = ARR_DIMS(arg);
5588 : 30177 : lbs = ARR_LBOUND(arg);
5589 [ + + ]: 30177 : data = ARR_DATA_PTR(arg);
5590 : 30177 : nitems = ArrayGetNItems(ndims, dims);
5591 [ + + ]: 30177 : ndatabytes = ARR_SIZE(arg) - ARR_DATA_OFFSET(arg);
5592 : :
5593 [ + + ]: 30177 : if (astate->ndims == 0)
5594 : : {
5595 : : /* First input; check/save the dimensionality info */
5596 : :
5597 : : /* Should we allow empty inputs and just produce an empty output? */
5598 [ + + ]: 153 : if (ndims == 0)
5599 [ + - ]: 3 : ereport(ERROR,
5600 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5601 : : errmsg("cannot accumulate empty arrays")));
5602 [ - + ]: 150 : if (ndims + 1 > MAXDIM)
3938 tgl@sss.pgh.pa.us 5603 [ # # ]:UBC 0 : ereport(ERROR,
5604 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
5605 : : errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
5606 : : ndims + 1, MAXDIM)));
5607 : :
5608 : : /*
5609 : : * The output array will have n+1 dimensions, with the ones after the
5610 : : * first matching the input's dimensions.
5611 : : */
3938 tgl@sss.pgh.pa.us 5612 :CBC 150 : astate->ndims = ndims + 1;
5613 : 150 : astate->dims[0] = 0;
5614 : 150 : memcpy(&astate->dims[1], dims, ndims * sizeof(int));
5615 : 150 : astate->lbs[0] = 1;
5616 : 150 : memcpy(&astate->lbs[1], lbs, ndims * sizeof(int));
5617 : :
5618 : : /* Allocate at least enough data space for this item */
1977 drowley@postgresql.o 5619 : 150 : astate->abytes = pg_nextpower2_32(Max(1024, ndatabytes + 1));
3938 tgl@sss.pgh.pa.us 5620 : 150 : astate->data = (char *) palloc(astate->abytes);
5621 : : }
5622 : : else
5623 : : {
5624 : : /* Second or later input: must match first input's dimensionality */
5625 [ - + ]: 30024 : if (astate->ndims != ndims + 1)
3938 tgl@sss.pgh.pa.us 5626 [ # # ]:UBC 0 : ereport(ERROR,
5627 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5628 : : errmsg("cannot accumulate arrays of different dimensionality")));
3938 tgl@sss.pgh.pa.us 5629 [ + + ]:CBC 60045 : for (i = 0; i < ndims; i++)
5630 : : {
5631 [ + + - + ]: 30024 : if (astate->dims[i + 1] != dims[i] || astate->lbs[i + 1] != lbs[i])
5632 [ + - ]: 3 : ereport(ERROR,
5633 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5634 : : errmsg("cannot accumulate arrays of different dimensionality")));
5635 : : }
5636 : :
5637 : : /* Enlarge data space if needed */
5638 [ + + ]: 30021 : if (astate->nbytes + ndatabytes > astate->abytes)
5639 : : {
5640 : 25 : astate->abytes = Max(astate->abytes * 2,
5641 : : astate->nbytes + ndatabytes);
5642 : 25 : astate->data = (char *) repalloc(astate->data, astate->abytes);
5643 : : }
5644 : : }
5645 : :
5646 : : /*
5647 : : * Copy the data portion of the sub-array. Note we assume that the
5648 : : * advertised data length of the sub-array is properly aligned. We do not
5649 : : * have to worry about detoasting elements since whatever's in the
5650 : : * sub-array should be OK already.
5651 : : */
5652 : 30171 : memcpy(astate->data + astate->nbytes, data, ndatabytes);
5653 : 30171 : astate->nbytes += ndatabytes;
5654 : :
5655 : : /* Deal with null bitmap if needed */
5656 [ + + + + ]: 30171 : if (astate->nullbitmap || ARR_HASNULL(arg))
5657 : : {
5658 : 14986 : int newnitems = astate->nitems + nitems;
5659 : :
5660 [ + + ]: 14986 : if (astate->nullbitmap == NULL)
5661 : : {
5662 : : /*
5663 : : * First input with nulls; we must retrospectively handle any
5664 : : * previous inputs by marking all their items non-null.
5665 : : */
1977 drowley@postgresql.o 5666 : 48 : astate->aitems = pg_nextpower2_32(Max(256, newnitems + 1));
3938 tgl@sss.pgh.pa.us 5667 : 48 : astate->nullbitmap = (bits8 *) palloc((astate->aitems + 7) / 8);
5668 : 48 : array_bitmap_copy(astate->nullbitmap, 0,
5669 : : NULL, 0,
5670 : : astate->nitems);
5671 : : }
5672 [ + + ]: 14938 : else if (newnitems > astate->aitems)
5673 : : {
5674 : 25 : astate->aitems = Max(astate->aitems * 2, newnitems);
5675 : 25 : astate->nullbitmap = (bits8 *)
5676 : 25 : repalloc(astate->nullbitmap, (astate->aitems + 7) / 8);
5677 : : }
5678 : 14986 : array_bitmap_copy(astate->nullbitmap, astate->nitems,
5679 [ + + ]: 14986 : ARR_NULLBITMAP(arg), 0,
5680 : : nitems);
5681 : : }
5682 : :
5683 : 30171 : astate->nitems += nitems;
5684 : 30171 : astate->dims[0] += 1;
5685 : :
5686 : 30171 : MemoryContextSwitchTo(oldcontext);
5687 : :
5688 : : /* Release detoasted copy if any */
5689 [ + + ]: 30171 : if ((Pointer) arg != DatumGetPointer(dvalue))
5690 : 63 : pfree(arg);
5691 : :
5692 : 30171 : return astate;
5693 : : }
5694 : :
5695 : : /*
5696 : : * makeArrayResultArr - produce N+1-D final result of accumArrayResultArr
5697 : : *
5698 : : * astate is working state (must not be NULL)
5699 : : * rcontext is where to construct result
5700 : : * release is true if okay to release working state
5701 : : */
5702 : : Datum
5703 : 117 : makeArrayResultArr(ArrayBuildStateArr *astate,
5704 : : MemoryContext rcontext,
5705 : : bool release)
5706 : : {
5707 : : ArrayType *result;
5708 : : MemoryContext oldcontext;
5709 : :
5710 : : /* Build the final array result in rcontext */
5711 : 117 : oldcontext = MemoryContextSwitchTo(rcontext);
5712 : :
5713 [ - + ]: 117 : if (astate->ndims == 0)
5714 : : {
5715 : : /* No inputs, return empty array */
3938 tgl@sss.pgh.pa.us 5716 :UBC 0 : result = construct_empty_array(astate->element_type);
5717 : : }
5718 : : else
5719 : : {
5720 : : int dataoffset,
5721 : : nbytes;
5722 : :
5723 : : /* Check for overflow of the array dimensions */
1580 tgl@sss.pgh.pa.us 5724 :CBC 117 : (void) ArrayGetNItems(astate->ndims, astate->dims);
5725 : 117 : ArrayCheckBounds(astate->ndims, astate->dims, astate->lbs);
5726 : :
5727 : : /* Compute required space */
3938 5728 : 117 : nbytes = astate->nbytes;
5729 [ + + ]: 117 : if (astate->nullbitmap != NULL)
5730 : : {
5731 : 33 : dataoffset = ARR_OVERHEAD_WITHNULLS(astate->ndims, astate->nitems);
5732 : 33 : nbytes += dataoffset;
5733 : : }
5734 : : else
5735 : : {
5736 : 84 : dataoffset = 0;
5737 : 84 : nbytes += ARR_OVERHEAD_NONULLS(astate->ndims);
5738 : : }
5739 : :
5740 : 117 : result = (ArrayType *) palloc0(nbytes);
5741 : 117 : SET_VARSIZE(result, nbytes);
5742 : 117 : result->ndim = astate->ndims;
5743 : 117 : result->dataoffset = dataoffset;
5744 : 117 : result->elemtype = astate->element_type;
5745 : :
5746 : 117 : memcpy(ARR_DIMS(result), astate->dims, astate->ndims * sizeof(int));
5747 : 117 : memcpy(ARR_LBOUND(result), astate->lbs, astate->ndims * sizeof(int));
5748 [ + + ]: 117 : memcpy(ARR_DATA_PTR(result), astate->data, astate->nbytes);
5749 : :
5750 [ + + ]: 117 : if (astate->nullbitmap != NULL)
5751 : 33 : array_bitmap_copy(ARR_NULLBITMAP(result), 0,
5752 [ + - ]: 33 : astate->nullbitmap, 0,
5753 : : astate->nitems);
5754 : : }
5755 : :
5756 : 117 : MemoryContextSwitchTo(oldcontext);
5757 : :
5758 : : /* Clean up all the junk */
5759 [ + + ]: 117 : if (release)
5760 : : {
3850 jdavis@postgresql.or 5761 [ - + ]: 24 : Assert(astate->private_cxt);
3938 tgl@sss.pgh.pa.us 5762 : 24 : MemoryContextDelete(astate->mcontext);
5763 : : }
5764 : :
5765 : 117 : return PointerGetDatum(result);
5766 : : }
5767 : :
5768 : : /*
5769 : : * The following three functions provide essentially the same API as
5770 : : * initArrayResult/accumArrayResult/makeArrayResult, but can accept either
5771 : : * scalar or array inputs, invoking the appropriate set of functions above.
5772 : : */
5773 : :
5774 : : /*
5775 : : * initArrayResultAny - initialize an empty ArrayBuildStateAny
5776 : : *
5777 : : * input_type is the input datatype (either element or array type)
5778 : : * rcontext is where to keep working state
5779 : : * subcontext is a flag determining whether to use a separate memory context
5780 : : */
5781 : : ArrayBuildStateAny *
3850 jdavis@postgresql.or 5782 : 28302 : initArrayResultAny(Oid input_type, MemoryContext rcontext, bool subcontext)
5783 : : {
5784 : : ArrayBuildStateAny *astate;
5785 : :
5786 : : /*
5787 : : * int2vector and oidvector will satisfy both get_element_type and
5788 : : * get_array_type. We prefer to treat them as scalars, to be consistent
5789 : : * with get_promoted_array_type. Hence, check get_array_type not
5790 : : * get_element_type.
5791 : : */
177 tgl@sss.pgh.pa.us 5792 [ + + ]: 28302 : if (!OidIsValid(get_array_type(input_type)))
5793 : : {
5794 : : /* Array case */
5795 : : ArrayBuildStateArr *arraystate;
5796 : :
3850 jdavis@postgresql.or 5797 : 24 : arraystate = initArrayResultArr(input_type, InvalidOid, rcontext, subcontext);
5798 : : astate = (ArrayBuildStateAny *)
3938 tgl@sss.pgh.pa.us 5799 : 24 : MemoryContextAlloc(arraystate->mcontext,
5800 : : sizeof(ArrayBuildStateAny));
5801 : 24 : astate->scalarstate = NULL;
5802 : 24 : astate->arraystate = arraystate;
5803 : : }
5804 : : else
5805 : : {
5806 : : /* Scalar case */
5807 : : ArrayBuildState *scalarstate;
5808 : :
3850 jdavis@postgresql.or 5809 : 28278 : scalarstate = initArrayResult(input_type, rcontext, subcontext);
5810 : : astate = (ArrayBuildStateAny *)
3938 tgl@sss.pgh.pa.us 5811 : 28278 : MemoryContextAlloc(scalarstate->mcontext,
5812 : : sizeof(ArrayBuildStateAny));
5813 : 28278 : astate->scalarstate = scalarstate;
5814 : 28278 : astate->arraystate = NULL;
5815 : : }
5816 : :
5817 : 28302 : return astate;
5818 : : }
5819 : :
5820 : : /*
5821 : : * accumArrayResultAny - accumulate one (more) input for an array result
5822 : : *
5823 : : * astate is working state (can be NULL on first call)
5824 : : * dvalue/disnull represent the new input to append to the array
5825 : : * input_type is the input datatype (either element or array type)
5826 : : * rcontext is where to keep working state
5827 : : */
5828 : : ArrayBuildStateAny *
5829 : 8856 : accumArrayResultAny(ArrayBuildStateAny *astate,
5830 : : Datum dvalue, bool disnull,
5831 : : Oid input_type,
5832 : : MemoryContext rcontext)
5833 : : {
5834 [ + + ]: 8856 : if (astate == NULL)
3850 jdavis@postgresql.or 5835 : 60 : astate = initArrayResultAny(input_type, rcontext, true);
5836 : :
3938 tgl@sss.pgh.pa.us 5837 [ + + ]: 8856 : if (astate->scalarstate)
5838 : 8787 : (void) accumArrayResult(astate->scalarstate,
5839 : : dvalue, disnull,
5840 : : input_type, rcontext);
5841 : : else
5842 : 69 : (void) accumArrayResultArr(astate->arraystate,
5843 : : dvalue, disnull,
5844 : : input_type, rcontext);
5845 : :
5846 : 8856 : return astate;
5847 : : }
5848 : :
5849 : : /*
5850 : : * makeArrayResultAny - produce final result of accumArrayResultAny
5851 : : *
5852 : : * astate is working state (must not be NULL)
5853 : : * rcontext is where to construct result
5854 : : * release is true if okay to release working state
5855 : : */
5856 : : Datum
5857 : 28302 : makeArrayResultAny(ArrayBuildStateAny *astate,
5858 : : MemoryContext rcontext, bool release)
5859 : : {
5860 : : Datum result;
5861 : :
5862 [ + + ]: 28302 : if (astate->scalarstate)
5863 : : {
5864 : : /* Must use makeMdArrayResult to support "release" parameter */
5865 : : int ndims;
5866 : : int dims[1];
5867 : : int lbs[1];
5868 : :
5869 : : /* If no elements were presented, we want to create an empty array */
5870 : 28278 : ndims = (astate->scalarstate->nelems > 0) ? 1 : 0;
5871 : 28278 : dims[0] = astate->scalarstate->nelems;
5872 : 28278 : lbs[0] = 1;
5873 : :
5874 : 28278 : result = makeMdArrayResult(astate->scalarstate, ndims, dims, lbs,
5875 : : rcontext, release);
5876 : : }
5877 : : else
5878 : : {
5879 : 24 : result = makeArrayResultArr(astate->arraystate,
5880 : : rcontext, release);
5881 : : }
5882 : 28302 : return result;
5883 : : }
5884 : :
5885 : :
5886 : : Datum
7495 neilc@samurai.com 5887 : 144 : array_larger(PG_FUNCTION_ARGS)
5888 : : {
3768 tgl@sss.pgh.pa.us 5889 [ + + ]: 144 : if (array_cmp(fcinfo) > 0)
5890 : 72 : PG_RETURN_DATUM(PG_GETARG_DATUM(0));
5891 : : else
5892 : 69 : PG_RETURN_DATUM(PG_GETARG_DATUM(1));
5893 : : }
5894 : :
5895 : : Datum
7495 neilc@samurai.com 5896 : 129 : array_smaller(PG_FUNCTION_ARGS)
5897 : : {
3768 tgl@sss.pgh.pa.us 5898 [ + + ]: 129 : if (array_cmp(fcinfo) < 0)
5899 : 87 : PG_RETURN_DATUM(PG_GETARG_DATUM(0));
5900 : : else
5901 : 42 : PG_RETURN_DATUM(PG_GETARG_DATUM(1));
5902 : : }
5903 : :
5904 : :
5905 : : typedef struct generate_subscripts_fctx
5906 : : {
5907 : : int32 lower;
5908 : : int32 upper;
5909 : : bool reverse;
5910 : : } generate_subscripts_fctx;
5911 : :
5912 : : /*
5913 : : * generate_subscripts(array anyarray, dim int [, reverse bool])
5914 : : * Returns all subscripts of the array for any dimension
5915 : : */
5916 : : Datum
6340 alvherre@alvh.no-ip. 5917 : 2435 : generate_subscripts(PG_FUNCTION_ARGS)
5918 : : {
5919 : : FuncCallContext *funcctx;
5920 : : MemoryContext oldcontext;
5921 : : generate_subscripts_fctx *fctx;
5922 : :
5923 : : /* stuff done only on the first call of the function */
5924 [ + + ]: 2435 : if (SRF_IS_FIRSTCALL())
5925 : : {
2910 tgl@sss.pgh.pa.us 5926 : 1138 : AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
5931 bruce@momjian.us 5927 : 1138 : int reqdim = PG_GETARG_INT32(1);
5928 : : int *lb,
5929 : : *dimv;
5930 : :
5931 : : /* create a function context for cross-call persistence */
6340 alvherre@alvh.no-ip. 5932 : 1138 : funcctx = SRF_FIRSTCALL_INIT();
5933 : :
5934 : : /* Sanity check: does it look like an array at all? */
3768 tgl@sss.pgh.pa.us 5935 [ - + - - : 1138 : if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
+ + - + -
- - + ]
6340 alvherre@alvh.no-ip. 5936 : 3 : SRF_RETURN_DONE(funcctx);
5937 : :
5938 : : /* Sanity check: was the requested dim valid */
3768 tgl@sss.pgh.pa.us 5939 [ + - - + : 1135 : if (reqdim <= 0 || reqdim > AARR_NDIM(v))
- + ]
6340 alvherre@alvh.no-ip. 5940 :UBC 0 : SRF_RETURN_DONE(funcctx);
5941 : :
5942 : : /*
5943 : : * switch to memory context appropriate for multiple function calls
5944 : : */
6340 alvherre@alvh.no-ip. 5945 :CBC 1135 : oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
5946 : 1135 : fctx = (generate_subscripts_fctx *) palloc(sizeof(generate_subscripts_fctx));
5947 : :
3768 tgl@sss.pgh.pa.us 5948 [ - + ]: 1135 : lb = AARR_LBOUND(v);
5949 [ - + ]: 1135 : dimv = AARR_DIMS(v);
5950 : :
6340 alvherre@alvh.no-ip. 5951 : 1135 : fctx->lower = lb[reqdim - 1];
5952 : 1135 : fctx->upper = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
5953 [ - + - - ]: 1135 : fctx->reverse = (PG_NARGS() < 3) ? false : PG_GETARG_BOOL(2);
5954 : :
5955 : 1135 : funcctx->user_fctx = fctx;
5956 : :
5957 : 1135 : MemoryContextSwitchTo(oldcontext);
5958 : : }
5959 : :
5960 : 2432 : funcctx = SRF_PERCALL_SETUP();
5961 : :
5962 : 2432 : fctx = funcctx->user_fctx;
5963 : :
5964 [ + + ]: 2432 : if (fctx->lower <= fctx->upper)
5965 : : {
5966 [ + - ]: 1297 : if (!fctx->reverse)
5967 : 1297 : SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->lower++));
5968 : : else
6340 alvherre@alvh.no-ip. 5969 :UBC 0 : SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->upper--));
5970 : : }
5971 : : else
5972 : : /* done when there are no more elements left */
6340 alvherre@alvh.no-ip. 5973 :CBC 1135 : SRF_RETURN_DONE(funcctx);
5974 : : }
5975 : :
5976 : : /*
5977 : : * generate_subscripts_nodir
5978 : : * Implements the 2-argument version of generate_subscripts
5979 : : */
5980 : : Datum
5981 : 2435 : generate_subscripts_nodir(PG_FUNCTION_ARGS)
5982 : : {
5983 : : /* just call the other one -- it can handle both cases */
5984 : 2435 : return generate_subscripts(fcinfo);
5985 : : }
5986 : :
5987 : : /*
5988 : : * array_fill_with_lower_bounds
5989 : : * Create and fill array with defined lower bounds.
5990 : : */
5991 : : Datum
6261 bruce@momjian.us 5992 : 33 : array_fill_with_lower_bounds(PG_FUNCTION_ARGS)
5993 : : {
5994 : : ArrayType *dims;
5995 : : ArrayType *lbs;
5996 : : ArrayType *result;
5997 : : Oid elmtype;
5998 : : Datum value;
5999 : : bool isnull;
6000 : :
6001 [ + + + + ]: 33 : if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
6256 tgl@sss.pgh.pa.us 6002 [ + - ]: 6 : ereport(ERROR,
6003 : : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
6004 : : errmsg("dimension array or low bound array cannot be null")));
6005 : :
6261 bruce@momjian.us 6006 : 27 : dims = PG_GETARG_ARRAYTYPE_P(1);
5931 6007 : 27 : lbs = PG_GETARG_ARRAYTYPE_P(2);
6008 : :
6261 6009 [ + + ]: 27 : if (!PG_ARGISNULL(0))
6010 : : {
6011 : 21 : value = PG_GETARG_DATUM(0);
6012 : 21 : isnull = false;
6013 : : }
6014 : : else
6015 : : {
6016 : 6 : value = 0;
6017 : 6 : isnull = true;
6018 : : }
6019 : :
6256 tgl@sss.pgh.pa.us 6020 : 27 : elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
6021 [ - + ]: 27 : if (!OidIsValid(elmtype))
6256 tgl@sss.pgh.pa.us 6022 [ # # ]:UBC 0 : elog(ERROR, "could not determine data type of input");
6023 : :
6256 tgl@sss.pgh.pa.us 6024 :CBC 27 : result = array_fill_internal(dims, lbs, value, isnull, elmtype, fcinfo);
6261 bruce@momjian.us 6025 : 21 : PG_RETURN_ARRAYTYPE_P(result);
6026 : : }
6027 : :
6028 : : /*
6029 : : * array_fill
6030 : : * Create and fill array with default lower bounds.
6031 : : */
6032 : : Datum
6033 : 45 : array_fill(PG_FUNCTION_ARGS)
6034 : : {
6035 : : ArrayType *dims;
6036 : : ArrayType *result;
6037 : : Oid elmtype;
6038 : : Datum value;
6039 : : bool isnull;
6040 : :
6041 [ - + ]: 45 : if (PG_ARGISNULL(1))
6256 tgl@sss.pgh.pa.us 6042 [ # # ]:UBC 0 : ereport(ERROR,
6043 : : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
6044 : : errmsg("dimension array or low bound array cannot be null")));
6045 : :
6261 bruce@momjian.us 6046 :CBC 45 : dims = PG_GETARG_ARRAYTYPE_P(1);
6047 : :
6048 [ + + ]: 45 : if (!PG_ARGISNULL(0))
6049 : : {
6050 : 39 : value = PG_GETARG_DATUM(0);
6051 : 39 : isnull = false;
6052 : : }
6053 : : else
6054 : : {
6055 : 6 : value = 0;
6056 : 6 : isnull = true;
6057 : : }
6058 : :
6256 tgl@sss.pgh.pa.us 6059 : 45 : elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
6060 [ - + ]: 45 : if (!OidIsValid(elmtype))
6256 tgl@sss.pgh.pa.us 6061 [ # # ]:UBC 0 : elog(ERROR, "could not determine data type of input");
6062 : :
6256 tgl@sss.pgh.pa.us 6063 :CBC 45 : result = array_fill_internal(dims, NULL, value, isnull, elmtype, fcinfo);
6261 bruce@momjian.us 6064 : 39 : PG_RETURN_ARRAYTYPE_P(result);
6065 : : }
6066 : :
6067 : : static ArrayType *
6068 : 33 : create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
6069 : : Oid elmtype, int dataoffset)
6070 : : {
6071 : : ArrayType *result;
6072 : :
6073 : 33 : result = (ArrayType *) palloc0(nbytes);
6074 : 33 : SET_VARSIZE(result, nbytes);
6075 : 33 : result->ndim = ndims;
6076 : 33 : result->dataoffset = dataoffset;
6077 : 33 : result->elemtype = elmtype;
6078 : 33 : memcpy(ARR_DIMS(result), dimv, ndims * sizeof(int));
6079 : 33 : memcpy(ARR_LBOUND(result), lbsv, ndims * sizeof(int));
6080 : :
6081 : 33 : return result;
6082 : : }
6083 : :
6084 : : static ArrayType *
6256 tgl@sss.pgh.pa.us 6085 : 72 : array_fill_internal(ArrayType *dims, ArrayType *lbs,
6086 : : Datum value, bool isnull, Oid elmtype,
6087 : : FunctionCallInfo fcinfo)
6088 : : {
6089 : : ArrayType *result;
6090 : : int *dimv;
6091 : : int *lbsv;
6092 : : int ndims;
6093 : : int nitems;
6094 : : int deflbs[MAXDIM];
6095 : : int16 elmlen;
6096 : : bool elmbyval;
6097 : : char elmalign;
6098 : : ArrayMetaState *my_extra;
6099 : :
6100 : : /*
6101 : : * Params checks
6102 : : */
3166 6103 [ + + ]: 72 : if (ARR_NDIM(dims) > 1)
6261 bruce@momjian.us 6104 [ + - ]: 3 : ereport(ERROR,
6105 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
6106 : : errmsg("wrong number of array subscripts"),
6107 : : errdetail("Dimension array must be one dimensional.")));
6108 : :
5354 tgl@sss.pgh.pa.us 6109 [ + + ]: 69 : if (array_contains_nulls(dims))
6256 6110 [ + - ]: 3 : ereport(ERROR,
6111 : : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
6112 : : errmsg("dimension values cannot be null")));
6113 : :
6261 bruce@momjian.us 6114 [ - + ]: 66 : dimv = (int *) ARR_DATA_PTR(dims);
3166 tgl@sss.pgh.pa.us 6115 [ + + ]: 66 : ndims = (ARR_NDIM(dims) > 0) ? ARR_DIMS(dims)[0] : 0;
6116 : :
6261 bruce@momjian.us 6117 [ - + ]: 66 : if (ndims < 0) /* we do allow zero-dimension arrays */
6261 bruce@momjian.us 6118 [ # # ]:UBC 0 : ereport(ERROR,
6119 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
6120 : : errmsg("invalid number of dimensions: %d", ndims)));
6261 bruce@momjian.us 6121 [ - + ]:CBC 66 : if (ndims > MAXDIM)
6261 bruce@momjian.us 6122 [ # # ]:UBC 0 : ereport(ERROR,
6123 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
6124 : : errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
6125 : : ndims, MAXDIM)));
6126 : :
6261 bruce@momjian.us 6127 [ + + ]:CBC 66 : if (lbs != NULL)
6128 : : {
3166 tgl@sss.pgh.pa.us 6129 [ - + ]: 27 : if (ARR_NDIM(lbs) > 1)
6261 bruce@momjian.us 6130 [ # # ]:UBC 0 : ereport(ERROR,
6131 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
6132 : : errmsg("wrong number of array subscripts"),
6133 : : errdetail("Dimension array must be one dimensional.")));
6134 : :
5354 tgl@sss.pgh.pa.us 6135 [ - + ]:CBC 27 : if (array_contains_nulls(lbs))
6256 tgl@sss.pgh.pa.us 6136 [ # # ]:UBC 0 : ereport(ERROR,
6137 : : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
6138 : : errmsg("dimension values cannot be null")));
6139 : :
3166 tgl@sss.pgh.pa.us 6140 [ + + + + ]:CBC 27 : if (ndims != ((ARR_NDIM(lbs) > 0) ? ARR_DIMS(lbs)[0] : 0))
6261 bruce@momjian.us 6141 [ + - ]: 6 : ereport(ERROR,
6142 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
6143 : : errmsg("wrong number of array subscripts"),
6144 : : errdetail("Low bound array has different size than dimensions array.")));
6145 : :
6146 [ - + ]: 21 : lbsv = (int *) ARR_DATA_PTR(lbs);
6147 : : }
6148 : : else
6149 : : {
6150 : : int i;
6151 : :
6152 [ + + ]: 273 : for (i = 0; i < MAXDIM; i++)
6153 : 234 : deflbs[i] = 1;
6154 : :
6155 : 39 : lbsv = deflbs;
6156 : : }
6157 : :
6158 : : /* This checks for overflow of the array dimensions */
3166 tgl@sss.pgh.pa.us 6159 : 60 : nitems = ArrayGetNItems(ndims, dimv);
1580 6160 : 60 : ArrayCheckBounds(ndims, dimv, lbsv);
6161 : :
6162 : : /* fast track for empty array */
3166 6163 [ + + ]: 60 : if (nitems <= 0)
6261 bruce@momjian.us 6164 : 27 : return construct_empty_array(elmtype);
6165 : :
6166 : : /*
6167 : : * We arrange to look up info about element type only once per series of
6168 : : * calls, assuming the element type doesn't change underneath us.
6169 : : */
6170 : 33 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
6171 [ + - ]: 33 : if (my_extra == NULL)
6172 : : {
6173 : 33 : fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
6174 : : sizeof(ArrayMetaState));
6175 : 33 : my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
6176 : 33 : my_extra->element_type = InvalidOid;
6177 : : }
6178 : :
6179 [ + - ]: 33 : if (my_extra->element_type != elmtype)
6180 : : {
6181 : : /* Get info about element type */
6182 : 33 : get_typlenbyvalalign(elmtype,
6183 : : &my_extra->typlen,
6184 : : &my_extra->typbyval,
6185 : : &my_extra->typalign);
6186 : 33 : my_extra->element_type = elmtype;
6187 : : }
6188 : :
6189 : 33 : elmlen = my_extra->typlen;
6190 : 33 : elmbyval = my_extra->typbyval;
6191 : 33 : elmalign = my_extra->typalign;
6192 : :
6193 : : /* compute required space */
6194 [ + + ]: 33 : if (!isnull)
6195 : : {
6196 : : int i;
6197 : : char *p;
6198 : : int nbytes;
6199 : : int totbytes;
6200 : :
6201 : : /* make sure data is not toasted */
6202 [ + + ]: 21 : if (elmlen == -1)
6203 : 6 : value = PointerGetDatum(PG_DETOAST_DATUM(value));
6204 : :
6205 [ + + + - : 21 : nbytes = att_addlength_datum(0, elmlen, value);
- - - - -
- - - - +
- - ]
6206 [ + + + - : 21 : nbytes = att_align_nominal(nbytes, elmalign);
+ - - - ]
6256 tgl@sss.pgh.pa.us 6207 [ - + ]: 21 : Assert(nbytes > 0);
6208 : :
6209 : 21 : totbytes = nbytes * nitems;
6210 : :
6211 : : /* check for overflow of multiplication or total request */
6212 [ + - ]: 21 : if (totbytes / nbytes != nitems ||
6213 [ - + ]: 21 : !AllocSizeIsValid(totbytes))
6261 bruce@momjian.us 6214 [ # # ]:UBC 0 : ereport(ERROR,
6215 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
6216 : : errmsg("array size exceeds the maximum allowed (%d)",
6217 : : (int) MaxAllocSize)));
6218 : :
6219 : : /*
6220 : : * This addition can't overflow, but it might cause us to go past
6221 : : * MaxAllocSize. We leave it to palloc to complain in that case.
6222 : : */
6256 tgl@sss.pgh.pa.us 6223 :CBC 21 : totbytes += ARR_OVERHEAD_NONULLS(ndims);
6224 : :
6225 : 21 : result = create_array_envelope(ndims, dimv, lbsv, totbytes,
6226 : : elmtype, 0);
6227 : :
6261 bruce@momjian.us 6228 [ - + ]: 21 : p = ARR_DATA_PTR(result);
6229 [ + + ]: 1500153 : for (i = 0; i < nitems; i++)
6230 : 1500132 : p += ArrayCastAndSet(value, elmlen, elmbyval, elmalign, p);
6231 : : }
6232 : : else
6233 : : {
6234 : : int nbytes;
6235 : : int dataoffset;
6236 : :
6237 : 12 : dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
6238 : 12 : nbytes = dataoffset;
6239 : :
6240 : 12 : result = create_array_envelope(ndims, dimv, lbsv, nbytes,
6241 : : elmtype, dataoffset);
6242 : :
6243 : : /* create_array_envelope already zeroed the bitmap, so we're done */
6244 : : }
6245 : :
6246 : 33 : return result;
6247 : : }
6248 : :
6249 : :
6250 : : /*
6251 : : * UNNEST
6252 : : */
6253 : : Datum
6140 tgl@sss.pgh.pa.us 6254 : 271458 : array_unnest(PG_FUNCTION_ARGS)
6255 : : {
6256 : : typedef struct
6257 : : {
6258 : : array_iter iter;
6259 : : int nextelem;
6260 : : int numelems;
6261 : : int16 elmlen;
6262 : : bool elmbyval;
6263 : : char elmalign;
6264 : : } array_unnest_fctx;
6265 : :
6266 : : FuncCallContext *funcctx;
6267 : : array_unnest_fctx *fctx;
6268 : : MemoryContext oldcontext;
6269 : :
6270 : : /* stuff done only on the first call of the function */
6271 [ + + ]: 271458 : if (SRF_IS_FIRSTCALL())
6272 : : {
6273 : : AnyArrayType *arr;
6274 : :
6275 : : /* create a function context for cross-call persistence */
6276 : 43546 : funcctx = SRF_FIRSTCALL_INIT();
6277 : :
6278 : : /*
6279 : : * switch to memory context appropriate for multiple function calls
6280 : : */
6281 : 43546 : oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
6282 : :
6283 : : /*
6284 : : * Get the array value and detoast if needed. We can't do this
6285 : : * earlier because if we have to detoast, we want the detoasted copy
6286 : : * to be in multi_call_memory_ctx, so it will go away when we're done
6287 : : * and not before. (If no detoast happens, we assume the originally
6288 : : * passed array will stick around till then.)
6289 : : */
2910 6290 : 43546 : arr = PG_GETARG_ANY_ARRAY_P(0);
6291 : :
6292 : : /* allocate memory for user context */
6140 6293 : 43546 : fctx = (array_unnest_fctx *) palloc(sizeof(array_unnest_fctx));
6294 : :
6295 : : /* initialize state */
3768 6296 : 43546 : array_iter_setup(&fctx->iter, arr);
6140 6297 : 43546 : fctx->nextelem = 0;
3768 6298 [ + + + + ]: 43546 : fctx->numelems = ArrayGetNItems(AARR_NDIM(arr), AARR_DIMS(arr));
6299 : :
6300 [ + + ]: 43546 : if (VARATT_IS_EXPANDED_HEADER(arr))
6301 : : {
6302 : : /* we can just grab the type data from expanded array */
6303 : 1 : fctx->elmlen = arr->xpn.typlen;
6304 : 1 : fctx->elmbyval = arr->xpn.typbyval;
6305 : 1 : fctx->elmalign = arr->xpn.typalign;
6306 : : }
6307 : : else
6308 [ - + ]: 43545 : get_typlenbyvalalign(AARR_ELEMTYPE(arr),
6309 : : &fctx->elmlen,
6310 : : &fctx->elmbyval,
6311 : : &fctx->elmalign);
6312 : :
6140 6313 : 43546 : funcctx->user_fctx = fctx;
6314 : 43546 : MemoryContextSwitchTo(oldcontext);
6315 : : }
6316 : :
6317 : : /* stuff done on every call of the function */
6318 : 271458 : funcctx = SRF_PERCALL_SETUP();
6319 : 271458 : fctx = funcctx->user_fctx;
6320 : :
6321 [ + + ]: 271458 : if (fctx->nextelem < fctx->numelems)
6322 : : {
5931 bruce@momjian.us 6323 : 227912 : int offset = fctx->nextelem++;
6324 : : Datum elem;
6325 : :
3768 tgl@sss.pgh.pa.us 6326 : 227912 : elem = array_iter_next(&fctx->iter, &fcinfo->isnull, offset,
6327 : 227912 : fctx->elmlen, fctx->elmbyval, fctx->elmalign);
6328 : :
6140 6329 : 227912 : SRF_RETURN_NEXT(funcctx, elem);
6330 : : }
6331 : : else
6332 : : {
6333 : : /* do when there is no more left */
6334 : 43546 : SRF_RETURN_DONE(funcctx);
6335 : : }
6336 : : }
6337 : :
6338 : : /*
6339 : : * Planner support function for array_unnest(anyarray)
6340 : : *
6341 : : * Note: this is now also used for information_schema._pg_expandarray(),
6342 : : * which is simply a wrapper around array_unnest().
6343 : : */
6344 : : Datum
2401 6345 : 9609 : array_unnest_support(PG_FUNCTION_ARGS)
6346 : : {
6347 : 9609 : Node *rawreq = (Node *) PG_GETARG_POINTER(0);
6348 : 9609 : Node *ret = NULL;
6349 : :
6350 [ + + ]: 9609 : if (IsA(rawreq, SupportRequestRows))
6351 : : {
6352 : : /* Try to estimate the number of rows returned */
6353 : 3181 : SupportRequestRows *req = (SupportRequestRows *) rawreq;
6354 : :
6355 [ + + ]: 3181 : if (is_funcclause(req->node)) /* be paranoid */
6356 : : {
6357 : 3178 : List *args = ((FuncExpr *) req->node)->args;
6358 : : Node *arg1;
6359 : :
6360 : : /* We can use estimated argument values here */
6361 : 3178 : arg1 = estimate_expression_value(req->root, linitial(args));
6362 : :
611 6363 : 3178 : req->rows = estimate_array_length(req->root, arg1);
2401 6364 : 3178 : ret = (Node *) req;
6365 : : }
6366 : : }
6367 : :
6368 : 9609 : PG_RETURN_POINTER(ret);
6369 : : }
6370 : :
6371 : :
6372 : : /*
6373 : : * array_replace/array_remove support
6374 : : *
6375 : : * Find all array entries matching (not distinct from) search/search_isnull,
6376 : : * and delete them if remove is true, else replace them with
6377 : : * replace/replace_isnull. Comparisons are done using the specified
6378 : : * collation. fcinfo is passed only for caching purposes.
6379 : : */
6380 : : static ArrayType *
4805 6381 : 1721 : array_replace_internal(ArrayType *array,
6382 : : Datum search, bool search_isnull,
6383 : : Datum replace, bool replace_isnull,
6384 : : bool remove, Oid collation,
6385 : : FunctionCallInfo fcinfo)
6386 : : {
2415 andres@anarazel.de 6387 : 1721 : LOCAL_FCINFO(locfcinfo, 2);
6388 : : ArrayType *result;
6389 : : Oid element_type;
6390 : : Datum *values;
6391 : : bool *nulls;
6392 : : int *dim;
6393 : : int ndim;
6394 : : int nitems,
6395 : : nresult;
6396 : : int i;
4805 tgl@sss.pgh.pa.us 6397 : 1721 : int32 nbytes = 0;
6398 : : int32 dataoffset;
6399 : : bool hasnulls;
6400 : : int typlen;
6401 : : bool typbyval;
6402 : : char typalign;
6403 : : char *arraydataptr;
6404 : : bits8 *bitmap;
6405 : : int bitmask;
6406 : 1721 : bool changed = false;
6407 : : TypeCacheEntry *typentry;
6408 : :
6409 : 1721 : element_type = ARR_ELEMTYPE(array);
6410 : 1721 : ndim = ARR_NDIM(array);
6411 : 1721 : dim = ARR_DIMS(array);
6412 : 1721 : nitems = ArrayGetNItems(ndim, dim);
6413 : :
6414 : : /* Return input array unmodified if it is empty */
6415 [ - + ]: 1721 : if (nitems <= 0)
4805 tgl@sss.pgh.pa.us 6416 :UBC 0 : return array;
6417 : :
6418 : : /*
6419 : : * We can't remove elements from multi-dimensional arrays, since the
6420 : : * result might not be rectangular.
6421 : : */
4805 tgl@sss.pgh.pa.us 6422 [ + + + + ]:CBC 1721 : if (remove && ndim > 1)
6423 [ + - ]: 3 : ereport(ERROR,
6424 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6425 : : errmsg("removing elements from multidimensional arrays is not supported")));
6426 : :
6427 : : /*
6428 : : * We arrange to look up the equality function only once per series of
6429 : : * calls, assuming the element type doesn't change underneath us.
6430 : : */
6431 : 1718 : typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
6432 [ + + ]: 1718 : if (typentry == NULL ||
6433 [ - + ]: 1312 : typentry->type_id != element_type)
6434 : : {
6435 : 406 : typentry = lookup_type_cache(element_type,
6436 : : TYPECACHE_EQ_OPR_FINFO);
6437 [ - + ]: 406 : if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
4805 tgl@sss.pgh.pa.us 6438 [ # # ]:UBC 0 : ereport(ERROR,
6439 : : (errcode(ERRCODE_UNDEFINED_FUNCTION),
6440 : : errmsg("could not identify an equality operator for type %s",
6441 : : format_type_be(element_type))));
282 peter@eisentraut.org 6442 :CBC 406 : fcinfo->flinfo->fn_extra = typentry;
6443 : : }
4805 tgl@sss.pgh.pa.us 6444 : 1718 : typlen = typentry->typlen;
6445 : 1718 : typbyval = typentry->typbyval;
6446 : 1718 : typalign = typentry->typalign;
6447 : :
6448 : : /*
6449 : : * Detoast values if they are toasted. The replacement value must be
6450 : : * detoasted for insertion into the result array, while detoasting the
6451 : : * search value only once saves cycles.
6452 : : */
6453 [ + + ]: 1718 : if (typlen == -1)
6454 : : {
6455 [ + + ]: 1697 : if (!search_isnull)
6456 : 1694 : search = PointerGetDatum(PG_DETOAST_DATUM(search));
6457 [ + + ]: 1697 : if (!replace_isnull)
6458 : 6 : replace = PointerGetDatum(PG_DETOAST_DATUM(replace));
6459 : : }
6460 : :
6461 : : /* Prepare to apply the comparison operator */
2415 andres@anarazel.de 6462 : 1718 : InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
6463 : : collation, NULL, NULL);
6464 : :
6465 : : /* Allocate temporary arrays for new values */
4805 tgl@sss.pgh.pa.us 6466 : 1718 : values = (Datum *) palloc(nitems * sizeof(Datum));
6467 : 1718 : nulls = (bool *) palloc(nitems * sizeof(bool));
6468 : :
6469 : : /* Loop over source data */
6470 [ + + ]: 1718 : arraydataptr = ARR_DATA_PTR(array);
6471 [ + + ]: 1718 : bitmap = ARR_NULLBITMAP(array);
6472 : 1718 : bitmask = 1;
6473 : 1718 : hasnulls = false;
6474 : 1718 : nresult = 0;
6475 : :
6476 [ + + ]: 3764 : for (i = 0; i < nitems; i++)
6477 : : {
6478 : : Datum elt;
6479 : : bool isNull;
6480 : : bool oprresult;
6481 : 2046 : bool skip = false;
6482 : :
6483 : : /* Get source element, checking for NULL */
6484 [ + + + + ]: 2046 : if (bitmap && (*bitmap & bitmask) == 0)
6485 : : {
6486 : 18 : isNull = true;
6487 : : /* If searching for NULL, we have a match */
6488 [ + - ]: 18 : if (search_isnull)
6489 : : {
6490 [ + + ]: 18 : if (remove)
6491 : : {
6492 : 6 : skip = true;
6493 : 6 : changed = true;
6494 : : }
6495 [ + + ]: 12 : else if (!replace_isnull)
6496 : : {
6497 : 9 : values[nresult] = replace;
6498 : 9 : isNull = false;
6499 : 9 : changed = true;
6500 : : }
6501 : : }
6502 : : }
6503 : : else
6504 : : {
6505 : 2028 : isNull = false;
6506 : 2028 : elt = fetch_att(arraydataptr, typbyval, typlen);
6507 [ + + + - : 2028 : arraydataptr = att_addlength_datum(arraydataptr, typlen, elt);
- - - - -
- - - - +
- - ]
6508 [ + - - - : 2028 : arraydataptr = (char *) att_align_nominal(arraydataptr, typalign);
- - - - ]
6509 : :
6510 [ + + ]: 2028 : if (search_isnull)
6511 : : {
6512 : : /* no match possible, keep element */
6513 : 27 : values[nresult] = elt;
6514 : : }
6515 : : else
6516 : : {
6517 : : /*
6518 : : * Apply the operator to the element pair; treat NULL as false
6519 : : */
2415 andres@anarazel.de 6520 : 2001 : locfcinfo->args[0].value = elt;
6521 : 2001 : locfcinfo->args[0].isnull = false;
6522 : 2001 : locfcinfo->args[1].value = search;
6523 : 2001 : locfcinfo->args[1].isnull = false;
6524 : 2001 : locfcinfo->isnull = false;
6525 : 2001 : oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
1964 tgl@sss.pgh.pa.us 6526 [ + - + + ]: 2001 : if (locfcinfo->isnull || !oprresult)
6527 : : {
6528 : : /* no match, keep element */
4805 6529 : 1916 : values[nresult] = elt;
6530 : : }
6531 : : else
6532 : : {
6533 : : /* match, so replace or delete */
6534 : 85 : changed = true;
6535 [ + + ]: 85 : if (remove)
6536 : 73 : skip = true;
6537 : : else
6538 : : {
6539 : 12 : values[nresult] = replace;
6540 : 12 : isNull = replace_isnull;
6541 : : }
6542 : : }
6543 : : }
6544 : : }
6545 : :
6546 [ + + ]: 2046 : if (!skip)
6547 : : {
6548 : 1967 : nulls[nresult] = isNull;
6549 [ + + ]: 1967 : if (isNull)
6550 : 6 : hasnulls = true;
6551 : : else
6552 : : {
6553 : : /* Update total result size */
6554 [ + + + - : 1961 : nbytes = att_addlength_datum(nbytes, typlen, values[nresult]);
- - - - -
- - - - +
- - ]
6555 [ + - - - : 1961 : nbytes = att_align_nominal(nbytes, typalign);
- - - - ]
6556 : : /* check for overflow of total request */
6557 [ - + ]: 1961 : if (!AllocSizeIsValid(nbytes))
4805 tgl@sss.pgh.pa.us 6558 [ # # ]:UBC 0 : ereport(ERROR,
6559 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
6560 : : errmsg("array size exceeds the maximum allowed (%d)",
6561 : : (int) MaxAllocSize)));
6562 : : }
4805 tgl@sss.pgh.pa.us 6563 :CBC 1967 : nresult++;
6564 : : }
6565 : :
6566 : : /* advance bitmap pointer if any */
6567 [ + + ]: 2046 : if (bitmap)
6568 : : {
6569 : 45 : bitmask <<= 1;
6570 [ - + ]: 45 : if (bitmask == 0x100)
6571 : : {
4805 tgl@sss.pgh.pa.us 6572 :UBC 0 : bitmap++;
6573 : 0 : bitmask = 1;
6574 : : }
6575 : : }
6576 : : }
6577 : :
6578 : : /*
6579 : : * If not changed just return the original array
6580 : : */
4805 tgl@sss.pgh.pa.us 6581 [ + + ]:CBC 1718 : if (!changed)
6582 : : {
6583 : 1636 : pfree(values);
6584 : 1636 : pfree(nulls);
6585 : 1636 : return array;
6586 : : }
6587 : :
6588 : : /* If all elements were removed return an empty array */
4481 noah@leadboat.com 6589 [ + + ]: 82 : if (nresult == 0)
6590 : : {
6591 : 3 : pfree(values);
6592 : 3 : pfree(nulls);
6593 : 3 : return construct_empty_array(element_type);
6594 : : }
6595 : :
6596 : : /* Allocate and initialize the result array */
4805 tgl@sss.pgh.pa.us 6597 [ + + ]: 79 : if (hasnulls)
6598 : : {
6599 : 3 : dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nresult);
6600 : 3 : nbytes += dataoffset;
6601 : : }
6602 : : else
6603 : : {
6604 : 76 : dataoffset = 0; /* marker for no null bitmap */
6605 : 76 : nbytes += ARR_OVERHEAD_NONULLS(ndim);
6606 : : }
6607 : 79 : result = (ArrayType *) palloc0(nbytes);
6608 : 79 : SET_VARSIZE(result, nbytes);
6609 : 79 : result->ndim = ndim;
6610 : 79 : result->dataoffset = dataoffset;
6611 : 79 : result->elemtype = element_type;
3768 6612 : 79 : memcpy(ARR_DIMS(result), ARR_DIMS(array), ndim * sizeof(int));
6613 : 79 : memcpy(ARR_LBOUND(result), ARR_LBOUND(array), ndim * sizeof(int));
6614 : :
4805 6615 [ + + ]: 79 : if (remove)
6616 : : {
6617 : : /* Adjust the result length */
6618 : 64 : ARR_DIMS(result)[0] = nresult;
6619 : : }
6620 : :
6621 : : /* Insert data into result array */
6622 : 79 : CopyArrayEls(result,
6623 : : values, nulls, nresult,
6624 : : typlen, typbyval, typalign,
6625 : : false);
6626 : :
6627 : 79 : pfree(values);
6628 : 79 : pfree(nulls);
6629 : :
6630 : 79 : return result;
6631 : : }
6632 : :
6633 : : /*
6634 : : * Remove any occurrences of an element from an array
6635 : : *
6636 : : * If used on a multi-dimensional array this will raise an error.
6637 : : */
6638 : : Datum
6639 : 98345 : array_remove(PG_FUNCTION_ARGS)
6640 : : {
6641 : : ArrayType *array;
6642 : 98345 : Datum search = PG_GETARG_DATUM(1);
6643 : 98345 : bool search_isnull = PG_ARGISNULL(1);
6644 : :
6645 [ + + ]: 98345 : if (PG_ARGISNULL(0))
6646 : 96642 : PG_RETURN_NULL();
6647 : 1703 : array = PG_GETARG_ARRAYTYPE_P(0);
6648 : :
6649 : 1703 : array = array_replace_internal(array,
6650 : : search, search_isnull,
6651 : : (Datum) 0, true,
6652 : : true, PG_GET_COLLATION(),
6653 : : fcinfo);
6654 : 1700 : PG_RETURN_ARRAYTYPE_P(array);
6655 : : }
6656 : :
6657 : : /*
6658 : : * Replace any occurrences of an element in an array
6659 : : */
6660 : : Datum
6661 : 18 : array_replace(PG_FUNCTION_ARGS)
6662 : : {
6663 : : ArrayType *array;
6664 : 18 : Datum search = PG_GETARG_DATUM(1);
6665 : 18 : bool search_isnull = PG_ARGISNULL(1);
6666 : 18 : Datum replace = PG_GETARG_DATUM(2);
6667 : 18 : bool replace_isnull = PG_ARGISNULL(2);
6668 : :
6669 [ - + ]: 18 : if (PG_ARGISNULL(0))
4805 tgl@sss.pgh.pa.us 6670 :UBC 0 : PG_RETURN_NULL();
4805 tgl@sss.pgh.pa.us 6671 :CBC 18 : array = PG_GETARG_ARRAYTYPE_P(0);
6672 : :
6673 : 18 : array = array_replace_internal(array,
6674 : : search, search_isnull,
6675 : : replace, replace_isnull,
6676 : : false, PG_GET_COLLATION(),
6677 : : fcinfo);
6678 : 18 : PG_RETURN_ARRAYTYPE_P(array);
6679 : : }
6680 : :
6681 : : /*
6682 : : * Implements width_bucket(anyelement, anyarray).
6683 : : *
6684 : : * 'thresholds' is an array containing lower bound values for each bucket;
6685 : : * these must be sorted from smallest to largest, or bogus results will be
6686 : : * produced. If N thresholds are supplied, the output is from 0 to N:
6687 : : * 0 is for inputs < first threshold, N is for inputs >= last threshold.
6688 : : */
6689 : : Datum
4015 6690 : 405 : width_bucket_array(PG_FUNCTION_ARGS)
6691 : : {
6692 : 405 : Datum operand = PG_GETARG_DATUM(0);
6693 : 405 : ArrayType *thresholds = PG_GETARG_ARRAYTYPE_P(1);
6694 : 405 : Oid collation = PG_GET_COLLATION();
6695 : 405 : Oid element_type = ARR_ELEMTYPE(thresholds);
6696 : : int result;
6697 : :
6698 : : /* Check input */
6699 [ + + ]: 405 : if (ARR_NDIM(thresholds) > 1)
6700 [ + - ]: 3 : ereport(ERROR,
6701 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
6702 : : errmsg("thresholds must be one-dimensional array")));
6703 : :
6704 [ + + ]: 402 : if (array_contains_nulls(thresholds))
6705 [ + - ]: 3 : ereport(ERROR,
6706 : : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
6707 : : errmsg("thresholds array must not contain NULLs")));
6708 : :
6709 : : /* We have a dedicated implementation for float8 data */
6710 [ + + ]: 399 : if (element_type == FLOAT8OID)
6711 : 183 : result = width_bucket_array_float8(operand, thresholds);
6712 : : else
6713 : : {
6714 : : TypeCacheEntry *typentry;
6715 : :
6716 : : /* Cache information about the input type */
6717 : 216 : typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
6718 [ + + ]: 216 : if (typentry == NULL ||
6719 [ - + ]: 195 : typentry->type_id != element_type)
6720 : : {
6721 : 21 : typentry = lookup_type_cache(element_type,
6722 : : TYPECACHE_CMP_PROC_FINFO);
6723 [ - + ]: 21 : if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
4015 tgl@sss.pgh.pa.us 6724 [ # # ]:UBC 0 : ereport(ERROR,
6725 : : (errcode(ERRCODE_UNDEFINED_FUNCTION),
6726 : : errmsg("could not identify a comparison function for type %s",
6727 : : format_type_be(element_type))));
282 peter@eisentraut.org 6728 :CBC 21 : fcinfo->flinfo->fn_extra = typentry;
6729 : : }
6730 : :
6731 : : /*
6732 : : * We have separate implementation paths for fixed- and variable-width
6733 : : * types, since indexing the array is a lot cheaper in the first case.
6734 : : */
4015 tgl@sss.pgh.pa.us 6735 [ + + ]: 216 : if (typentry->typlen > 0)
6736 : 45 : result = width_bucket_array_fixed(operand, thresholds,
6737 : : collation, typentry);
6738 : : else
6739 : 171 : result = width_bucket_array_variable(operand, thresholds,
6740 : : collation, typentry);
6741 : : }
6742 : :
6743 : : /* Avoid leaking memory when handed toasted input. */
6744 [ - + ]: 399 : PG_FREE_IF_COPY(thresholds, 1);
6745 : :
6746 : 399 : PG_RETURN_INT32(result);
6747 : : }
6748 : :
6749 : : /*
6750 : : * width_bucket_array for float8 data.
6751 : : */
6752 : : static int
6753 : 183 : width_bucket_array_float8(Datum operand, ArrayType *thresholds)
6754 : : {
6755 : 183 : float8 op = DatumGetFloat8(operand);
6756 : : float8 *thresholds_data;
6757 : : int left;
6758 : : int right;
6759 : :
6760 : : /*
6761 : : * Since we know the array contains no NULLs, we can just index it
6762 : : * directly.
6763 : : */
6764 [ - + ]: 183 : thresholds_data = (float8 *) ARR_DATA_PTR(thresholds);
6765 : :
6766 : 183 : left = 0;
6767 : 183 : right = ArrayGetNItems(ARR_NDIM(thresholds), ARR_DIMS(thresholds));
6768 : :
6769 : : /*
6770 : : * If the probe value is a NaN, it's greater than or equal to all possible
6771 : : * threshold values (including other NaNs), so we need not search. Note
6772 : : * that this would give the same result as searching even if the array
6773 : : * contains multiple NaNs (as long as they're correctly sorted), since the
6774 : : * loop logic will find the rightmost of multiple equal threshold values.
6775 : : */
6776 [ + + ]: 183 : if (isnan(op))
6777 : 3 : return right;
6778 : :
6779 : : /* Find the bucket */
6780 [ + + ]: 567 : while (left < right)
6781 : : {
6782 : 387 : int mid = (left + right) / 2;
6783 : :
6784 [ + + + + ]: 387 : if (isnan(thresholds_data[mid]) || op < thresholds_data[mid])
6785 : 168 : right = mid;
6786 : : else
6787 : 219 : left = mid + 1;
6788 : : }
6789 : :
6790 : 180 : return left;
6791 : : }
6792 : :
6793 : : /*
6794 : : * width_bucket_array for generic fixed-width data types.
6795 : : */
6796 : : static int
6797 : 45 : width_bucket_array_fixed(Datum operand,
6798 : : ArrayType *thresholds,
6799 : : Oid collation,
6800 : : TypeCacheEntry *typentry)
6801 : : {
2415 andres@anarazel.de 6802 : 45 : LOCAL_FCINFO(locfcinfo, 2);
6803 : : char *thresholds_data;
4015 tgl@sss.pgh.pa.us 6804 : 45 : int typlen = typentry->typlen;
6805 : 45 : bool typbyval = typentry->typbyval;
6806 : : int left;
6807 : : int right;
6808 : :
6809 : : /*
6810 : : * Since we know the array contains no NULLs, we can just index it
6811 : : * directly.
6812 : : */
6813 [ - + ]: 45 : thresholds_data = (char *) ARR_DATA_PTR(thresholds);
6814 : :
2415 andres@anarazel.de 6815 : 45 : InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
6816 : : collation, NULL, NULL);
6817 : :
6818 : : /* Find the bucket */
4015 tgl@sss.pgh.pa.us 6819 : 45 : left = 0;
6820 : 45 : right = ArrayGetNItems(ARR_NDIM(thresholds), ARR_DIMS(thresholds));
6821 [ + + ]: 135 : while (left < right)
6822 : : {
6823 : 90 : int mid = (left + right) / 2;
6824 : : char *ptr;
6825 : : int32 cmpresult;
6826 : :
6827 : 90 : ptr = thresholds_data + mid * typlen;
6828 : :
2415 andres@anarazel.de 6829 : 90 : locfcinfo->args[0].value = operand;
6830 : 90 : locfcinfo->args[0].isnull = false;
6831 : 90 : locfcinfo->args[1].value = fetch_att(ptr, typbyval, typlen);
6832 : 90 : locfcinfo->args[1].isnull = false;
6833 : :
6834 : 90 : cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
6835 : :
6836 : : /* We don't expect comparison support functions to return null */
1964 tgl@sss.pgh.pa.us 6837 [ - + ]: 90 : Assert(!locfcinfo->isnull);
6838 : :
4015 6839 [ + + ]: 90 : if (cmpresult < 0)
6840 : 45 : right = mid;
6841 : : else
6842 : 45 : left = mid + 1;
6843 : : }
6844 : :
6845 : 45 : return left;
6846 : : }
6847 : :
6848 : : /*
6849 : : * width_bucket_array for generic variable-width data types.
6850 : : */
6851 : : static int
6852 : 171 : width_bucket_array_variable(Datum operand,
6853 : : ArrayType *thresholds,
6854 : : Oid collation,
6855 : : TypeCacheEntry *typentry)
6856 : : {
2415 andres@anarazel.de 6857 : 171 : LOCAL_FCINFO(locfcinfo, 2);
6858 : : char *thresholds_data;
4015 tgl@sss.pgh.pa.us 6859 : 171 : int typlen = typentry->typlen;
6860 : 171 : bool typbyval = typentry->typbyval;
6861 : 171 : char typalign = typentry->typalign;
6862 : : int left;
6863 : : int right;
6864 : :
6865 [ - + ]: 171 : thresholds_data = (char *) ARR_DATA_PTR(thresholds);
6866 : :
2415 andres@anarazel.de 6867 : 171 : InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
6868 : : collation, NULL, NULL);
6869 : :
6870 : : /* Find the bucket */
4015 tgl@sss.pgh.pa.us 6871 : 171 : left = 0;
6872 : 171 : right = ArrayGetNItems(ARR_NDIM(thresholds), ARR_DIMS(thresholds));
6873 [ + + ]: 534 : while (left < right)
6874 : : {
6875 : 363 : int mid = (left + right) / 2;
6876 : : char *ptr;
6877 : : int i;
6878 : : int32 cmpresult;
6879 : :
6880 : : /* Locate mid'th array element by advancing from left element */
6881 : 363 : ptr = thresholds_data;
6882 [ + + ]: 621 : for (i = left; i < mid; i++)
6883 : : {
6884 [ - + + - : 258 : ptr = att_addlength_pointer(ptr, typlen, ptr);
- - - - -
- - - - +
- - ]
6885 [ + - - - : 258 : ptr = (char *) att_align_nominal(ptr, typalign);
- - - - ]
6886 : : }
6887 : :
2415 andres@anarazel.de 6888 : 363 : locfcinfo->args[0].value = operand;
6889 : 363 : locfcinfo->args[0].isnull = false;
6890 : 363 : locfcinfo->args[1].value = fetch_att(ptr, typbyval, typlen);
6891 : 363 : locfcinfo->args[1].isnull = false;
6892 : :
6893 : 363 : cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
6894 : :
6895 : : /* We don't expect comparison support functions to return null */
1964 tgl@sss.pgh.pa.us 6896 [ - + ]: 363 : Assert(!locfcinfo->isnull);
6897 : :
4015 6898 [ + + ]: 363 : if (cmpresult < 0)
6899 : 150 : right = mid;
6900 : : else
6901 : : {
6902 : 213 : left = mid + 1;
6903 : :
6904 : : /*
6905 : : * Move the thresholds pointer to match new "left" index, so we
6906 : : * don't have to seek over those elements again. This trick
6907 : : * ensures we do only O(N) array indexing work, not O(N^2).
6908 : : */
6909 [ - + + - : 213 : ptr = att_addlength_pointer(ptr, typlen, ptr);
- - - - -
- - - - +
- - ]
6910 [ + - - - : 213 : thresholds_data = (char *) att_align_nominal(ptr, typalign);
- - - - ]
6911 : : }
6912 : : }
6913 : :
6914 : 171 : return left;
6915 : : }
6916 : :
6917 : : /*
6918 : : * Trim the last N elements from an array by building an appropriate slice.
6919 : : * Only the first dimension is trimmed.
6920 : : */
6921 : : Datum
1648 6922 : 24 : trim_array(PG_FUNCTION_ARGS)
6923 : : {
6924 : 24 : ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
6925 : 24 : int n = PG_GETARG_INT32(1);
1133 6926 [ + + ]: 24 : int array_length = (ARR_NDIM(v) > 0) ? ARR_DIMS(v)[0] : 0;
6927 : : int16 elmlen;
6928 : : bool elmbyval;
6929 : : char elmalign;
6930 : : int lower[MAXDIM];
6931 : : int upper[MAXDIM];
6932 : : bool lowerProvided[MAXDIM];
6933 : : bool upperProvided[MAXDIM];
6934 : : Datum result;
6935 : :
6936 : : /* Per spec, throw an error if out of bounds */
1648 6937 [ + + + + ]: 24 : if (n < 0 || n > array_length)
6938 [ + - ]: 9 : ereport(ERROR,
6939 : : (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
6940 : : errmsg("number of elements to trim must be between 0 and %d",
6941 : : array_length)));
6942 : :
6943 : : /* Set all the bounds as unprovided except the first upper bound */
6944 : 15 : memset(lowerProvided, false, sizeof(lowerProvided));
6945 : 15 : memset(upperProvided, false, sizeof(upperProvided));
1133 6946 [ + - ]: 15 : if (ARR_NDIM(v) > 0)
6947 : : {
6948 : 15 : upper[0] = ARR_LBOUND(v)[0] + array_length - n - 1;
6949 : 15 : upperProvided[0] = true;
6950 : : }
6951 : :
6952 : : /* Fetch the needed information about the element type */
1648 6953 : 15 : get_typlenbyvalalign(ARR_ELEMTYPE(v), &elmlen, &elmbyval, &elmalign);
6954 : :
6955 : : /* Get the slice */
6956 : 15 : result = array_get_slice(PointerGetDatum(v), 1,
6957 : : upper, lower, upperProvided, lowerProvided,
6958 : : -1, elmlen, elmbyval, elmalign);
6959 : :
6960 : 15 : PG_RETURN_DATUM(result);
6961 : : }
|