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-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 : : */
43 : : int upperindex[MAXDIM];
44 : : int lowerindex[MAXDIM];
45 : : } ArraySubWorkspace;
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
1222 tgl@sss.pgh.pa.us 55 :CBC 5601 : array_subscript_transform(SubscriptingRef *sbsref,
56 : : List *indirection,
57 : : ParseState *pstate,
58 : : bool isSlice,
59 : : bool isAssignment)
60 : : {
61 : 5601 : List *upperIndexpr = NIL;
62 : 5601 : 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 [ + - + + : 11347 : foreach(idx, indirection)
+ + ]
74 : : {
75 : 5746 : A_Indices *ai = lfirst_node(A_Indices, idx);
76 : : Node *subexpr;
77 : :
78 [ + + ]: 5746 : if (isSlice)
79 : : {
80 [ + + ]: 286 : if (ai->lidx)
81 : : {
82 : 220 : subexpr = transformExpr(pstate, ai->lidx, pstate->p_expr_kind);
83 : : /* If it's not int4 already, try to coerce */
84 : 220 : subexpr = coerce_to_target_type(pstate,
85 : : subexpr, exprType(subexpr),
86 : : INT4OID, -1,
87 : : COERCION_ASSIGNMENT,
88 : : COERCE_IMPLICIT_CAST,
89 : : -1);
90 [ - + ]: 220 : if (subexpr == NULL)
1222 tgl@sss.pgh.pa.us 91 [ # # ]:UBC 0 : ereport(ERROR,
92 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
93 : : errmsg("array subscript must have type integer"),
94 : : parser_errposition(pstate, exprLocation(ai->lidx))));
95 : : }
1222 tgl@sss.pgh.pa.us 96 [ + + ]:CBC 66 : else if (!ai->is_slice)
97 : : {
98 : : /* Make a constant 1 */
99 : 27 : 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 : 39 : subexpr = NULL;
111 : : }
112 : 286 : lowerIndexpr = lappend(lowerIndexpr, subexpr);
113 : : }
114 : : else
115 [ + - - + ]: 5460 : Assert(ai->lidx == NULL && !ai->is_slice);
116 : :
117 [ + + ]: 5746 : if (ai->uidx)
118 : : {
119 : 5707 : subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind);
120 : : /* If it's not int4 already, try to coerce */
121 : 5707 : subexpr = coerce_to_target_type(pstate,
122 : : subexpr, exprType(subexpr),
123 : : INT4OID, -1,
124 : : COERCION_ASSIGNMENT,
125 : : COERCE_IMPLICIT_CAST,
126 : : -1);
127 [ - + ]: 5707 : if (subexpr == NULL)
1222 tgl@sss.pgh.pa.us 128 [ # # ]:UBC 0 : 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 */
1222 tgl@sss.pgh.pa.us 136 [ + - - + ]:CBC 39 : Assert(isSlice && ai->is_slice);
137 : 39 : subexpr = NULL;
138 : : }
139 : 5746 : upperIndexpr = lappend(upperIndexpr, subexpr);
140 : : }
141 : :
142 : : /* ... and store the transformed lists into the SubscriptRef node */
143 : 5601 : sbsref->refupperindexpr = upperIndexpr;
144 : 5601 : sbsref->reflowerindexpr = lowerIndexpr;
145 : :
146 : : /* Verify subscript list lengths are within implementation limit */
147 [ + + ]: 5601 : if (list_length(upperIndexpr) > MAXDIM)
148 [ + - ]: 3 : 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 [ + + ]: 5598 : if (isSlice)
161 : 208 : sbsref->refrestype = sbsref->refcontainertype;
162 : : else
163 : 5390 : sbsref->refrestype = sbsref->refelemtype;
164 : 5598 : }
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
180 : 356501 : array_subscript_check_subscripts(ExprState *state,
181 : : ExprEvalStep *op,
182 : : ExprContext *econtext)
183 : : {
184 : 356501 : SubscriptingRefState *sbsrefstate = op->d.sbsref_subscript.state;
185 : 356501 : ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace;
186 : :
187 : : /* Process upper subscripts */
188 [ + + ]: 713225 : for (int i = 0; i < sbsrefstate->numupper; i++)
189 : : {
190 [ + + ]: 356736 : if (sbsrefstate->upperprovided[i])
191 : : {
192 : : /* If any index expr yields NULL, result is NULL or error */
193 [ + + ]: 356664 : if (sbsrefstate->upperindexnull[i])
194 : : {
195 [ + + ]: 12 : if (sbsrefstate->isassignment)
196 [ + - ]: 6 : ereport(ERROR,
197 : : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
198 : : errmsg("array subscript in assignment must not be null")));
199 : 6 : *op->resnull = true;
200 : 6 : return false;
201 : : }
202 : 356652 : workspace->upperindex[i] = DatumGetInt32(sbsrefstate->upperindex[i]);
203 : : }
204 : : }
205 : :
206 : : /* Likewise for lower subscripts */
207 [ + + ]: 356924 : for (int i = 0; i < sbsrefstate->numlower; i++)
208 : : {
209 [ + + ]: 441 : if (sbsrefstate->lowerprovided[i])
210 : : {
211 : : /* If any index expr yields NULL, result is NULL or error */
212 [ + + ]: 378 : if (sbsrefstate->lowerindexnull[i])
213 : : {
214 [ + + ]: 6 : if (sbsrefstate->isassignment)
215 [ + - ]: 3 : ereport(ERROR,
216 : : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
217 : : errmsg("array subscript in assignment must not be null")));
218 : 3 : *op->resnull = true;
219 : 3 : return false;
220 : : }
221 : 372 : workspace->lowerindex[i] = DatumGetInt32(sbsrefstate->lowerindex[i]);
222 : : }
223 : : }
224 : :
225 : 356483 : 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
236 : 355613 : array_subscript_fetch(ExprState *state,
237 : : ExprEvalStep *op,
238 : : ExprContext *econtext)
239 : : {
240 : 355613 : SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
241 : 355613 : ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace;
242 : :
243 : : /* Should not get here if source array (or any subscript) is null */
244 [ - + ]: 355613 : Assert(!(*op->resnull));
245 : :
246 : 711226 : *op->resvalue = array_get_element(*op->resvalue,
247 : : sbsrefstate->numupper,
248 : 355613 : workspace->upperindex,
249 : 355613 : workspace->refattrlength,
250 : 355613 : workspace->refelemlength,
251 : 355613 : workspace->refelembyval,
252 : 355613 : workspace->refelemalign,
253 : : op->resnull);
254 : 355613 : }
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
264 : 177 : array_subscript_fetch_slice(ExprState *state,
265 : : ExprEvalStep *op,
266 : : ExprContext *econtext)
267 : : {
268 : 177 : SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
269 : 177 : ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace;
270 : :
271 : : /* Should not get here if source array (or any subscript) is null */
272 [ - + ]: 177 : Assert(!(*op->resnull));
273 : :
274 : 342 : *op->resvalue = array_get_slice(*op->resvalue,
275 : : sbsrefstate->numupper,
276 : 177 : workspace->upperindex,
277 : 177 : workspace->lowerindex,
278 : : sbsrefstate->upperprovided,
279 : : sbsrefstate->lowerprovided,
280 : 177 : workspace->refattrlength,
281 : 177 : workspace->refelemlength,
282 : 177 : workspace->refelembyval,
283 : 177 : workspace->refelemalign);
284 : : /* The slice is never NULL, so no need to change *op->resnull */
285 : 165 : }
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
294 : 552 : array_subscript_assign(ExprState *state,
295 : : ExprEvalStep *op,
296 : : ExprContext *econtext)
297 : : {
298 : 552 : SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
299 : 552 : ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace;
300 : 552 : 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 [ + + ]: 552 : if (workspace->refattrlength > 0)
308 : : {
309 [ + + + + ]: 18 : if (*op->resnull || sbsrefstate->replacenull)
310 : 9 : 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 [ + + ]: 543 : if (*op->resnull)
320 : : {
321 : 171 : arraySource = PointerGetDatum(construct_empty_array(workspace->refelemtype));
322 : 171 : *op->resnull = false;
323 : : }
324 : :
325 : 531 : *op->resvalue = array_set_element(arraySource,
326 : : sbsrefstate->numupper,
327 : 543 : workspace->upperindex,
328 : : sbsrefstate->replacevalue,
329 : 543 : sbsrefstate->replacenull,
330 : 543 : workspace->refattrlength,
331 : 543 : workspace->refelemlength,
332 : 543 : workspace->refelembyval,
333 : 543 : 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
344 : 125 : array_subscript_assign_slice(ExprState *state,
345 : : ExprEvalStep *op,
346 : : ExprContext *econtext)
347 : : {
348 : 125 : SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
349 : 125 : ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace;
350 : 125 : 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 [ - + ]: 125 : if (workspace->refattrlength > 0)
358 : : {
1222 tgl@sss.pgh.pa.us 359 [ # # # # ]:UBC 0 : if (*op->resnull || sbsrefstate->replacenull)
360 : 0 : 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 : : */
1222 tgl@sss.pgh.pa.us 369 [ + + ]:CBC 125 : if (*op->resnull)
370 : : {
371 : 22 : arraySource = PointerGetDatum(construct_empty_array(workspace->refelemtype));
372 : 22 : *op->resnull = false;
373 : : }
374 : :
375 : 116 : *op->resvalue = array_set_slice(arraySource,
376 : : sbsrefstate->numupper,
377 : 125 : workspace->upperindex,
378 : 125 : workspace->lowerindex,
379 : : sbsrefstate->upperprovided,
380 : : sbsrefstate->lowerprovided,
381 : : sbsrefstate->replacevalue,
382 : 125 : sbsrefstate->replacenull,
383 : 125 : workspace->refattrlength,
384 : 125 : workspace->refelemlength,
385 : 125 : workspace->refelembyval,
386 : 125 : 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
399 : 138 : array_subscript_fetch_old(ExprState *state,
400 : : ExprEvalStep *op,
401 : : ExprContext *econtext)
402 : : {
403 : 138 : SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
404 : 138 : ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace;
405 : :
406 [ + + ]: 138 : if (*op->resnull)
407 : : {
408 : : /* whole array is null, so any element is too */
409 : 44 : sbsrefstate->prevvalue = (Datum) 0;
410 : 44 : sbsrefstate->prevnull = true;
411 : : }
412 : : else
413 : 94 : sbsrefstate->prevvalue = array_get_element(*op->resvalue,
414 : : sbsrefstate->numupper,
415 : 94 : workspace->upperindex,
416 : 94 : workspace->refattrlength,
417 : 94 : workspace->refelemlength,
418 : 94 : workspace->refelembyval,
419 : 94 : workspace->refelemalign,
420 : : &sbsrefstate->prevnull);
421 : 138 : }
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
1222 tgl@sss.pgh.pa.us 439 :UBC 0 : array_subscript_fetch_old_slice(ExprState *state,
440 : : ExprEvalStep *op,
441 : : ExprContext *econtext)
442 : : {
443 : 0 : SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
444 : 0 : ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace;
445 : :
446 [ # # ]: 0 : if (*op->resnull)
447 : : {
448 : : /* whole array is null, so any slice is too */
449 : 0 : sbsrefstate->prevvalue = (Datum) 0;
450 : 0 : sbsrefstate->prevnull = true;
451 : : }
452 : : else
453 : : {
454 : 0 : sbsrefstate->prevvalue = array_get_slice(*op->resvalue,
455 : : sbsrefstate->numupper,
456 : 0 : workspace->upperindex,
457 : 0 : workspace->lowerindex,
458 : : sbsrefstate->upperprovided,
459 : : sbsrefstate->lowerprovided,
460 : 0 : workspace->refattrlength,
461 : 0 : workspace->refelemlength,
462 : 0 : workspace->refelembyval,
463 : 0 : workspace->refelemalign);
464 : : /* slices of non-null arrays are never null */
465 : 0 : sbsrefstate->prevnull = false;
466 : : }
467 : 0 : }
468 : :
469 : : /*
470 : : * Set up execution state for an array subscript operation.
471 : : */
472 : : static void
1222 tgl@sss.pgh.pa.us 473 :CBC 10952 : array_exec_setup(const SubscriptingRef *sbsref,
474 : : SubscriptingRefState *sbsrefstate,
475 : : SubscriptExecSteps *methods)
476 : : {
477 : 10952 : 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 [ - + ]: 10952 : if (sbsrefstate->numupper > MAXDIM)
1222 tgl@sss.pgh.pa.us 486 [ # # ]:UBC 0 : 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: */
1222 tgl@sss.pgh.pa.us 492 [ + + ]:CBC 10952 : if (sbsrefstate->numlower != 0 &&
493 [ - + ]: 204 : sbsrefstate->numupper != sbsrefstate->numlower)
1222 tgl@sss.pgh.pa.us 494 [ # # ]:UBC 0 : elog(ERROR, "upper and lower index lists are not same length");
495 : :
496 : : /*
497 : : * Allocate type-specific workspace.
498 : : */
1222 tgl@sss.pgh.pa.us 499 :CBC 10952 : workspace = (ArraySubWorkspace *) palloc(sizeof(ArraySubWorkspace));
500 : 10952 : sbsrefstate->workspace = workspace;
501 : :
502 : : /*
503 : : * Collect datatype details we'll need at execution.
504 : : */
505 : 10952 : workspace->refelemtype = sbsref->refelemtype;
506 : 10952 : workspace->refattrlength = get_typlen(sbsref->refcontainertype);
507 : 10952 : 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 : : */
515 : 10952 : methods->sbs_check_subscripts = array_subscript_check_subscripts;
516 [ + + ]: 10952 : if (is_slice)
517 : : {
518 : 204 : methods->sbs_fetch = array_subscript_fetch_slice;
519 : 204 : methods->sbs_assign = array_subscript_assign_slice;
520 : 204 : methods->sbs_fetch_old = array_subscript_fetch_old_slice;
521 : : }
522 : : else
523 : : {
524 : 10748 : methods->sbs_fetch = array_subscript_fetch;
525 : 10748 : methods->sbs_assign = array_subscript_assign;
526 : 10748 : methods->sbs_fetch_old = array_subscript_fetch_old;
527 : : }
528 : 10952 : }
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
539 : 15806 : array_subscript_handler(PG_FUNCTION_ARGS)
540 : : {
541 : : static const SubscriptRoutines sbsroutines = {
542 : : .transform = array_subscript_transform,
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 : 15806 : 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
566 : 747 : raw_array_subscript_handler(PG_FUNCTION_ARGS)
567 : : {
568 : : static const SubscriptRoutines sbsroutines = {
569 : : .transform = array_subscript_transform,
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 : 747 : PG_RETURN_POINTER(&sbsroutines);
577 : : }
|