PostgreSQL Source Code  git master
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-2024, 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/fmgrprotos.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  */
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
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  {
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,
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,
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
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
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
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
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
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
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
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
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  */
516  if (is_slice)
517  {
521  }
522  else
523  {
524  methods->sbs_fetch = array_subscript_fetch;
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
540 {
541  static const SubscriptRoutines sbsroutines = {
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
567 {
568  static const SubscriptRoutines sbsroutines = {
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 }
Datum idx(PG_FUNCTION_ARGS)
Definition: _int_op.c:259
#define MAXDIM
Definition: array.h:75
Datum array_set_element(Datum arraydatum, int nSubscripts, int *indx, Datum dataValue, bool isNull, int arraytyplen, int elmlen, bool elmbyval, char elmalign)
Definition: arrayfuncs.c:2201
ArrayType * construct_empty_array(Oid elmtype)
Definition: arrayfuncs.c:3561
Datum array_get_element(Datum arraydatum, int nSubscripts, int *indx, int arraytyplen, int elmlen, bool elmbyval, char elmalign, bool *isNull)
Definition: arrayfuncs.c:1820
Datum array_get_slice(Datum arraydatum, int nSubscripts, int *upperIndx, int *lowerIndx, bool *upperProvided, bool *lowerProvided, int arraytyplen, int elmlen, bool elmbyval, char elmalign)
Definition: arrayfuncs.c:2030
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)
Definition: arrayfuncs.c:2806
static void array_subscript_assign_slice(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
Definition: arraysubs.c:344
static void array_subscript_fetch_old(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
Definition: arraysubs.c:399
static void array_subscript_assign(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
Definition: arraysubs.c:294
static void array_exec_setup(const SubscriptingRef *sbsref, SubscriptingRefState *sbsrefstate, SubscriptExecSteps *methods)
Definition: arraysubs.c:473
static void array_subscript_fetch(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
Definition: arraysubs.c:236
static void array_subscript_fetch_old_slice(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
Definition: arraysubs.c:439
static void array_subscript_fetch_slice(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
Definition: arraysubs.c:264
Datum array_subscript_handler(PG_FUNCTION_ARGS)
Definition: arraysubs.c:539
static void array_subscript_transform(SubscriptingRef *sbsref, List *indirection, ParseState *pstate, bool isSlice, bool isAssignment)
Definition: arraysubs.c:55
Datum raw_array_subscript_handler(PG_FUNCTION_ARGS)
Definition: arraysubs.c:566
static bool array_subscript_check_subscripts(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
Definition: arraysubs.c:180
struct ArraySubWorkspace ArraySubWorkspace
signed short int16
Definition: c.h:493
signed int int32
Definition: c.h:494
#define Assert(condition)
Definition: c.h:858
int errcode(int sqlerrcode)
Definition: elog.c:859
int errmsg(const char *fmt,...)
Definition: elog.c:1072
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:224
#define ereport(elevel,...)
Definition: elog.h:149
#define PG_RETURN_POINTER(x)
Definition: fmgr.h:361
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
for(;;)
int i
Definition: isn.c:73
if(TABLE==NULL||TABLE_index==NULL)
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:2271
int16 get_typlen(Oid typid)
Definition: lsyscache.c:2197
Const * makeConst(Oid consttype, int32 consttypmod, Oid constcollid, int constlen, Datum constvalue, bool constisnull, bool constbyval)
Definition: makefuncs.c:301
void * palloc(Size size)
Definition: mcxt.c:1316
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
int exprLocation(const Node *expr)
Definition: nodeFuncs.c:1386
Node * coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype, Oid targettype, int32 targettypmod, CoercionContext ccontext, CoercionForm cformat, int location)
Definition: parse_coerce.c:78
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
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:322
uintptr_t Datum
Definition: postgres.h:64
static Datum Int32GetDatum(int32 X)
Definition: postgres.h:212
static int32 DatumGetInt32(Datum X)
Definition: postgres.h:202
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
@ COERCE_IMPLICIT_CAST
Definition: primnodes.h:706
@ COERCION_ASSIGNMENT
Definition: primnodes.h:685
bool is_slice
Definition: parsenodes.h:459
Node * uidx
Definition: parsenodes.h:461
Node * lidx
Definition: parsenodes.h:460
int upperindex[MAXDIM]
Definition: arraysubs.c:43
int lowerindex[MAXDIM]
Definition: arraysubs.c:44
int16 refattrlength
Definition: arraysubs.c:33
int16 refelemlength
Definition: arraysubs.c:34
union ExprEvalStep::@54 d
Datum * resvalue
Definition: execExpr.h:284
struct ExprEvalStep::@54::@82 sbsref
bool * resnull
Definition: execExpr.h:285
struct ExprEvalStep::@54::@81 sbsref_subscript
Definition: pg_list.h:54
Definition: nodes.h:129
ParseExprKind p_expr_kind
Definition: parse_node.h:211
ExecEvalSubroutine sbs_fetch_old
Definition: execExpr.h:758
ExecEvalBoolSubroutine sbs_check_subscripts
Definition: execExpr.h:755
ExecEvalSubroutine sbs_assign
Definition: execExpr.h:757
ExecEvalSubroutine sbs_fetch
Definition: execExpr.h:756
SubscriptTransform transform
Definition: subscripting.h:160
List * refupperindexpr
Definition: primnodes.h:663
List * reflowerindexpr
Definition: primnodes.h:669
Definition: regguts.h:323