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