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