1 /*-------------------------------------------------------------------------
2  *
3  * parse_param.c
4  *	  handle parameters in parser
5  *
6  * This code covers two cases that are used within the core backend:
7  *		* a fixed list of parameters with known types
8  *		* an expandable list of parameters whose types can optionally
9  *		  be determined from context
10  * In both cases, only explicit $n references (ParamRef nodes) are supported.
11  *
12  * Note that other approaches to parameters are possible using the parser
13  * hooks defined in ParseState.
14  *
15  * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
16  * Portions Copyright (c) 1994, Regents of the University of California
17  *
18  *
19  * IDENTIFICATION
20  *	  src/backend/parser/parse_param.c
21  *
22  *-------------------------------------------------------------------------
23  */
24 
25 #include "postgres.h"
26 
27 #include <limits.h>
28 
29 #include "catalog/pg_type.h"
30 #include "nodes/nodeFuncs.h"
31 #include "parser/parse_param.h"
32 #include "utils/builtins.h"
33 #include "utils/lsyscache.h"
34 
35 
36 typedef struct FixedParamState
37 {
38 	Oid		   *paramTypes;		/* array of parameter type OIDs */
39 	int			numParams;		/* number of array entries */
40 } FixedParamState;
41 
42 /*
43  * In the varparams case, the caller-supplied OID array (if any) can be
44  * re-palloc'd larger at need.  A zero array entry means that parameter number
45  * hasn't been seen, while UNKNOWNOID means the parameter has been used but
46  * its type is not yet known.
47  */
48 typedef struct VarParamState
49 {
50 	Oid		  **paramTypes;		/* array of parameter type OIDs */
51 	int		   *numParams;		/* number of array entries */
52 } VarParamState;
53 
54 static Node *fixed_paramref_hook(ParseState *pstate, ParamRef *pref);
55 static Node *variable_paramref_hook(ParseState *pstate, ParamRef *pref);
56 static Node *variable_coerce_param_hook(ParseState *pstate, Param *param,
57 										Oid targetTypeId, int32 targetTypeMod,
58 										int location);
59 static bool check_parameter_resolution_walker(Node *node, ParseState *pstate);
60 static bool query_contains_extern_params_walker(Node *node, void *context);
61 
62 
63 /*
64  * Set up to process a query containing references to fixed parameters.
65  */
66 void
parse_fixed_parameters(ParseState * pstate,Oid * paramTypes,int numParams)67 parse_fixed_parameters(ParseState *pstate,
68 					   Oid *paramTypes, int numParams)
69 {
70 	FixedParamState *parstate = palloc(sizeof(FixedParamState));
71 
72 	parstate->paramTypes = paramTypes;
73 	parstate->numParams = numParams;
74 	pstate->p_ref_hook_state = (void *) parstate;
75 	pstate->p_paramref_hook = fixed_paramref_hook;
76 	/* no need to use p_coerce_param_hook */
77 }
78 
79 /*
80  * Set up to process a query containing references to variable parameters.
81  */
82 void
parse_variable_parameters(ParseState * pstate,Oid ** paramTypes,int * numParams)83 parse_variable_parameters(ParseState *pstate,
84 						  Oid **paramTypes, int *numParams)
85 {
86 	VarParamState *parstate = palloc(sizeof(VarParamState));
87 
88 	parstate->paramTypes = paramTypes;
89 	parstate->numParams = numParams;
90 	pstate->p_ref_hook_state = (void *) parstate;
91 	pstate->p_paramref_hook = variable_paramref_hook;
92 	pstate->p_coerce_param_hook = variable_coerce_param_hook;
93 }
94 
95 /*
96  * Transform a ParamRef using fixed parameter types.
97  */
98 static Node *
fixed_paramref_hook(ParseState * pstate,ParamRef * pref)99 fixed_paramref_hook(ParseState *pstate, ParamRef *pref)
100 {
101 	FixedParamState *parstate = (FixedParamState *) pstate->p_ref_hook_state;
102 	int			paramno = pref->number;
103 	Param	   *param;
104 
105 	/* Check parameter number is valid */
106 	if (paramno <= 0 || paramno > parstate->numParams ||
107 		!OidIsValid(parstate->paramTypes[paramno - 1]))
108 		ereport(ERROR,
109 				(errcode(ERRCODE_UNDEFINED_PARAMETER),
110 				 errmsg("there is no parameter $%d", paramno),
111 				 parser_errposition(pstate, pref->location)));
112 
113 	param = makeNode(Param);
114 	param->paramkind = PARAM_EXTERN;
115 	param->paramid = paramno;
116 	param->paramtype = parstate->paramTypes[paramno - 1];
117 	param->paramtypmod = -1;
118 	param->paramcollid = get_typcollation(param->paramtype);
119 	param->location = pref->location;
120 
121 	return (Node *) param;
122 }
123 
124 /*
125  * Transform a ParamRef using variable parameter types.
126  *
127  * The only difference here is we must enlarge the parameter type array
128  * as needed.
129  */
130 static Node *
variable_paramref_hook(ParseState * pstate,ParamRef * pref)131 variable_paramref_hook(ParseState *pstate, ParamRef *pref)
132 {
133 	VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
134 	int			paramno = pref->number;
135 	Oid		   *pptype;
136 	Param	   *param;
137 
138 	/* Check parameter number is in range */
139 	if (paramno <= 0 || paramno > INT_MAX / sizeof(Oid))
140 		ereport(ERROR,
141 				(errcode(ERRCODE_UNDEFINED_PARAMETER),
142 				 errmsg("there is no parameter $%d", paramno),
143 				 parser_errposition(pstate, pref->location)));
144 	if (paramno > *parstate->numParams)
145 	{
146 		/* Need to enlarge param array */
147 		if (*parstate->paramTypes)
148 			*parstate->paramTypes = (Oid *) repalloc(*parstate->paramTypes,
149 													 paramno * sizeof(Oid));
150 		else
151 			*parstate->paramTypes = (Oid *) palloc(paramno * sizeof(Oid));
152 		/* Zero out the previously-unreferenced slots */
153 		MemSet(*parstate->paramTypes + *parstate->numParams,
154 			   0,
155 			   (paramno - *parstate->numParams) * sizeof(Oid));
156 		*parstate->numParams = paramno;
157 	}
158 
159 	/* Locate param's slot in array */
160 	pptype = &(*parstate->paramTypes)[paramno - 1];
161 
162 	/* If not seen before, initialize to UNKNOWN type */
163 	if (*pptype == InvalidOid)
164 		*pptype = UNKNOWNOID;
165 
166 	/*
167 	 * If the argument is of type void and it's procedure call, interpret it
168 	 * as unknown.  This allows the JDBC driver to not have to distinguish
169 	 * function and procedure calls.  See also another component of this hack
170 	 * in ParseFuncOrColumn().
171 	 */
172 	if (*pptype == VOIDOID && pstate->p_expr_kind == EXPR_KIND_CALL_ARGUMENT)
173 		*pptype = UNKNOWNOID;
174 
175 	param = makeNode(Param);
176 	param->paramkind = PARAM_EXTERN;
177 	param->paramid = paramno;
178 	param->paramtype = *pptype;
179 	param->paramtypmod = -1;
180 	param->paramcollid = get_typcollation(param->paramtype);
181 	param->location = pref->location;
182 
183 	return (Node *) param;
184 }
185 
186 /*
187  * Coerce a Param to a query-requested datatype, in the varparams case.
188  */
189 static Node *
variable_coerce_param_hook(ParseState * pstate,Param * param,Oid targetTypeId,int32 targetTypeMod,int location)190 variable_coerce_param_hook(ParseState *pstate, Param *param,
191 						   Oid targetTypeId, int32 targetTypeMod,
192 						   int location)
193 {
194 	if (param->paramkind == PARAM_EXTERN && param->paramtype == UNKNOWNOID)
195 	{
196 		/*
197 		 * Input is a Param of previously undetermined type, and we want to
198 		 * update our knowledge of the Param's type.
199 		 */
200 		VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
201 		Oid		   *paramTypes = *parstate->paramTypes;
202 		int			paramno = param->paramid;
203 
204 		if (paramno <= 0 ||		/* shouldn't happen, but... */
205 			paramno > *parstate->numParams)
206 			ereport(ERROR,
207 					(errcode(ERRCODE_UNDEFINED_PARAMETER),
208 					 errmsg("there is no parameter $%d", paramno),
209 					 parser_errposition(pstate, param->location)));
210 
211 		if (paramTypes[paramno - 1] == UNKNOWNOID)
212 		{
213 			/* We've successfully resolved the type */
214 			paramTypes[paramno - 1] = targetTypeId;
215 		}
216 		else if (paramTypes[paramno - 1] == targetTypeId)
217 		{
218 			/* We previously resolved the type, and it matches */
219 		}
220 		else
221 		{
222 			/* Oops */
223 			ereport(ERROR,
224 					(errcode(ERRCODE_AMBIGUOUS_PARAMETER),
225 					 errmsg("inconsistent types deduced for parameter $%d",
226 							paramno),
227 					 errdetail("%s versus %s",
228 							   format_type_be(paramTypes[paramno - 1]),
229 							   format_type_be(targetTypeId)),
230 					 parser_errposition(pstate, param->location)));
231 		}
232 
233 		param->paramtype = targetTypeId;
234 
235 		/*
236 		 * Note: it is tempting here to set the Param's paramtypmod to
237 		 * targetTypeMod, but that is probably unwise because we have no
238 		 * infrastructure that enforces that the value delivered for a Param
239 		 * will match any particular typmod.  Leaving it -1 ensures that a
240 		 * run-time length check/coercion will occur if needed.
241 		 */
242 		param->paramtypmod = -1;
243 
244 		/*
245 		 * This module always sets a Param's collation to be the default for
246 		 * its datatype.  If that's not what you want, you should be using the
247 		 * more general parser substitution hooks.
248 		 */
249 		param->paramcollid = get_typcollation(param->paramtype);
250 
251 		/* Use the leftmost of the param's and coercion's locations */
252 		if (location >= 0 &&
253 			(param->location < 0 || location < param->location))
254 			param->location = location;
255 
256 		return (Node *) param;
257 	}
258 
259 	/* Else signal to proceed with normal coercion */
260 	return NULL;
261 }
262 
263 /*
264  * Check for consistent assignment of variable parameters after completion
265  * of parsing with parse_variable_parameters.
266  *
267  * Note: this code intentionally does not check that all parameter positions
268  * were used, nor that all got non-UNKNOWN types assigned.  Caller of parser
269  * should enforce that if it's important.
270  */
271 void
check_variable_parameters(ParseState * pstate,Query * query)272 check_variable_parameters(ParseState *pstate, Query *query)
273 {
274 	VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
275 
276 	/* If numParams is zero then no Params were generated, so no work */
277 	if (*parstate->numParams > 0)
278 		(void) query_tree_walker(query,
279 								 check_parameter_resolution_walker,
280 								 (void *) pstate, 0);
281 }
282 
283 /*
284  * Traverse a fully-analyzed tree to verify that parameter symbols
285  * match their types.  We need this because some Params might still
286  * be UNKNOWN, if there wasn't anything to force their coercion,
287  * and yet other instances seen later might have gotten coerced.
288  */
289 static bool
check_parameter_resolution_walker(Node * node,ParseState * pstate)290 check_parameter_resolution_walker(Node *node, ParseState *pstate)
291 {
292 	if (node == NULL)
293 		return false;
294 	if (IsA(node, Param))
295 	{
296 		Param	   *param = (Param *) node;
297 
298 		if (param->paramkind == PARAM_EXTERN)
299 		{
300 			VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
301 			int			paramno = param->paramid;
302 
303 			if (paramno <= 0 || /* shouldn't happen, but... */
304 				paramno > *parstate->numParams)
305 				ereport(ERROR,
306 						(errcode(ERRCODE_UNDEFINED_PARAMETER),
307 						 errmsg("there is no parameter $%d", paramno),
308 						 parser_errposition(pstate, param->location)));
309 
310 			if (param->paramtype != (*parstate->paramTypes)[paramno - 1])
311 				ereport(ERROR,
312 						(errcode(ERRCODE_AMBIGUOUS_PARAMETER),
313 						 errmsg("could not determine data type of parameter $%d",
314 								paramno),
315 						 parser_errposition(pstate, param->location)));
316 		}
317 		return false;
318 	}
319 	if (IsA(node, Query))
320 	{
321 		/* Recurse into RTE subquery or not-yet-planned sublink subquery */
322 		return query_tree_walker((Query *) node,
323 								 check_parameter_resolution_walker,
324 								 (void *) pstate, 0);
325 	}
326 	return expression_tree_walker(node, check_parameter_resolution_walker,
327 								  (void *) pstate);
328 }
329 
330 /*
331  * Check to see if a fully-parsed query tree contains any PARAM_EXTERN Params.
332  */
333 bool
query_contains_extern_params(Query * query)334 query_contains_extern_params(Query *query)
335 {
336 	return query_tree_walker(query,
337 							 query_contains_extern_params_walker,
338 							 NULL, 0);
339 }
340 
341 static bool
query_contains_extern_params_walker(Node * node,void * context)342 query_contains_extern_params_walker(Node *node, void *context)
343 {
344 	if (node == NULL)
345 		return false;
346 	if (IsA(node, Param))
347 	{
348 		Param	   *param = (Param *) node;
349 
350 		if (param->paramkind == PARAM_EXTERN)
351 			return true;
352 		return false;
353 	}
354 	if (IsA(node, Query))
355 	{
356 		/* Recurse into RTE subquery or not-yet-planned sublink subquery */
357 		return query_tree_walker((Query *) node,
358 								 query_contains_extern_params_walker,
359 								 context, 0);
360 	}
361 	return expression_tree_walker(node, query_contains_extern_params_walker,
362 								  context);
363 }
364