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-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 */
31 : : typedef 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 : : */
45 : : int upperindex[MAXDIM];
46 : : int lowerindex[MAXDIM];
47 : : } ArraySubWorkspace;
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 : : */
56 : : static void
1973 tgl@sss.pgh.pa.us 57 :CBC 8763 : array_subscript_transform(SubscriptingRef *sbsref,
58 : : List *indirection,
59 : : ParseState *pstate,
60 : : bool isSlice,
61 : : bool isAssignment)
62 : : {
63 : 8763 : List *upperIndexpr = NIL;
64 : 8763 : List *lowerIndexpr = NIL;
65 : : ListCell *idx;
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 [ + - + + : 17724 : foreach(idx, indirection)
+ + ]
76 : : {
77 : 8961 : A_Indices *ai = lfirst_node(A_Indices, idx);
78 : : Node *subexpr;
79 : :
80 [ + + ]: 8961 : if (isSlice)
81 : : {
82 [ + + ]: 412 : if (ai->lidx)
83 : : {
84 : 324 : subexpr = transformExpr(pstate, ai->lidx, pstate->p_expr_kind);
85 : : /* If it's not int4 already, try to coerce */
86 : 324 : subexpr = coerce_to_target_type(pstate,
87 : : subexpr, exprType(subexpr),
88 : : INT4OID, -1,
89 : : COERCION_ASSIGNMENT,
90 : : COERCE_IMPLICIT_CAST,
91 : : -1);
92 [ - + ]: 324 : if (subexpr == NULL)
1973 tgl@sss.pgh.pa.us 93 [ # # ]:UBC 0 : ereport(ERROR,
94 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
95 : : errmsg("array subscript must have type integer"),
96 : : parser_errposition(pstate, exprLocation(ai->lidx))));
97 : : }
1973 tgl@sss.pgh.pa.us 98 [ + + ]:CBC 88 : else if (!ai->is_slice)
99 : : {
100 : : /* Make a constant 1 */
101 : 36 : subexpr = (Node *) makeConst(INT4OID,
102 : : -1,
103 : : InvalidOid,
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 : 52 : subexpr = NULL;
113 : : }
114 : 412 : lowerIndexpr = lappend(lowerIndexpr, subexpr);
115 : : }
116 : : else
117 [ + - - + ]: 8549 : Assert(ai->lidx == NULL && !ai->is_slice);
118 : :
119 [ + + ]: 8961 : if (ai->uidx)
120 : : {
121 : 8909 : subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind);
122 : : /* If it's not int4 already, try to coerce */
123 : 8909 : subexpr = coerce_to_target_type(pstate,
124 : : subexpr, exprType(subexpr),
125 : : INT4OID, -1,
126 : : COERCION_ASSIGNMENT,
127 : : COERCE_IMPLICIT_CAST,
128 : : -1);
129 [ - + ]: 8909 : if (subexpr == NULL)
1973 tgl@sss.pgh.pa.us 130 [ # # ]:UBC 0 : ereport(ERROR,
131 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
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 */
1973 tgl@sss.pgh.pa.us 138 [ + - - + ]:CBC 52 : Assert(isSlice && ai->is_slice);
139 : 52 : subexpr = NULL;
140 : : }
141 : 8961 : upperIndexpr = lappend(upperIndexpr, subexpr);
142 : : }
143 : :
144 : : /* ... and store the transformed lists into the SubscriptingRef node */
145 : 8763 : sbsref->refupperindexpr = upperIndexpr;
146 : 8763 : sbsref->reflowerindexpr = lowerIndexpr;
147 : :
148 : : /* Verify subscript list lengths are within implementation limit */
149 [ + + ]: 8763 : if (list_length(upperIndexpr) > MAXDIM)
150 [ + - ]: 4 : ereport(ERROR,
151 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
152 : : errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
153 : : list_length(upperIndexpr), MAXDIM)));
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 [ + + ]: 8759 : if (isSlice)
163 : 304 : sbsref->refrestype = sbsref->refcontainertype;
164 : : else
165 : 8455 : sbsref->refrestype = sbsref->refelemtype;
166 : 8759 : }
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 : : */
181 : : static bool
182 : 577819 : array_subscript_check_subscripts(ExprState *state,
183 : : ExprEvalStep *op,
184 : : ExprContext *econtext)
185 : : {
186 : 577819 : SubscriptingRefState *sbsrefstate = op->d.sbsref_subscript.state;
187 : 577819 : ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace;
188 : :
189 : : /* Process upper subscripts */
190 [ + + ]: 1155951 : for (int i = 0; i < sbsrefstate->numupper; i++)
191 : : {
192 [ + + ]: 578150 : if (sbsrefstate->upperprovided[i])
193 : : {
194 : : /* If any index expr yields NULL, result is NULL or error */
195 [ + + ]: 578054 : if (sbsrefstate->upperindexnull[i])
196 : : {
197 [ + + ]: 18 : if (sbsrefstate->isassignment)
198 [ + - ]: 8 : ereport(ERROR,
199 : : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
200 : : errmsg("array subscript in assignment must not be null")));
201 : 10 : *op->resnull = true;
202 : 10 : return false;
203 : : }
204 : 578036 : workspace->upperindex[i] = DatumGetInt32(sbsrefstate->upperindex[i]);
205 : : }
206 : : }
207 : :
208 : : /* Likewise for lower subscripts */
209 [ + + ]: 578425 : for (int i = 0; i < sbsrefstate->numlower; i++)
210 : : {
211 [ + + ]: 633 : if (sbsrefstate->lowerprovided[i])
212 : : {
213 : : /* If any index expr yields NULL, result is NULL or error */
214 [ + + ]: 549 : if (sbsrefstate->lowerindexnull[i])
215 : : {
216 [ + + ]: 9 : if (sbsrefstate->isassignment)
217 [ + - ]: 4 : ereport(ERROR,
218 : : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
219 : : errmsg("array subscript in assignment must not be null")));
220 : 5 : *op->resnull = true;
221 : 5 : return false;
222 : : }
223 : 540 : workspace->lowerindex[i] = DatumGetInt32(sbsrefstate->lowerindex[i]);
224 : : }
225 : : }
226 : :
227 : 577792 : 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 : : */
237 : : static void
238 : 576586 : array_subscript_fetch(ExprState *state,
239 : : ExprEvalStep *op,
240 : : ExprContext *econtext)
241 : : {
242 : 576586 : SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
243 : 576586 : ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace;
244 : :
245 : : /* Should not get here if source array (or any subscript) is null */
246 [ - + ]: 576586 : Assert(!(*op->resnull));
247 : :
248 : 1153172 : *op->resvalue = array_get_element(*op->resvalue,
249 : : sbsrefstate->numupper,
250 : 576586 : workspace->upperindex,
251 : 576586 : workspace->refattrlength,
252 : 576586 : workspace->refelemlength,
253 : 576586 : workspace->refelembyval,
254 : 576586 : workspace->refelemalign,
255 : : op->resnull);
256 : 576586 : }
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 : : */
265 : : static void
266 : 256 : array_subscript_fetch_slice(ExprState *state,
267 : : ExprEvalStep *op,
268 : : ExprContext *econtext)
269 : : {
270 : 256 : SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
271 : 256 : ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace;
272 : :
273 : : /* Should not get here if source array (or any subscript) is null */
274 [ - + ]: 256 : Assert(!(*op->resnull));
275 : :
276 : 496 : *op->resvalue = array_get_slice(*op->resvalue,
277 : : sbsrefstate->numupper,
278 : 256 : workspace->upperindex,
279 : 256 : workspace->lowerindex,
280 : : sbsrefstate->upperprovided,
281 : : sbsrefstate->lowerprovided,
282 : 256 : workspace->refattrlength,
283 : 256 : workspace->refelemlength,
284 : 256 : workspace->refelembyval,
285 : 256 : workspace->refelemalign);
286 : : /* The slice is never NULL, so no need to change *op->resnull */
287 : 240 : }
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 : : */
295 : : static void
296 : 749 : array_subscript_assign(ExprState *state,
297 : : ExprEvalStep *op,
298 : : ExprContext *econtext)
299 : : {
300 : 749 : SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
301 : 749 : ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace;
302 : 749 : Datum arraySource = *op->resvalue;
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 [ + + ]: 749 : if (workspace->refattrlength > 0)
310 : : {
311 [ + + + + ]: 25 : if (*op->resnull || sbsrefstate->replacenull)
312 : 13 : 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 [ + + ]: 736 : if (*op->resnull)
322 : : {
323 : 236 : arraySource = PointerGetDatum(construct_empty_array(workspace->refelemtype));
324 : 236 : *op->resnull = false;
325 : : }
326 : :
327 : 720 : *op->resvalue = array_set_element(arraySource,
328 : : sbsrefstate->numupper,
329 : 736 : workspace->upperindex,
330 : : sbsrefstate->replacevalue,
331 : 736 : sbsrefstate->replacenull,
332 : 736 : workspace->refattrlength,
333 : 736 : workspace->refelemlength,
334 : 736 : workspace->refelembyval,
335 : 736 : 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 : : */
345 : : static void
346 : 180 : array_subscript_assign_slice(ExprState *state,
347 : : ExprEvalStep *op,
348 : : ExprContext *econtext)
349 : : {
350 : 180 : SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
351 : 180 : ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace;
352 : 180 : Datum arraySource = *op->resvalue;
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 [ - + ]: 180 : if (workspace->refattrlength > 0)
360 : : {
1973 tgl@sss.pgh.pa.us 361 [ # # # # ]:UBC 0 : if (*op->resnull || sbsrefstate->replacenull)
362 : 0 : 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 : : */
1973 tgl@sss.pgh.pa.us 371 [ + + ]:CBC 180 : if (*op->resnull)
372 : : {
373 : 43 : arraySource = PointerGetDatum(construct_empty_array(workspace->refelemtype));
374 : 43 : *op->resnull = false;
375 : : }
376 : :
377 : 160 : *op->resvalue = array_set_slice(arraySource,
378 : : sbsrefstate->numupper,
379 : 180 : workspace->upperindex,
380 : 180 : workspace->lowerindex,
381 : : sbsrefstate->upperprovided,
382 : : sbsrefstate->lowerprovided,
383 : : sbsrefstate->replacevalue,
384 : 180 : sbsrefstate->replacenull,
385 : 180 : workspace->refattrlength,
386 : 180 : workspace->refelemlength,
387 : 180 : workspace->refelembyval,
388 : 180 : 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 : : */
400 : : static void
401 : 183 : array_subscript_fetch_old(ExprState *state,
402 : : ExprEvalStep *op,
403 : : ExprContext *econtext)
404 : : {
405 : 183 : SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
406 : 183 : ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace;
407 : :
408 [ + + ]: 183 : if (*op->resnull)
409 : : {
410 : : /* whole array is null, so any element is too */
411 : 58 : sbsrefstate->prevvalue = (Datum) 0;
412 : 58 : sbsrefstate->prevnull = true;
413 : : }
414 : : else
415 : 125 : sbsrefstate->prevvalue = array_get_element(*op->resvalue,
416 : : sbsrefstate->numupper,
417 : 125 : workspace->upperindex,
418 : 125 : workspace->refattrlength,
419 : 125 : workspace->refelemlength,
420 : 125 : workspace->refelembyval,
421 : 125 : workspace->refelemalign,
422 : : &sbsrefstate->prevnull);
423 : 183 : }
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 : : */
440 : : static void
1973 tgl@sss.pgh.pa.us 441 :UBC 0 : array_subscript_fetch_old_slice(ExprState *state,
442 : : ExprEvalStep *op,
443 : : ExprContext *econtext)
444 : : {
445 : 0 : SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
446 : 0 : ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace;
447 : :
448 [ # # ]: 0 : if (*op->resnull)
449 : : {
450 : : /* whole array is null, so any slice is too */
451 : 0 : sbsrefstate->prevvalue = (Datum) 0;
452 : 0 : sbsrefstate->prevnull = true;
453 : : }
454 : : else
455 : : {
456 : 0 : sbsrefstate->prevvalue = array_get_slice(*op->resvalue,
457 : : sbsrefstate->numupper,
458 : 0 : workspace->upperindex,
459 : 0 : workspace->lowerindex,
460 : : sbsrefstate->upperprovided,
461 : : sbsrefstate->lowerprovided,
462 : 0 : workspace->refattrlength,
463 : 0 : workspace->refelemlength,
464 : 0 : workspace->refelembyval,
465 : 0 : workspace->refelemalign);
466 : : /* slices of non-null arrays are never null */
467 : 0 : sbsrefstate->prevnull = false;
468 : : }
469 : 0 : }
470 : :
471 : : /*
472 : : * Set up execution state for an array subscript operation.
473 : : */
474 : : static void
1973 tgl@sss.pgh.pa.us 475 :CBC 20043 : array_exec_setup(const SubscriptingRef *sbsref,
476 : : SubscriptingRefState *sbsrefstate,
477 : : SubscriptExecSteps *methods)
478 : : {
479 : 20043 : 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 [ - + ]: 20043 : if (sbsrefstate->numupper > MAXDIM)
1973 tgl@sss.pgh.pa.us 488 [ # # ]:UBC 0 : ereport(ERROR,
489 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
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: */
1973 tgl@sss.pgh.pa.us 494 [ + + ]:CBC 20043 : if (sbsrefstate->numlower != 0 &&
495 [ - + ]: 307 : sbsrefstate->numupper != sbsrefstate->numlower)
1973 tgl@sss.pgh.pa.us 496 [ # # ]:UBC 0 : elog(ERROR, "upper and lower index lists are not same length");
497 : :
498 : : /*
499 : : * Allocate type-specific workspace.
500 : : */
146 michael@paquier.xyz 501 :GNC 20043 : workspace = palloc_object(ArraySubWorkspace);
1973 tgl@sss.pgh.pa.us 502 :CBC 20043 : sbsrefstate->workspace = workspace;
503 : :
504 : : /*
505 : : * Collect datatype details we'll need at execution.
506 : : */
507 : 20043 : workspace->refelemtype = sbsref->refelemtype;
508 : 20043 : workspace->refattrlength = get_typlen(sbsref->refcontainertype);
509 : 20043 : 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 : : */
517 : 20043 : methods->sbs_check_subscripts = array_subscript_check_subscripts;
518 [ + + ]: 20043 : if (is_slice)
519 : : {
520 : 307 : methods->sbs_fetch = array_subscript_fetch_slice;
521 : 307 : methods->sbs_assign = array_subscript_assign_slice;
522 : 307 : methods->sbs_fetch_old = array_subscript_fetch_old_slice;
523 : : }
524 : : else
525 : : {
526 : 19736 : methods->sbs_fetch = array_subscript_fetch;
527 : 19736 : methods->sbs_assign = array_subscript_assign;
528 : 19736 : methods->sbs_fetch_old = array_subscript_fetch_old;
529 : : }
530 : 20043 : }
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 : : */
540 : : Datum
541 : 27747 : array_subscript_handler(PG_FUNCTION_ARGS)
542 : : {
543 : : static const SubscriptRoutines sbsroutines = {
544 : : .transform = array_subscript_transform,
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 : :
551 : 27747 : PG_RETURN_POINTER(&sbsroutines);
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 : : */
567 : : Datum
568 : 1059 : raw_array_subscript_handler(PG_FUNCTION_ARGS)
569 : : {
570 : : static const SubscriptRoutines sbsroutines = {
571 : : .transform = array_subscript_transform,
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 : :
578 : 1059 : PG_RETURN_POINTER(&sbsroutines);
579 : : }
580 : :
581 : : /*
582 : : * array_subscript_handler_support()
583 : : *
584 : : * Planner support function for array_subscript_handler()
585 : : */
586 : : Datum
448 587 : 1 : array_subscript_handler_support(PG_FUNCTION_ARGS)
588 : : {
589 : 1 : Node *rawreq = (Node *) PG_GETARG_POINTER(0);
590 : 1 : Node *ret = NULL;
591 : :
592 [ + - ]: 1 : if (IsA(rawreq, SupportRequestModifyInPlace))
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 : : */
601 : 1 : SupportRequestModifyInPlace *req = (SupportRequestModifyInPlace *) rawreq;
602 : 1 : Param *refexpr = (Param *) linitial(req->args);
603 : :
604 [ + - + - ]: 1 : if (refexpr && IsA(refexpr, Param) &&
605 [ + - ]: 1 : refexpr->paramkind == PARAM_EXTERN &&
606 [ + - ]: 1 : refexpr->paramid == req->paramid &&
607 [ + - ]: 1 : lsecond(req->args) != NULL)
608 : 1 : ret = (Node *) refexpr;
609 : : }
610 : :
611 : 1 : PG_RETURN_POINTER(ret);
612 : : }
|