/src/postgres/src/backend/utils/adt/arraysubs.c
Line | Count | Source (jump to first uncovered line) |
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 |
56 | | array_subscript_transform(SubscriptingRef *sbsref, |
57 | | List *indirection, |
58 | | ParseState *pstate, |
59 | | bool isSlice, |
60 | | bool isAssignment) |
61 | 0 | { |
62 | 0 | List *upperIndexpr = NIL; |
63 | 0 | List *lowerIndexpr = NIL; |
64 | 0 | 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 | 0 | foreach(idx, indirection) |
75 | 0 | { |
76 | 0 | A_Indices *ai = lfirst_node(A_Indices, idx); |
77 | 0 | Node *subexpr; |
78 | |
|
79 | 0 | if (isSlice) |
80 | 0 | { |
81 | 0 | if (ai->lidx) |
82 | 0 | { |
83 | 0 | subexpr = transformExpr(pstate, ai->lidx, pstate->p_expr_kind); |
84 | | /* If it's not int4 already, try to coerce */ |
85 | 0 | subexpr = coerce_to_target_type(pstate, |
86 | 0 | subexpr, exprType(subexpr), |
87 | 0 | INT4OID, -1, |
88 | 0 | COERCION_ASSIGNMENT, |
89 | 0 | COERCE_IMPLICIT_CAST, |
90 | 0 | -1); |
91 | 0 | if (subexpr == NULL) |
92 | 0 | ereport(ERROR, |
93 | 0 | (errcode(ERRCODE_DATATYPE_MISMATCH), |
94 | 0 | errmsg("array subscript must have type integer"), |
95 | 0 | parser_errposition(pstate, exprLocation(ai->lidx)))); |
96 | 0 | } |
97 | 0 | else if (!ai->is_slice) |
98 | 0 | { |
99 | | /* Make a constant 1 */ |
100 | 0 | subexpr = (Node *) makeConst(INT4OID, |
101 | 0 | -1, |
102 | 0 | InvalidOid, |
103 | 0 | sizeof(int32), |
104 | 0 | Int32GetDatum(1), |
105 | 0 | false, |
106 | 0 | true); /* pass by value */ |
107 | 0 | } |
108 | 0 | else |
109 | 0 | { |
110 | | /* Slice with omitted lower bound, put NULL into the list */ |
111 | 0 | subexpr = NULL; |
112 | 0 | } |
113 | 0 | lowerIndexpr = lappend(lowerIndexpr, subexpr); |
114 | 0 | } |
115 | 0 | else |
116 | 0 | Assert(ai->lidx == NULL && !ai->is_slice); |
117 | | |
118 | 0 | if (ai->uidx) |
119 | 0 | { |
120 | 0 | subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind); |
121 | | /* If it's not int4 already, try to coerce */ |
122 | 0 | subexpr = coerce_to_target_type(pstate, |
123 | 0 | subexpr, exprType(subexpr), |
124 | 0 | INT4OID, -1, |
125 | 0 | COERCION_ASSIGNMENT, |
126 | 0 | COERCE_IMPLICIT_CAST, |
127 | 0 | -1); |
128 | 0 | if (subexpr == NULL) |
129 | 0 | ereport(ERROR, |
130 | 0 | (errcode(ERRCODE_DATATYPE_MISMATCH), |
131 | 0 | errmsg("array subscript must have type integer"), |
132 | 0 | parser_errposition(pstate, exprLocation(ai->uidx)))); |
133 | 0 | } |
134 | 0 | else |
135 | 0 | { |
136 | | /* Slice with omitted upper bound, put NULL into the list */ |
137 | 0 | Assert(isSlice && ai->is_slice); |
138 | 0 | subexpr = NULL; |
139 | 0 | } |
140 | 0 | upperIndexpr = lappend(upperIndexpr, subexpr); |
141 | 0 | } |
142 | | |
143 | | /* ... and store the transformed lists into the SubscriptRef node */ |
144 | 0 | sbsref->refupperindexpr = upperIndexpr; |
145 | 0 | sbsref->reflowerindexpr = lowerIndexpr; |
146 | | |
147 | | /* Verify subscript list lengths are within implementation limit */ |
148 | 0 | if (list_length(upperIndexpr) > MAXDIM) |
149 | 0 | ereport(ERROR, |
150 | 0 | (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), |
151 | 0 | errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)", |
152 | 0 | 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 | 0 | if (isSlice) |
162 | 0 | sbsref->refrestype = sbsref->refcontainertype; |
163 | 0 | else |
164 | 0 | sbsref->refrestype = sbsref->refelemtype; |
165 | 0 | } |
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 | | array_subscript_check_subscripts(ExprState *state, |
182 | | ExprEvalStep *op, |
183 | | ExprContext *econtext) |
184 | 0 | { |
185 | 0 | SubscriptingRefState *sbsrefstate = op->d.sbsref_subscript.state; |
186 | 0 | ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace; |
187 | | |
188 | | /* Process upper subscripts */ |
189 | 0 | for (int i = 0; i < sbsrefstate->numupper; i++) |
190 | 0 | { |
191 | 0 | if (sbsrefstate->upperprovided[i]) |
192 | 0 | { |
193 | | /* If any index expr yields NULL, result is NULL or error */ |
194 | 0 | if (sbsrefstate->upperindexnull[i]) |
195 | 0 | { |
196 | 0 | if (sbsrefstate->isassignment) |
197 | 0 | ereport(ERROR, |
198 | 0 | (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), |
199 | 0 | errmsg("array subscript in assignment must not be null"))); |
200 | 0 | *op->resnull = true; |
201 | 0 | return false; |
202 | 0 | } |
203 | 0 | workspace->upperindex[i] = DatumGetInt32(sbsrefstate->upperindex[i]); |
204 | 0 | } |
205 | 0 | } |
206 | | |
207 | | /* Likewise for lower subscripts */ |
208 | 0 | for (int i = 0; i < sbsrefstate->numlower; i++) |
209 | 0 | { |
210 | 0 | if (sbsrefstate->lowerprovided[i]) |
211 | 0 | { |
212 | | /* If any index expr yields NULL, result is NULL or error */ |
213 | 0 | if (sbsrefstate->lowerindexnull[i]) |
214 | 0 | { |
215 | 0 | if (sbsrefstate->isassignment) |
216 | 0 | ereport(ERROR, |
217 | 0 | (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), |
218 | 0 | errmsg("array subscript in assignment must not be null"))); |
219 | 0 | *op->resnull = true; |
220 | 0 | return false; |
221 | 0 | } |
222 | 0 | workspace->lowerindex[i] = DatumGetInt32(sbsrefstate->lowerindex[i]); |
223 | 0 | } |
224 | 0 | } |
225 | | |
226 | 0 | return true; |
227 | 0 | } |
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 | | array_subscript_fetch(ExprState *state, |
238 | | ExprEvalStep *op, |
239 | | ExprContext *econtext) |
240 | 0 | { |
241 | 0 | SubscriptingRefState *sbsrefstate = op->d.sbsref.state; |
242 | 0 | ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace; |
243 | | |
244 | | /* Should not get here if source array (or any subscript) is null */ |
245 | 0 | Assert(!(*op->resnull)); |
246 | |
|
247 | 0 | *op->resvalue = array_get_element(*op->resvalue, |
248 | 0 | sbsrefstate->numupper, |
249 | 0 | workspace->upperindex, |
250 | 0 | workspace->refattrlength, |
251 | 0 | workspace->refelemlength, |
252 | 0 | workspace->refelembyval, |
253 | 0 | workspace->refelemalign, |
254 | 0 | op->resnull); |
255 | 0 | } |
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 | | array_subscript_fetch_slice(ExprState *state, |
266 | | ExprEvalStep *op, |
267 | | ExprContext *econtext) |
268 | 0 | { |
269 | 0 | SubscriptingRefState *sbsrefstate = op->d.sbsref.state; |
270 | 0 | ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace; |
271 | | |
272 | | /* Should not get here if source array (or any subscript) is null */ |
273 | 0 | Assert(!(*op->resnull)); |
274 | |
|
275 | 0 | *op->resvalue = array_get_slice(*op->resvalue, |
276 | 0 | sbsrefstate->numupper, |
277 | 0 | workspace->upperindex, |
278 | 0 | workspace->lowerindex, |
279 | 0 | sbsrefstate->upperprovided, |
280 | 0 | sbsrefstate->lowerprovided, |
281 | 0 | workspace->refattrlength, |
282 | 0 | workspace->refelemlength, |
283 | 0 | workspace->refelembyval, |
284 | 0 | workspace->refelemalign); |
285 | | /* The slice is never NULL, so no need to change *op->resnull */ |
286 | 0 | } |
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 | | array_subscript_assign(ExprState *state, |
296 | | ExprEvalStep *op, |
297 | | ExprContext *econtext) |
298 | 0 | { |
299 | 0 | SubscriptingRefState *sbsrefstate = op->d.sbsref.state; |
300 | 0 | ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace; |
301 | 0 | 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 | 0 | if (workspace->refattrlength > 0) |
309 | 0 | { |
310 | 0 | if (*op->resnull || sbsrefstate->replacenull) |
311 | 0 | return; |
312 | 0 | } |
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 | 0 | if (*op->resnull) |
321 | 0 | { |
322 | 0 | arraySource = PointerGetDatum(construct_empty_array(workspace->refelemtype)); |
323 | 0 | *op->resnull = false; |
324 | 0 | } |
325 | |
|
326 | 0 | *op->resvalue = array_set_element(arraySource, |
327 | 0 | sbsrefstate->numupper, |
328 | 0 | workspace->upperindex, |
329 | 0 | sbsrefstate->replacevalue, |
330 | 0 | sbsrefstate->replacenull, |
331 | 0 | workspace->refattrlength, |
332 | 0 | workspace->refelemlength, |
333 | 0 | workspace->refelembyval, |
334 | 0 | workspace->refelemalign); |
335 | | /* The result is never NULL, so no need to change *op->resnull */ |
336 | 0 | } |
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 | | array_subscript_assign_slice(ExprState *state, |
346 | | ExprEvalStep *op, |
347 | | ExprContext *econtext) |
348 | 0 | { |
349 | 0 | SubscriptingRefState *sbsrefstate = op->d.sbsref.state; |
350 | 0 | ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace; |
351 | 0 | 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 | 0 | if (workspace->refattrlength > 0) |
359 | 0 | { |
360 | 0 | if (*op->resnull || sbsrefstate->replacenull) |
361 | 0 | return; |
362 | 0 | } |
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 | | */ |
370 | 0 | if (*op->resnull) |
371 | 0 | { |
372 | 0 | arraySource = PointerGetDatum(construct_empty_array(workspace->refelemtype)); |
373 | 0 | *op->resnull = false; |
374 | 0 | } |
375 | |
|
376 | 0 | *op->resvalue = array_set_slice(arraySource, |
377 | 0 | sbsrefstate->numupper, |
378 | 0 | workspace->upperindex, |
379 | 0 | workspace->lowerindex, |
380 | 0 | sbsrefstate->upperprovided, |
381 | 0 | sbsrefstate->lowerprovided, |
382 | 0 | sbsrefstate->replacevalue, |
383 | 0 | sbsrefstate->replacenull, |
384 | 0 | workspace->refattrlength, |
385 | 0 | workspace->refelemlength, |
386 | 0 | workspace->refelembyval, |
387 | 0 | workspace->refelemalign); |
388 | | /* The result is never NULL, so no need to change *op->resnull */ |
389 | 0 | } |
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 | | array_subscript_fetch_old(ExprState *state, |
401 | | ExprEvalStep *op, |
402 | | ExprContext *econtext) |
403 | 0 | { |
404 | 0 | SubscriptingRefState *sbsrefstate = op->d.sbsref.state; |
405 | 0 | ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace; |
406 | |
|
407 | 0 | if (*op->resnull) |
408 | 0 | { |
409 | | /* whole array is null, so any element is too */ |
410 | 0 | sbsrefstate->prevvalue = (Datum) 0; |
411 | 0 | sbsrefstate->prevnull = true; |
412 | 0 | } |
413 | 0 | else |
414 | 0 | sbsrefstate->prevvalue = array_get_element(*op->resvalue, |
415 | 0 | sbsrefstate->numupper, |
416 | 0 | workspace->upperindex, |
417 | 0 | workspace->refattrlength, |
418 | 0 | workspace->refelemlength, |
419 | 0 | workspace->refelembyval, |
420 | 0 | workspace->refelemalign, |
421 | 0 | &sbsrefstate->prevnull); |
422 | 0 | } |
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 |
440 | | array_subscript_fetch_old_slice(ExprState *state, |
441 | | ExprEvalStep *op, |
442 | | ExprContext *econtext) |
443 | 0 | { |
444 | 0 | SubscriptingRefState *sbsrefstate = op->d.sbsref.state; |
445 | 0 | ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace; |
446 | |
|
447 | 0 | if (*op->resnull) |
448 | 0 | { |
449 | | /* whole array is null, so any slice is too */ |
450 | 0 | sbsrefstate->prevvalue = (Datum) 0; |
451 | 0 | sbsrefstate->prevnull = true; |
452 | 0 | } |
453 | 0 | else |
454 | 0 | { |
455 | 0 | sbsrefstate->prevvalue = array_get_slice(*op->resvalue, |
456 | 0 | sbsrefstate->numupper, |
457 | 0 | workspace->upperindex, |
458 | 0 | workspace->lowerindex, |
459 | 0 | sbsrefstate->upperprovided, |
460 | 0 | 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 | 0 | } |
468 | 0 | } |
469 | | |
470 | | /* |
471 | | * Set up execution state for an array subscript operation. |
472 | | */ |
473 | | static void |
474 | | array_exec_setup(const SubscriptingRef *sbsref, |
475 | | SubscriptingRefState *sbsrefstate, |
476 | | SubscriptExecSteps *methods) |
477 | 0 | { |
478 | 0 | bool is_slice = (sbsrefstate->numlower != 0); |
479 | 0 | 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 | 0 | if (sbsrefstate->numupper > MAXDIM) |
487 | 0 | ereport(ERROR, |
488 | 0 | (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), |
489 | 0 | errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)", |
490 | 0 | sbsrefstate->numupper, MAXDIM))); |
491 | | |
492 | | /* Should be impossible if parser is sane, but check anyway: */ |
493 | 0 | if (sbsrefstate->numlower != 0 && |
494 | 0 | sbsrefstate->numupper != sbsrefstate->numlower) |
495 | 0 | elog(ERROR, "upper and lower index lists are not same length"); |
496 | | |
497 | | /* |
498 | | * Allocate type-specific workspace. |
499 | | */ |
500 | 0 | workspace = (ArraySubWorkspace *) palloc(sizeof(ArraySubWorkspace)); |
501 | 0 | sbsrefstate->workspace = workspace; |
502 | | |
503 | | /* |
504 | | * Collect datatype details we'll need at execution. |
505 | | */ |
506 | 0 | workspace->refelemtype = sbsref->refelemtype; |
507 | 0 | workspace->refattrlength = get_typlen(sbsref->refcontainertype); |
508 | 0 | get_typlenbyvalalign(sbsref->refelemtype, |
509 | 0 | &workspace->refelemlength, |
510 | 0 | &workspace->refelembyval, |
511 | 0 | &workspace->refelemalign); |
512 | | |
513 | | /* |
514 | | * Pass back pointers to appropriate step execution functions. |
515 | | */ |
516 | 0 | methods->sbs_check_subscripts = array_subscript_check_subscripts; |
517 | 0 | if (is_slice) |
518 | 0 | { |
519 | 0 | methods->sbs_fetch = array_subscript_fetch_slice; |
520 | 0 | methods->sbs_assign = array_subscript_assign_slice; |
521 | 0 | methods->sbs_fetch_old = array_subscript_fetch_old_slice; |
522 | 0 | } |
523 | 0 | else |
524 | 0 | { |
525 | 0 | methods->sbs_fetch = array_subscript_fetch; |
526 | 0 | methods->sbs_assign = array_subscript_assign; |
527 | 0 | methods->sbs_fetch_old = array_subscript_fetch_old; |
528 | 0 | } |
529 | 0 | } |
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 | | array_subscript_handler(PG_FUNCTION_ARGS) |
541 | 0 | { |
542 | 0 | static const SubscriptRoutines sbsroutines = { |
543 | 0 | .transform = array_subscript_transform, |
544 | 0 | .exec_setup = array_exec_setup, |
545 | 0 | .fetch_strict = true, /* fetch returns NULL for NULL inputs */ |
546 | 0 | .fetch_leakproof = true, /* fetch returns NULL for bad subscript */ |
547 | | .store_leakproof = false /* ... but assignment throws error */ |
548 | 0 | }; |
549 | |
|
550 | 0 | PG_RETURN_POINTER(&sbsroutines); |
551 | 0 | } |
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 | | raw_array_subscript_handler(PG_FUNCTION_ARGS) |
568 | 0 | { |
569 | 0 | static const SubscriptRoutines sbsroutines = { |
570 | 0 | .transform = array_subscript_transform, |
571 | 0 | .exec_setup = array_exec_setup, |
572 | 0 | .fetch_strict = true, /* fetch returns NULL for NULL inputs */ |
573 | 0 | .fetch_leakproof = true, /* fetch returns NULL for bad subscript */ |
574 | | .store_leakproof = false /* ... but assignment throws error */ |
575 | 0 | }; |
576 | |
|
577 | 0 | PG_RETURN_POINTER(&sbsroutines); |
578 | 0 | } |
579 | | |
580 | | /* |
581 | | * array_subscript_handler_support() |
582 | | * |
583 | | * Planner support function for array_subscript_handler() |
584 | | */ |
585 | | Datum |
586 | | array_subscript_handler_support(PG_FUNCTION_ARGS) |
587 | 0 | { |
588 | 0 | Node *rawreq = (Node *) PG_GETARG_POINTER(0); |
589 | 0 | Node *ret = NULL; |
590 | |
|
591 | 0 | if (IsA(rawreq, SupportRequestModifyInPlace)) |
592 | 0 | { |
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 | 0 | SupportRequestModifyInPlace *req = (SupportRequestModifyInPlace *) rawreq; |
601 | 0 | Param *refexpr = (Param *) linitial(req->args); |
602 | |
|
603 | 0 | if (refexpr && IsA(refexpr, Param) && |
604 | 0 | refexpr->paramkind == PARAM_EXTERN && |
605 | 0 | refexpr->paramid == req->paramid && |
606 | 0 | lsecond(req->args) != NULL) |
607 | 0 | ret = (Node *) refexpr; |
608 | 0 | } |
609 | |
|
610 | 0 | PG_RETURN_POINTER(ret); |
611 | 0 | } |