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