1 /*-------------------------------------------------------------------------
2 *
3 * arraysubs.c
4 * Subscripting support functions for arrays.
5 *
6 * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 *
10 * IDENTIFICATION
11 * src/backend/utils/adt/arraysubs.c
12 *
13 *-------------------------------------------------------------------------
14 */
15 #include "postgres.h"
16
17 #include "executor/execExpr.h"
18 #include "nodes/makefuncs.h"
19 #include "nodes/nodeFuncs.h"
20 #include "nodes/subscripting.h"
21 #include "parser/parse_coerce.h"
22 #include "parser/parse_expr.h"
23 #include "utils/array.h"
24 #include "utils/builtins.h"
25 #include "utils/lsyscache.h"
26
27
28 /* SubscriptingRefState.workspace for array subscripting execution */
29 typedef struct ArraySubWorkspace
30 {
31 /* Values determined during expression compilation */
32 Oid refelemtype; /* OID of the array element type */
33 int16 refattrlength; /* typlen of array type */
34 int16 refelemlength; /* typlen of the array element type */
35 bool refelembyval; /* is the element type pass-by-value? */
36 char refelemalign; /* typalign of the element type */
37
38 /*
39 * Subscript values converted to integers. Note that these arrays must be
40 * of length MAXDIM even when dealing with fewer subscripts, because
41 * array_get/set_slice may scribble on the extra entries.
42 */
43 int upperindex[MAXDIM];
44 int lowerindex[MAXDIM];
45 } ArraySubWorkspace;
46
47
48 /*
49 * Finish parse analysis of a SubscriptingRef expression for an array.
50 *
51 * Transform the subscript expressions, coerce them to integers,
52 * and determine the result type of the SubscriptingRef node.
53 */
54 static void
array_subscript_transform(SubscriptingRef * sbsref,List * indirection,ParseState * pstate,bool isSlice,bool isAssignment)55 array_subscript_transform(SubscriptingRef *sbsref,
56 List *indirection,
57 ParseState *pstate,
58 bool isSlice,
59 bool isAssignment)
60 {
61 List *upperIndexpr = NIL;
62 List *lowerIndexpr = NIL;
63 ListCell *idx;
64
65 /*
66 * Transform the subscript expressions, and separate upper and lower
67 * bounds into two lists.
68 *
69 * If we have a container slice expression, we convert any non-slice
70 * indirection items to slices by treating the single subscript as the
71 * upper bound and supplying an assumed lower bound of 1.
72 */
73 foreach(idx, indirection)
74 {
75 A_Indices *ai = lfirst_node(A_Indices, idx);
76 Node *subexpr;
77
78 if (isSlice)
79 {
80 if (ai->lidx)
81 {
82 subexpr = transformExpr(pstate, ai->lidx, pstate->p_expr_kind);
83 /* If it's not int4 already, try to coerce */
84 subexpr = coerce_to_target_type(pstate,
85 subexpr, exprType(subexpr),
86 INT4OID, -1,
87 COERCION_ASSIGNMENT,
88 COERCE_IMPLICIT_CAST,
89 -1);
90 if (subexpr == NULL)
91 ereport(ERROR,
92 (errcode(ERRCODE_DATATYPE_MISMATCH),
93 errmsg("array subscript must have type integer"),
94 parser_errposition(pstate, exprLocation(ai->lidx))));
95 }
96 else if (!ai->is_slice)
97 {
98 /* Make a constant 1 */
99 subexpr = (Node *) makeConst(INT4OID,
100 -1,
101 InvalidOid,
102 sizeof(int32),
103 Int32GetDatum(1),
104 false,
105 true); /* pass by value */
106 }
107 else
108 {
109 /* Slice with omitted lower bound, put NULL into the list */
110 subexpr = NULL;
111 }
112 lowerIndexpr = lappend(lowerIndexpr, subexpr);
113 }
114 else
115 Assert(ai->lidx == NULL && !ai->is_slice);
116
117 if (ai->uidx)
118 {
119 subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind);
120 /* If it's not int4 already, try to coerce */
121 subexpr = coerce_to_target_type(pstate,
122 subexpr, exprType(subexpr),
123 INT4OID, -1,
124 COERCION_ASSIGNMENT,
125 COERCE_IMPLICIT_CAST,
126 -1);
127 if (subexpr == NULL)
128 ereport(ERROR,
129 (errcode(ERRCODE_DATATYPE_MISMATCH),
130 errmsg("array subscript must have type integer"),
131 parser_errposition(pstate, exprLocation(ai->uidx))));
132 }
133 else
134 {
135 /* Slice with omitted upper bound, put NULL into the list */
136 Assert(isSlice && ai->is_slice);
137 subexpr = NULL;
138 }
139 upperIndexpr = lappend(upperIndexpr, subexpr);
140 }
141
142 /* ... and store the transformed lists into the SubscriptRef node */
143 sbsref->refupperindexpr = upperIndexpr;
144 sbsref->reflowerindexpr = lowerIndexpr;
145
146 /* Verify subscript list lengths are within implementation limit */
147 if (list_length(upperIndexpr) > MAXDIM)
148 ereport(ERROR,
149 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
150 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
151 list_length(upperIndexpr), MAXDIM)));
152 /* We need not check lowerIndexpr separately */
153
154 /*
155 * Determine the result type of the subscripting operation. It's the same
156 * as the array type if we're slicing, else it's the element type. In
157 * either case, the typmod is the same as the array's, so we need not
158 * change reftypmod.
159 */
160 if (isSlice)
161 sbsref->refrestype = sbsref->refcontainertype;
162 else
163 sbsref->refrestype = sbsref->refelemtype;
164 }
165
166 /*
167 * During execution, process the subscripts in a SubscriptingRef expression.
168 *
169 * The subscript expressions are already evaluated in Datum form in the
170 * SubscriptingRefState's arrays. Check and convert them as necessary.
171 *
172 * If any subscript is NULL, we throw error in assignment cases, or in fetch
173 * cases set result to NULL and return false (instructing caller to skip the
174 * rest of the SubscriptingRef sequence).
175 *
176 * We convert all the subscripts to plain integers and save them in the
177 * sbsrefstate->workspace arrays.
178 */
179 static bool
array_subscript_check_subscripts(ExprState * state,ExprEvalStep * op,ExprContext * econtext)180 array_subscript_check_subscripts(ExprState *state,
181 ExprEvalStep *op,
182 ExprContext *econtext)
183 {
184 SubscriptingRefState *sbsrefstate = op->d.sbsref_subscript.state;
185 ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace;
186
187 /* Process upper subscripts */
188 for (int i = 0; i < sbsrefstate->numupper; i++)
189 {
190 if (sbsrefstate->upperprovided[i])
191 {
192 /* If any index expr yields NULL, result is NULL or error */
193 if (sbsrefstate->upperindexnull[i])
194 {
195 if (sbsrefstate->isassignment)
196 ereport(ERROR,
197 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
198 errmsg("array subscript in assignment must not be null")));
199 *op->resnull = true;
200 return false;
201 }
202 workspace->upperindex[i] = DatumGetInt32(sbsrefstate->upperindex[i]);
203 }
204 }
205
206 /* Likewise for lower subscripts */
207 for (int i = 0; i < sbsrefstate->numlower; i++)
208 {
209 if (sbsrefstate->lowerprovided[i])
210 {
211 /* If any index expr yields NULL, result is NULL or error */
212 if (sbsrefstate->lowerindexnull[i])
213 {
214 if (sbsrefstate->isassignment)
215 ereport(ERROR,
216 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
217 errmsg("array subscript in assignment must not be null")));
218 *op->resnull = true;
219 return false;
220 }
221 workspace->lowerindex[i] = DatumGetInt32(sbsrefstate->lowerindex[i]);
222 }
223 }
224
225 return true;
226 }
227
228 /*
229 * Evaluate SubscriptingRef fetch for an array element.
230 *
231 * Source container is in step's result variable (it's known not NULL, since
232 * we set fetch_strict to true), and indexes have already been evaluated into
233 * workspace array.
234 */
235 static void
array_subscript_fetch(ExprState * state,ExprEvalStep * op,ExprContext * econtext)236 array_subscript_fetch(ExprState *state,
237 ExprEvalStep *op,
238 ExprContext *econtext)
239 {
240 SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
241 ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace;
242
243 /* Should not get here if source array (or any subscript) is null */
244 Assert(!(*op->resnull));
245
246 *op->resvalue = array_get_element(*op->resvalue,
247 sbsrefstate->numupper,
248 workspace->upperindex,
249 workspace->refattrlength,
250 workspace->refelemlength,
251 workspace->refelembyval,
252 workspace->refelemalign,
253 op->resnull);
254 }
255
256 /*
257 * Evaluate SubscriptingRef fetch for an array slice.
258 *
259 * Source container is in step's result variable (it's known not NULL, since
260 * we set fetch_strict to true), and indexes have already been evaluated into
261 * workspace array.
262 */
263 static void
array_subscript_fetch_slice(ExprState * state,ExprEvalStep * op,ExprContext * econtext)264 array_subscript_fetch_slice(ExprState *state,
265 ExprEvalStep *op,
266 ExprContext *econtext)
267 {
268 SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
269 ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace;
270
271 /* Should not get here if source array (or any subscript) is null */
272 Assert(!(*op->resnull));
273
274 *op->resvalue = array_get_slice(*op->resvalue,
275 sbsrefstate->numupper,
276 workspace->upperindex,
277 workspace->lowerindex,
278 sbsrefstate->upperprovided,
279 sbsrefstate->lowerprovided,
280 workspace->refattrlength,
281 workspace->refelemlength,
282 workspace->refelembyval,
283 workspace->refelemalign);
284 /* The slice is never NULL, so no need to change *op->resnull */
285 }
286
287 /*
288 * Evaluate SubscriptingRef assignment for an array element assignment.
289 *
290 * Input container (possibly null) is in result area, replacement value is in
291 * SubscriptingRefState's replacevalue/replacenull.
292 */
293 static void
array_subscript_assign(ExprState * state,ExprEvalStep * op,ExprContext * econtext)294 array_subscript_assign(ExprState *state,
295 ExprEvalStep *op,
296 ExprContext *econtext)
297 {
298 SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
299 ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace;
300 Datum arraySource = *op->resvalue;
301
302 /*
303 * For an assignment to a fixed-length array type, both the original array
304 * and the value to be assigned into it must be non-NULL, else we punt and
305 * return the original array.
306 */
307 if (workspace->refattrlength > 0)
308 {
309 if (*op->resnull || sbsrefstate->replacenull)
310 return;
311 }
312
313 /*
314 * For assignment to varlena arrays, we handle a NULL original array by
315 * substituting an empty (zero-dimensional) array; insertion of the new
316 * element will result in a singleton array value. It does not matter
317 * whether the new element is NULL.
318 */
319 if (*op->resnull)
320 {
321 arraySource = PointerGetDatum(construct_empty_array(workspace->refelemtype));
322 *op->resnull = false;
323 }
324
325 *op->resvalue = array_set_element(arraySource,
326 sbsrefstate->numupper,
327 workspace->upperindex,
328 sbsrefstate->replacevalue,
329 sbsrefstate->replacenull,
330 workspace->refattrlength,
331 workspace->refelemlength,
332 workspace->refelembyval,
333 workspace->refelemalign);
334 /* The result is never NULL, so no need to change *op->resnull */
335 }
336
337 /*
338 * Evaluate SubscriptingRef assignment for an array slice assignment.
339 *
340 * Input container (possibly null) is in result area, replacement value is in
341 * SubscriptingRefState's replacevalue/replacenull.
342 */
343 static void
array_subscript_assign_slice(ExprState * state,ExprEvalStep * op,ExprContext * econtext)344 array_subscript_assign_slice(ExprState *state,
345 ExprEvalStep *op,
346 ExprContext *econtext)
347 {
348 SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
349 ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace;
350 Datum arraySource = *op->resvalue;
351
352 /*
353 * For an assignment to a fixed-length array type, both the original array
354 * and the value to be assigned into it must be non-NULL, else we punt and
355 * return the original array.
356 */
357 if (workspace->refattrlength > 0)
358 {
359 if (*op->resnull || sbsrefstate->replacenull)
360 return;
361 }
362
363 /*
364 * For assignment to varlena arrays, we handle a NULL original array by
365 * substituting an empty (zero-dimensional) array; insertion of the new
366 * element will result in a singleton array value. It does not matter
367 * whether the new element is NULL.
368 */
369 if (*op->resnull)
370 {
371 arraySource = PointerGetDatum(construct_empty_array(workspace->refelemtype));
372 *op->resnull = false;
373 }
374
375 *op->resvalue = array_set_slice(arraySource,
376 sbsrefstate->numupper,
377 workspace->upperindex,
378 workspace->lowerindex,
379 sbsrefstate->upperprovided,
380 sbsrefstate->lowerprovided,
381 sbsrefstate->replacevalue,
382 sbsrefstate->replacenull,
383 workspace->refattrlength,
384 workspace->refelemlength,
385 workspace->refelembyval,
386 workspace->refelemalign);
387 /* The result is never NULL, so no need to change *op->resnull */
388 }
389
390 /*
391 * Compute old array element value for a SubscriptingRef assignment
392 * expression. Will only be called if the new-value subexpression
393 * contains SubscriptingRef or FieldStore. This is the same as the
394 * regular fetch case, except that we have to handle a null array,
395 * and the value should be stored into the SubscriptingRefState's
396 * prevvalue/prevnull fields.
397 */
398 static void
array_subscript_fetch_old(ExprState * state,ExprEvalStep * op,ExprContext * econtext)399 array_subscript_fetch_old(ExprState *state,
400 ExprEvalStep *op,
401 ExprContext *econtext)
402 {
403 SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
404 ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace;
405
406 if (*op->resnull)
407 {
408 /* whole array is null, so any element is too */
409 sbsrefstate->prevvalue = (Datum) 0;
410 sbsrefstate->prevnull = true;
411 }
412 else
413 sbsrefstate->prevvalue = array_get_element(*op->resvalue,
414 sbsrefstate->numupper,
415 workspace->upperindex,
416 workspace->refattrlength,
417 workspace->refelemlength,
418 workspace->refelembyval,
419 workspace->refelemalign,
420 &sbsrefstate->prevnull);
421 }
422
423 /*
424 * Compute old array slice value for a SubscriptingRef assignment
425 * expression. Will only be called if the new-value subexpression
426 * contains SubscriptingRef or FieldStore. This is the same as the
427 * regular fetch case, except that we have to handle a null array,
428 * and the value should be stored into the SubscriptingRefState's
429 * prevvalue/prevnull fields.
430 *
431 * Note: this is presently dead code, because the new value for a
432 * slice would have to be an array, so it couldn't directly contain a
433 * FieldStore; nor could it contain a SubscriptingRef assignment, since
434 * we consider adjacent subscripts to index one multidimensional array
435 * not nested array types. Future generalizations might make this
436 * reachable, however.
437 */
438 static void
array_subscript_fetch_old_slice(ExprState * state,ExprEvalStep * op,ExprContext * econtext)439 array_subscript_fetch_old_slice(ExprState *state,
440 ExprEvalStep *op,
441 ExprContext *econtext)
442 {
443 SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
444 ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace;
445
446 if (*op->resnull)
447 {
448 /* whole array is null, so any slice is too */
449 sbsrefstate->prevvalue = (Datum) 0;
450 sbsrefstate->prevnull = true;
451 }
452 else
453 {
454 sbsrefstate->prevvalue = array_get_slice(*op->resvalue,
455 sbsrefstate->numupper,
456 workspace->upperindex,
457 workspace->lowerindex,
458 sbsrefstate->upperprovided,
459 sbsrefstate->lowerprovided,
460 workspace->refattrlength,
461 workspace->refelemlength,
462 workspace->refelembyval,
463 workspace->refelemalign);
464 /* slices of non-null arrays are never null */
465 sbsrefstate->prevnull = false;
466 }
467 }
468
469 /*
470 * Set up execution state for an array subscript operation.
471 */
472 static void
array_exec_setup(const SubscriptingRef * sbsref,SubscriptingRefState * sbsrefstate,SubscriptExecSteps * methods)473 array_exec_setup(const SubscriptingRef *sbsref,
474 SubscriptingRefState *sbsrefstate,
475 SubscriptExecSteps *methods)
476 {
477 bool is_slice = (sbsrefstate->numlower != 0);
478 ArraySubWorkspace *workspace;
479
480 /*
481 * Enforce the implementation limit on number of array subscripts. This
482 * check isn't entirely redundant with checking at parse time; conceivably
483 * the expression was stored by a backend with a different MAXDIM value.
484 */
485 if (sbsrefstate->numupper > MAXDIM)
486 ereport(ERROR,
487 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
488 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
489 sbsrefstate->numupper, MAXDIM)));
490
491 /* Should be impossible if parser is sane, but check anyway: */
492 if (sbsrefstate->numlower != 0 &&
493 sbsrefstate->numupper != sbsrefstate->numlower)
494 elog(ERROR, "upper and lower index lists are not same length");
495
496 /*
497 * Allocate type-specific workspace.
498 */
499 workspace = (ArraySubWorkspace *) palloc(sizeof(ArraySubWorkspace));
500 sbsrefstate->workspace = workspace;
501
502 /*
503 * Collect datatype details we'll need at execution.
504 */
505 workspace->refelemtype = sbsref->refelemtype;
506 workspace->refattrlength = get_typlen(sbsref->refcontainertype);
507 get_typlenbyvalalign(sbsref->refelemtype,
508 &workspace->refelemlength,
509 &workspace->refelembyval,
510 &workspace->refelemalign);
511
512 /*
513 * Pass back pointers to appropriate step execution functions.
514 */
515 methods->sbs_check_subscripts = array_subscript_check_subscripts;
516 if (is_slice)
517 {
518 methods->sbs_fetch = array_subscript_fetch_slice;
519 methods->sbs_assign = array_subscript_assign_slice;
520 methods->sbs_fetch_old = array_subscript_fetch_old_slice;
521 }
522 else
523 {
524 methods->sbs_fetch = array_subscript_fetch;
525 methods->sbs_assign = array_subscript_assign;
526 methods->sbs_fetch_old = array_subscript_fetch_old;
527 }
528 }
529
530 /*
531 * array_subscript_handler
532 * Subscripting handler for standard varlena arrays.
533 *
534 * This should be used only for "true" array types, which have array headers
535 * as understood by the varlena array routines, and are referenced by the
536 * element type's pg_type.typarray field.
537 */
538 Datum
array_subscript_handler(PG_FUNCTION_ARGS)539 array_subscript_handler(PG_FUNCTION_ARGS)
540 {
541 static const SubscriptRoutines sbsroutines = {
542 .transform = array_subscript_transform,
543 .exec_setup = array_exec_setup,
544 .fetch_strict = true, /* fetch returns NULL for NULL inputs */
545 .fetch_leakproof = true, /* fetch returns NULL for bad subscript */
546 .store_leakproof = false /* ... but assignment throws error */
547 };
548
549 PG_RETURN_POINTER(&sbsroutines);
550 }
551
552 /*
553 * raw_array_subscript_handler
554 * Subscripting handler for "raw" arrays.
555 *
556 * A "raw" array just contains N independent instances of the element type.
557 * Currently we require both the element type and the array type to be fixed
558 * length, but it wouldn't be too hard to relax that for the array type.
559 *
560 * As of now, all the support code is shared with standard varlena arrays.
561 * We may split those into separate code paths, but probably that would yield
562 * only marginal speedups. The main point of having a separate handler is
563 * so that pg_type.typsubscript clearly indicates the type's semantics.
564 */
565 Datum
raw_array_subscript_handler(PG_FUNCTION_ARGS)566 raw_array_subscript_handler(PG_FUNCTION_ARGS)
567 {
568 static const SubscriptRoutines sbsroutines = {
569 .transform = array_subscript_transform,
570 .exec_setup = array_exec_setup,
571 .fetch_strict = true, /* fetch returns NULL for NULL inputs */
572 .fetch_leakproof = true, /* fetch returns NULL for bad subscript */
573 .store_leakproof = false /* ... but assignment throws error */
574 };
575
576 PG_RETURN_POINTER(&sbsroutines);
577 }
578