1 /*-------------------------------------------------------------------------
2  *
3  * arraysubs.c
4  *	  Subscripting support functions for arrays.
5  *
6  * Portions Copyright (c) 1996-2021, 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/builtins.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
array_subscript_transform(SubscriptingRef * sbsref,List * indirection,ParseState * pstate,bool isSlice,bool isAssignment)55 array_subscript_transform(SubscriptingRef *sbsref,
56 						  List *indirection,
57 						  ParseState *pstate,
58 						  bool isSlice,
59 						  bool isAssignment)
60 {
61 	List	   *upperIndexpr = NIL;
62 	List	   *lowerIndexpr = NIL;
63 	ListCell   *idx;
64 
65 	/*
66 	 * Transform the subscript expressions, and separate upper and lower
67 	 * bounds into two lists.
68 	 *
69 	 * If we have a container slice expression, we convert any non-slice
70 	 * indirection items to slices by treating the single subscript as the
71 	 * upper bound and supplying an assumed lower bound of 1.
72 	 */
73 	foreach(idx, indirection)
74 	{
75 		A_Indices  *ai = lfirst_node(A_Indices, idx);
76 		Node	   *subexpr;
77 
78 		if (isSlice)
79 		{
80 			if (ai->lidx)
81 			{
82 				subexpr = transformExpr(pstate, ai->lidx, pstate->p_expr_kind);
83 				/* If it's not int4 already, try to coerce */
84 				subexpr = coerce_to_target_type(pstate,
85 												subexpr, exprType(subexpr),
86 												INT4OID, -1,
87 												COERCION_ASSIGNMENT,
88 												COERCE_IMPLICIT_CAST,
89 												-1);
90 				if (subexpr == NULL)
91 					ereport(ERROR,
92 							(errcode(ERRCODE_DATATYPE_MISMATCH),
93 							 errmsg("array subscript must have type integer"),
94 							 parser_errposition(pstate, exprLocation(ai->lidx))));
95 			}
96 			else if (!ai->is_slice)
97 			{
98 				/* Make a constant 1 */
99 				subexpr = (Node *) makeConst(INT4OID,
100 											 -1,
101 											 InvalidOid,
102 											 sizeof(int32),
103 											 Int32GetDatum(1),
104 											 false,
105 											 true); /* pass by value */
106 			}
107 			else
108 			{
109 				/* Slice with omitted lower bound, put NULL into the list */
110 				subexpr = NULL;
111 			}
112 			lowerIndexpr = lappend(lowerIndexpr, subexpr);
113 		}
114 		else
115 			Assert(ai->lidx == NULL && !ai->is_slice);
116 
117 		if (ai->uidx)
118 		{
119 			subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind);
120 			/* If it's not int4 already, try to coerce */
121 			subexpr = coerce_to_target_type(pstate,
122 											subexpr, exprType(subexpr),
123 											INT4OID, -1,
124 											COERCION_ASSIGNMENT,
125 											COERCE_IMPLICIT_CAST,
126 											-1);
127 			if (subexpr == NULL)
128 				ereport(ERROR,
129 						(errcode(ERRCODE_DATATYPE_MISMATCH),
130 						 errmsg("array subscript must have type integer"),
131 						 parser_errposition(pstate, exprLocation(ai->uidx))));
132 		}
133 		else
134 		{
135 			/* Slice with omitted upper bound, put NULL into the list */
136 			Assert(isSlice && ai->is_slice);
137 			subexpr = NULL;
138 		}
139 		upperIndexpr = lappend(upperIndexpr, subexpr);
140 	}
141 
142 	/* ... and store the transformed lists into the SubscriptRef node */
143 	sbsref->refupperindexpr = upperIndexpr;
144 	sbsref->reflowerindexpr = lowerIndexpr;
145 
146 	/* Verify subscript list lengths are within implementation limit */
147 	if (list_length(upperIndexpr) > MAXDIM)
148 		ereport(ERROR,
149 				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
150 				 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
151 						list_length(upperIndexpr), MAXDIM)));
152 	/* We need not check lowerIndexpr separately */
153 
154 	/*
155 	 * Determine the result type of the subscripting operation.  It's the same
156 	 * as the array type if we're slicing, else it's the element type.  In
157 	 * either case, the typmod is the same as the array's, so we need not
158 	 * change reftypmod.
159 	 */
160 	if (isSlice)
161 		sbsref->refrestype = sbsref->refcontainertype;
162 	else
163 		sbsref->refrestype = sbsref->refelemtype;
164 }
165 
166 /*
167  * During execution, process the subscripts in a SubscriptingRef expression.
168  *
169  * The subscript expressions are already evaluated in Datum form in the
170  * SubscriptingRefState's arrays.  Check and convert them as necessary.
171  *
172  * If any subscript is NULL, we throw error in assignment cases, or in fetch
173  * cases set result to NULL and return false (instructing caller to skip the
174  * rest of the SubscriptingRef sequence).
175  *
176  * We convert all the subscripts to plain integers and save them in the
177  * sbsrefstate->workspace arrays.
178  */
179 static bool
array_subscript_check_subscripts(ExprState * state,ExprEvalStep * op,ExprContext * econtext)180 array_subscript_check_subscripts(ExprState *state,
181 								 ExprEvalStep *op,
182 								 ExprContext *econtext)
183 {
184 	SubscriptingRefState *sbsrefstate = op->d.sbsref_subscript.state;
185 	ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace;
186 
187 	/* Process upper subscripts */
188 	for (int i = 0; i < sbsrefstate->numupper; i++)
189 	{
190 		if (sbsrefstate->upperprovided[i])
191 		{
192 			/* If any index expr yields NULL, result is NULL or error */
193 			if (sbsrefstate->upperindexnull[i])
194 			{
195 				if (sbsrefstate->isassignment)
196 					ereport(ERROR,
197 							(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
198 							 errmsg("array subscript in assignment must not be null")));
199 				*op->resnull = true;
200 				return false;
201 			}
202 			workspace->upperindex[i] = DatumGetInt32(sbsrefstate->upperindex[i]);
203 		}
204 	}
205 
206 	/* Likewise for lower subscripts */
207 	for (int i = 0; i < sbsrefstate->numlower; i++)
208 	{
209 		if (sbsrefstate->lowerprovided[i])
210 		{
211 			/* If any index expr yields NULL, result is NULL or error */
212 			if (sbsrefstate->lowerindexnull[i])
213 			{
214 				if (sbsrefstate->isassignment)
215 					ereport(ERROR,
216 							(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
217 							 errmsg("array subscript in assignment must not be null")));
218 				*op->resnull = true;
219 				return false;
220 			}
221 			workspace->lowerindex[i] = DatumGetInt32(sbsrefstate->lowerindex[i]);
222 		}
223 	}
224 
225 	return true;
226 }
227 
228 /*
229  * Evaluate SubscriptingRef fetch for an array element.
230  *
231  * Source container is in step's result variable (it's known not NULL, since
232  * we set fetch_strict to true), and indexes have already been evaluated into
233  * workspace array.
234  */
235 static void
array_subscript_fetch(ExprState * state,ExprEvalStep * op,ExprContext * econtext)236 array_subscript_fetch(ExprState *state,
237 					  ExprEvalStep *op,
238 					  ExprContext *econtext)
239 {
240 	SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
241 	ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace;
242 
243 	/* Should not get here if source array (or any subscript) is null */
244 	Assert(!(*op->resnull));
245 
246 	*op->resvalue = array_get_element(*op->resvalue,
247 									  sbsrefstate->numupper,
248 									  workspace->upperindex,
249 									  workspace->refattrlength,
250 									  workspace->refelemlength,
251 									  workspace->refelembyval,
252 									  workspace->refelemalign,
253 									  op->resnull);
254 }
255 
256 /*
257  * Evaluate SubscriptingRef fetch for an array slice.
258  *
259  * Source container is in step's result variable (it's known not NULL, since
260  * we set fetch_strict to true), and indexes have already been evaluated into
261  * workspace array.
262  */
263 static void
array_subscript_fetch_slice(ExprState * state,ExprEvalStep * op,ExprContext * econtext)264 array_subscript_fetch_slice(ExprState *state,
265 							ExprEvalStep *op,
266 							ExprContext *econtext)
267 {
268 	SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
269 	ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace;
270 
271 	/* Should not get here if source array (or any subscript) is null */
272 	Assert(!(*op->resnull));
273 
274 	*op->resvalue = array_get_slice(*op->resvalue,
275 									sbsrefstate->numupper,
276 									workspace->upperindex,
277 									workspace->lowerindex,
278 									sbsrefstate->upperprovided,
279 									sbsrefstate->lowerprovided,
280 									workspace->refattrlength,
281 									workspace->refelemlength,
282 									workspace->refelembyval,
283 									workspace->refelemalign);
284 	/* The slice is never NULL, so no need to change *op->resnull */
285 }
286 
287 /*
288  * Evaluate SubscriptingRef assignment for an array element assignment.
289  *
290  * Input container (possibly null) is in result area, replacement value is in
291  * SubscriptingRefState's replacevalue/replacenull.
292  */
293 static void
array_subscript_assign(ExprState * state,ExprEvalStep * op,ExprContext * econtext)294 array_subscript_assign(ExprState *state,
295 					   ExprEvalStep *op,
296 					   ExprContext *econtext)
297 {
298 	SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
299 	ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace;
300 	Datum		arraySource = *op->resvalue;
301 
302 	/*
303 	 * For an assignment to a fixed-length array type, both the original array
304 	 * and the value to be assigned into it must be non-NULL, else we punt and
305 	 * return the original array.
306 	 */
307 	if (workspace->refattrlength > 0)
308 	{
309 		if (*op->resnull || sbsrefstate->replacenull)
310 			return;
311 	}
312 
313 	/*
314 	 * For assignment to varlena arrays, we handle a NULL original array by
315 	 * substituting an empty (zero-dimensional) array; insertion of the new
316 	 * element will result in a singleton array value.  It does not matter
317 	 * whether the new element is NULL.
318 	 */
319 	if (*op->resnull)
320 	{
321 		arraySource = PointerGetDatum(construct_empty_array(workspace->refelemtype));
322 		*op->resnull = false;
323 	}
324 
325 	*op->resvalue = array_set_element(arraySource,
326 									  sbsrefstate->numupper,
327 									  workspace->upperindex,
328 									  sbsrefstate->replacevalue,
329 									  sbsrefstate->replacenull,
330 									  workspace->refattrlength,
331 									  workspace->refelemlength,
332 									  workspace->refelembyval,
333 									  workspace->refelemalign);
334 	/* The result is never NULL, so no need to change *op->resnull */
335 }
336 
337 /*
338  * Evaluate SubscriptingRef assignment for an array slice assignment.
339  *
340  * Input container (possibly null) is in result area, replacement value is in
341  * SubscriptingRefState's replacevalue/replacenull.
342  */
343 static void
array_subscript_assign_slice(ExprState * state,ExprEvalStep * op,ExprContext * econtext)344 array_subscript_assign_slice(ExprState *state,
345 							 ExprEvalStep *op,
346 							 ExprContext *econtext)
347 {
348 	SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
349 	ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace;
350 	Datum		arraySource = *op->resvalue;
351 
352 	/*
353 	 * For an assignment to a fixed-length array type, both the original array
354 	 * and the value to be assigned into it must be non-NULL, else we punt and
355 	 * return the original array.
356 	 */
357 	if (workspace->refattrlength > 0)
358 	{
359 		if (*op->resnull || sbsrefstate->replacenull)
360 			return;
361 	}
362 
363 	/*
364 	 * For assignment to varlena arrays, we handle a NULL original array by
365 	 * substituting an empty (zero-dimensional) array; insertion of the new
366 	 * element will result in a singleton array value.  It does not matter
367 	 * whether the new element is NULL.
368 	 */
369 	if (*op->resnull)
370 	{
371 		arraySource = PointerGetDatum(construct_empty_array(workspace->refelemtype));
372 		*op->resnull = false;
373 	}
374 
375 	*op->resvalue = array_set_slice(arraySource,
376 									sbsrefstate->numupper,
377 									workspace->upperindex,
378 									workspace->lowerindex,
379 									sbsrefstate->upperprovided,
380 									sbsrefstate->lowerprovided,
381 									sbsrefstate->replacevalue,
382 									sbsrefstate->replacenull,
383 									workspace->refattrlength,
384 									workspace->refelemlength,
385 									workspace->refelembyval,
386 									workspace->refelemalign);
387 	/* The result is never NULL, so no need to change *op->resnull */
388 }
389 
390 /*
391  * Compute old array element value for a SubscriptingRef assignment
392  * expression.  Will only be called if the new-value subexpression
393  * contains SubscriptingRef or FieldStore.  This is the same as the
394  * regular fetch case, except that we have to handle a null array,
395  * and the value should be stored into the SubscriptingRefState's
396  * prevvalue/prevnull fields.
397  */
398 static void
array_subscript_fetch_old(ExprState * state,ExprEvalStep * op,ExprContext * econtext)399 array_subscript_fetch_old(ExprState *state,
400 						  ExprEvalStep *op,
401 						  ExprContext *econtext)
402 {
403 	SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
404 	ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace;
405 
406 	if (*op->resnull)
407 	{
408 		/* whole array is null, so any element is too */
409 		sbsrefstate->prevvalue = (Datum) 0;
410 		sbsrefstate->prevnull = true;
411 	}
412 	else
413 		sbsrefstate->prevvalue = array_get_element(*op->resvalue,
414 												   sbsrefstate->numupper,
415 												   workspace->upperindex,
416 												   workspace->refattrlength,
417 												   workspace->refelemlength,
418 												   workspace->refelembyval,
419 												   workspace->refelemalign,
420 												   &sbsrefstate->prevnull);
421 }
422 
423 /*
424  * Compute old array slice value for a SubscriptingRef assignment
425  * expression.  Will only be called if the new-value subexpression
426  * contains SubscriptingRef or FieldStore.  This is the same as the
427  * regular fetch case, except that we have to handle a null array,
428  * and the value should be stored into the SubscriptingRefState's
429  * prevvalue/prevnull fields.
430  *
431  * Note: this is presently dead code, because the new value for a
432  * slice would have to be an array, so it couldn't directly contain a
433  * FieldStore; nor could it contain a SubscriptingRef assignment, since
434  * we consider adjacent subscripts to index one multidimensional array
435  * not nested array types.  Future generalizations might make this
436  * reachable, however.
437  */
438 static void
array_subscript_fetch_old_slice(ExprState * state,ExprEvalStep * op,ExprContext * econtext)439 array_subscript_fetch_old_slice(ExprState *state,
440 								ExprEvalStep *op,
441 								ExprContext *econtext)
442 {
443 	SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
444 	ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace;
445 
446 	if (*op->resnull)
447 	{
448 		/* whole array is null, so any slice is too */
449 		sbsrefstate->prevvalue = (Datum) 0;
450 		sbsrefstate->prevnull = true;
451 	}
452 	else
453 	{
454 		sbsrefstate->prevvalue = array_get_slice(*op->resvalue,
455 												 sbsrefstate->numupper,
456 												 workspace->upperindex,
457 												 workspace->lowerindex,
458 												 sbsrefstate->upperprovided,
459 												 sbsrefstate->lowerprovided,
460 												 workspace->refattrlength,
461 												 workspace->refelemlength,
462 												 workspace->refelembyval,
463 												 workspace->refelemalign);
464 		/* slices of non-null arrays are never null */
465 		sbsrefstate->prevnull = false;
466 	}
467 }
468 
469 /*
470  * Set up execution state for an array subscript operation.
471  */
472 static void
array_exec_setup(const SubscriptingRef * sbsref,SubscriptingRefState * sbsrefstate,SubscriptExecSteps * methods)473 array_exec_setup(const SubscriptingRef *sbsref,
474 				 SubscriptingRefState *sbsrefstate,
475 				 SubscriptExecSteps *methods)
476 {
477 	bool		is_slice = (sbsrefstate->numlower != 0);
478 	ArraySubWorkspace *workspace;
479 
480 	/*
481 	 * Enforce the implementation limit on number of array subscripts.  This
482 	 * check isn't entirely redundant with checking at parse time; conceivably
483 	 * the expression was stored by a backend with a different MAXDIM value.
484 	 */
485 	if (sbsrefstate->numupper > MAXDIM)
486 		ereport(ERROR,
487 				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
488 				 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
489 						sbsrefstate->numupper, MAXDIM)));
490 
491 	/* Should be impossible if parser is sane, but check anyway: */
492 	if (sbsrefstate->numlower != 0 &&
493 		sbsrefstate->numupper != sbsrefstate->numlower)
494 		elog(ERROR, "upper and lower index lists are not same length");
495 
496 	/*
497 	 * Allocate type-specific workspace.
498 	 */
499 	workspace = (ArraySubWorkspace *) palloc(sizeof(ArraySubWorkspace));
500 	sbsrefstate->workspace = workspace;
501 
502 	/*
503 	 * Collect datatype details we'll need at execution.
504 	 */
505 	workspace->refelemtype = sbsref->refelemtype;
506 	workspace->refattrlength = get_typlen(sbsref->refcontainertype);
507 	get_typlenbyvalalign(sbsref->refelemtype,
508 						 &workspace->refelemlength,
509 						 &workspace->refelembyval,
510 						 &workspace->refelemalign);
511 
512 	/*
513 	 * Pass back pointers to appropriate step execution functions.
514 	 */
515 	methods->sbs_check_subscripts = array_subscript_check_subscripts;
516 	if (is_slice)
517 	{
518 		methods->sbs_fetch = array_subscript_fetch_slice;
519 		methods->sbs_assign = array_subscript_assign_slice;
520 		methods->sbs_fetch_old = array_subscript_fetch_old_slice;
521 	}
522 	else
523 	{
524 		methods->sbs_fetch = array_subscript_fetch;
525 		methods->sbs_assign = array_subscript_assign;
526 		methods->sbs_fetch_old = array_subscript_fetch_old;
527 	}
528 }
529 
530 /*
531  * array_subscript_handler
532  *		Subscripting handler for standard varlena arrays.
533  *
534  * This should be used only for "true" array types, which have array headers
535  * as understood by the varlena array routines, and are referenced by the
536  * element type's pg_type.typarray field.
537  */
538 Datum
array_subscript_handler(PG_FUNCTION_ARGS)539 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 	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
raw_array_subscript_handler(PG_FUNCTION_ARGS)566 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 	PG_RETURN_POINTER(&sbsroutines);
577 }
578