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