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-2016, 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 	param = makeNode(Param);
167 	param->paramkind = PARAM_EXTERN;
168 	param->paramid = paramno;
169 	param->paramtype = *pptype;
170 	param->paramtypmod = -1;
171 	param->paramcollid = get_typcollation(param->paramtype);
172 	param->location = pref->location;
173 
174 	return (Node *) param;
175 }
176 
177 /*
178  * Coerce a Param to a query-requested datatype, in the varparams case.
179  */
180 static Node *
variable_coerce_param_hook(ParseState * pstate,Param * param,Oid targetTypeId,int32 targetTypeMod,int location)181 variable_coerce_param_hook(ParseState *pstate, Param *param,
182 						   Oid targetTypeId, int32 targetTypeMod,
183 						   int location)
184 {
185 	if (param->paramkind == PARAM_EXTERN && param->paramtype == UNKNOWNOID)
186 	{
187 		/*
188 		 * Input is a Param of previously undetermined type, and we want to
189 		 * update our knowledge of the Param's type.
190 		 */
191 		VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
192 		Oid		   *paramTypes = *parstate->paramTypes;
193 		int			paramno = param->paramid;
194 
195 		if (paramno <= 0 ||		/* shouldn't happen, but... */
196 			paramno > *parstate->numParams)
197 			ereport(ERROR,
198 					(errcode(ERRCODE_UNDEFINED_PARAMETER),
199 					 errmsg("there is no parameter $%d", paramno),
200 					 parser_errposition(pstate, param->location)));
201 
202 		if (paramTypes[paramno - 1] == UNKNOWNOID)
203 		{
204 			/* We've successfully resolved the type */
205 			paramTypes[paramno - 1] = targetTypeId;
206 		}
207 		else if (paramTypes[paramno - 1] == targetTypeId)
208 		{
209 			/* We previously resolved the type, and it matches */
210 		}
211 		else
212 		{
213 			/* Ooops */
214 			ereport(ERROR,
215 					(errcode(ERRCODE_AMBIGUOUS_PARAMETER),
216 					 errmsg("inconsistent types deduced for parameter $%d",
217 							paramno),
218 					 errdetail("%s versus %s",
219 							   format_type_be(paramTypes[paramno - 1]),
220 							   format_type_be(targetTypeId)),
221 					 parser_errposition(pstate, param->location)));
222 		}
223 
224 		param->paramtype = targetTypeId;
225 
226 		/*
227 		 * Note: it is tempting here to set the Param's paramtypmod to
228 		 * targetTypeMod, but that is probably unwise because we have no
229 		 * infrastructure that enforces that the value delivered for a Param
230 		 * will match any particular typmod.  Leaving it -1 ensures that a
231 		 * run-time length check/coercion will occur if needed.
232 		 */
233 		param->paramtypmod = -1;
234 
235 		/*
236 		 * This module always sets a Param's collation to be the default for
237 		 * its datatype.  If that's not what you want, you should be using the
238 		 * more general parser substitution hooks.
239 		 */
240 		param->paramcollid = get_typcollation(param->paramtype);
241 
242 		/* Use the leftmost of the param's and coercion's locations */
243 		if (location >= 0 &&
244 			(param->location < 0 || location < param->location))
245 			param->location = location;
246 
247 		return (Node *) param;
248 	}
249 
250 	/* Else signal to proceed with normal coercion */
251 	return NULL;
252 }
253 
254 /*
255  * Check for consistent assignment of variable parameters after completion
256  * of parsing with parse_variable_parameters.
257  *
258  * Note: this code intentionally does not check that all parameter positions
259  * were used, nor that all got non-UNKNOWN types assigned.  Caller of parser
260  * should enforce that if it's important.
261  */
262 void
check_variable_parameters(ParseState * pstate,Query * query)263 check_variable_parameters(ParseState *pstate, Query *query)
264 {
265 	VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
266 
267 	/* If numParams is zero then no Params were generated, so no work */
268 	if (*parstate->numParams > 0)
269 		(void) query_tree_walker(query,
270 								 check_parameter_resolution_walker,
271 								 (void *) pstate, 0);
272 }
273 
274 /*
275  * Traverse a fully-analyzed tree to verify that parameter symbols
276  * match their types.  We need this because some Params might still
277  * be UNKNOWN, if there wasn't anything to force their coercion,
278  * and yet other instances seen later might have gotten coerced.
279  */
280 static bool
check_parameter_resolution_walker(Node * node,ParseState * pstate)281 check_parameter_resolution_walker(Node *node, ParseState *pstate)
282 {
283 	if (node == NULL)
284 		return false;
285 	if (IsA(node, Param))
286 	{
287 		Param	   *param = (Param *) node;
288 
289 		if (param->paramkind == PARAM_EXTERN)
290 		{
291 			VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
292 			int			paramno = param->paramid;
293 
294 			if (paramno <= 0 || /* shouldn't happen, but... */
295 				paramno > *parstate->numParams)
296 				ereport(ERROR,
297 						(errcode(ERRCODE_UNDEFINED_PARAMETER),
298 						 errmsg("there is no parameter $%d", paramno),
299 						 parser_errposition(pstate, param->location)));
300 
301 			if (param->paramtype != (*parstate->paramTypes)[paramno - 1])
302 				ereport(ERROR,
303 						(errcode(ERRCODE_AMBIGUOUS_PARAMETER),
304 					 errmsg("could not determine data type of parameter $%d",
305 							paramno),
306 						 parser_errposition(pstate, param->location)));
307 		}
308 		return false;
309 	}
310 	if (IsA(node, Query))
311 	{
312 		/* Recurse into RTE subquery or not-yet-planned sublink subquery */
313 		return query_tree_walker((Query *) node,
314 								 check_parameter_resolution_walker,
315 								 (void *) pstate, 0);
316 	}
317 	return expression_tree_walker(node, check_parameter_resolution_walker,
318 								  (void *) pstate);
319 }
320 
321 /*
322  * Check to see if a fully-parsed query tree contains any PARAM_EXTERN Params.
323  */
324 bool
query_contains_extern_params(Query * query)325 query_contains_extern_params(Query *query)
326 {
327 	return query_tree_walker(query,
328 							 query_contains_extern_params_walker,
329 							 NULL, 0);
330 }
331 
332 static bool
query_contains_extern_params_walker(Node * node,void * context)333 query_contains_extern_params_walker(Node *node, void *context)
334 {
335 	if (node == NULL)
336 		return false;
337 	if (IsA(node, Param))
338 	{
339 		Param	   *param = (Param *) node;
340 
341 		if (param->paramkind == PARAM_EXTERN)
342 			return true;
343 		return false;
344 	}
345 	if (IsA(node, Query))
346 	{
347 		/* Recurse into RTE subquery or not-yet-planned sublink subquery */
348 		return query_tree_walker((Query *) node,
349 								 query_contains_extern_params_walker,
350 								 context, 0);
351 	}
352 	return expression_tree_walker(node, query_contains_extern_params_walker,
353 								  context);
354 }
355