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