Line data Source code
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-2017, 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
67 168 : parse_fixed_parameters(ParseState *pstate,
68 : Oid *paramTypes, int numParams)
69 : {
70 168 : FixedParamState *parstate = palloc(sizeof(FixedParamState));
71 :
72 168 : parstate->paramTypes = paramTypes;
73 168 : parstate->numParams = numParams;
74 168 : pstate->p_ref_hook_state = (void *) parstate;
75 168 : pstate->p_paramref_hook = fixed_paramref_hook;
76 : /* no need to use p_coerce_param_hook */
77 168 : }
78 :
79 : /*
80 : * Set up to process a query containing references to variable parameters.
81 : */
82 : void
83 36 : parse_variable_parameters(ParseState *pstate,
84 : Oid **paramTypes, int *numParams)
85 : {
86 36 : VarParamState *parstate = palloc(sizeof(VarParamState));
87 :
88 36 : parstate->paramTypes = paramTypes;
89 36 : parstate->numParams = numParams;
90 36 : pstate->p_ref_hook_state = (void *) parstate;
91 36 : pstate->p_paramref_hook = variable_paramref_hook;
92 36 : pstate->p_coerce_param_hook = variable_coerce_param_hook;
93 36 : }
94 :
95 : /*
96 : * Transform a ParamRef using fixed parameter types.
97 : */
98 : static Node *
99 263 : fixed_paramref_hook(ParseState *pstate, ParamRef *pref)
100 : {
101 263 : FixedParamState *parstate = (FixedParamState *) pstate->p_ref_hook_state;
102 263 : int paramno = pref->number;
103 : Param *param;
104 :
105 : /* Check parameter number is valid */
106 526 : if (paramno <= 0 || paramno > parstate->numParams ||
107 263 : !OidIsValid(parstate->paramTypes[paramno - 1]))
108 0 : ereport(ERROR,
109 : (errcode(ERRCODE_UNDEFINED_PARAMETER),
110 : errmsg("there is no parameter $%d", paramno),
111 : parser_errposition(pstate, pref->location)));
112 :
113 263 : param = makeNode(Param);
114 263 : param->paramkind = PARAM_EXTERN;
115 263 : param->paramid = paramno;
116 263 : param->paramtype = parstate->paramTypes[paramno - 1];
117 263 : param->paramtypmod = -1;
118 263 : param->paramcollid = get_typcollation(param->paramtype);
119 263 : param->location = pref->location;
120 :
121 263 : 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 *
131 21 : variable_paramref_hook(ParseState *pstate, ParamRef *pref)
132 : {
133 21 : VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
134 21 : int paramno = pref->number;
135 : Oid *pptype;
136 : Param *param;
137 :
138 : /* Check parameter number is in range */
139 21 : if (paramno <= 0 || paramno > INT_MAX / sizeof(Oid))
140 0 : ereport(ERROR,
141 : (errcode(ERRCODE_UNDEFINED_PARAMETER),
142 : errmsg("there is no parameter $%d", paramno),
143 : parser_errposition(pstate, pref->location)));
144 21 : if (paramno > *parstate->numParams)
145 : {
146 : /* Need to enlarge param array */
147 3 : if (*parstate->paramTypes)
148 1 : *parstate->paramTypes = (Oid *) repalloc(*parstate->paramTypes,
149 : paramno * sizeof(Oid));
150 : else
151 2 : *parstate->paramTypes = (Oid *) palloc(paramno * sizeof(Oid));
152 : /* Zero out the previously-unreferenced slots */
153 3 : MemSet(*parstate->paramTypes + *parstate->numParams,
154 : 0,
155 : (paramno - *parstate->numParams) * sizeof(Oid));
156 3 : *parstate->numParams = paramno;
157 : }
158 :
159 : /* Locate param's slot in array */
160 21 : pptype = &(*parstate->paramTypes)[paramno - 1];
161 :
162 : /* If not seen before, initialize to UNKNOWN type */
163 21 : if (*pptype == InvalidOid)
164 3 : *pptype = UNKNOWNOID;
165 :
166 21 : param = makeNode(Param);
167 21 : param->paramkind = PARAM_EXTERN;
168 21 : param->paramid = paramno;
169 21 : param->paramtype = *pptype;
170 21 : param->paramtypmod = -1;
171 21 : param->paramcollid = get_typcollation(param->paramtype);
172 21 : param->location = pref->location;
173 :
174 21 : return (Node *) param;
175 : }
176 :
177 : /*
178 : * Coerce a Param to a query-requested datatype, in the varparams case.
179 : */
180 : static Node *
181 7 : variable_coerce_param_hook(ParseState *pstate, Param *param,
182 : Oid targetTypeId, int32 targetTypeMod,
183 : int location)
184 : {
185 7 : 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 4 : VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
192 4 : Oid *paramTypes = *parstate->paramTypes;
193 4 : int paramno = param->paramid;
194 :
195 8 : if (paramno <= 0 || /* shouldn't happen, but... */
196 4 : paramno > *parstate->numParams)
197 0 : ereport(ERROR,
198 : (errcode(ERRCODE_UNDEFINED_PARAMETER),
199 : errmsg("there is no parameter $%d", paramno),
200 : parser_errposition(pstate, param->location)));
201 :
202 4 : if (paramTypes[paramno - 1] == UNKNOWNOID)
203 : {
204 : /* We've successfully resolved the type */
205 4 : paramTypes[paramno - 1] = targetTypeId;
206 : }
207 0 : else if (paramTypes[paramno - 1] == targetTypeId)
208 : {
209 : /* We previously resolved the type, and it matches */
210 : }
211 : else
212 : {
213 : /* Oops */
214 0 : 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 4 : 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 4 : 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 4 : param->paramcollid = get_typcollation(param->paramtype);
241 :
242 : /* Use the leftmost of the param's and coercion's locations */
243 5 : if (location >= 0 &&
244 2 : (param->location < 0 || location < param->location))
245 0 : param->location = location;
246 :
247 4 : return (Node *) param;
248 : }
249 :
250 : /* Else signal to proceed with normal coercion */
251 3 : 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
263 35 : check_variable_parameters(ParseState *pstate, Query *query)
264 : {
265 35 : VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
266 :
267 : /* If numParams is zero then no Params were generated, so no work */
268 35 : if (*parstate->numParams > 0)
269 13 : (void) query_tree_walker(query,
270 : check_parameter_resolution_walker,
271 : (void *) pstate, 0);
272 35 : }
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
281 442 : check_parameter_resolution_walker(Node *node, ParseState *pstate)
282 : {
283 442 : if (node == NULL)
284 149 : return false;
285 293 : if (IsA(node, Param))
286 : {
287 21 : Param *param = (Param *) node;
288 :
289 21 : if (param->paramkind == PARAM_EXTERN)
290 : {
291 21 : VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
292 21 : int paramno = param->paramid;
293 :
294 42 : if (paramno <= 0 || /* shouldn't happen, but... */
295 21 : paramno > *parstate->numParams)
296 0 : ereport(ERROR,
297 : (errcode(ERRCODE_UNDEFINED_PARAMETER),
298 : errmsg("there is no parameter $%d", paramno),
299 : parser_errposition(pstate, param->location)));
300 :
301 21 : if (param->paramtype != (*parstate->paramTypes)[paramno - 1])
302 0 : 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 21 : return false;
309 : }
310 272 : if (IsA(node, Query))
311 : {
312 : /* Recurse into RTE subquery or not-yet-planned sublink subquery */
313 1 : return query_tree_walker((Query *) node,
314 : check_parameter_resolution_walker,
315 : (void *) pstate, 0);
316 : }
317 271 : 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
325 34 : query_contains_extern_params(Query *query)
326 : {
327 34 : return query_tree_walker(query,
328 : query_contains_extern_params_walker,
329 : NULL, 0);
330 : }
331 :
332 : static bool
333 700 : query_contains_extern_params_walker(Node *node, void *context)
334 : {
335 700 : if (node == NULL)
336 403 : return false;
337 297 : if (IsA(node, Param))
338 : {
339 0 : Param *param = (Param *) node;
340 :
341 0 : if (param->paramkind == PARAM_EXTERN)
342 0 : return true;
343 0 : return false;
344 : }
345 297 : if (IsA(node, Query))
346 : {
347 : /* Recurse into RTE subquery or not-yet-planned sublink subquery */
348 3 : return query_tree_walker((Query *) node,
349 : query_contains_extern_params_walker,
350 : context, 0);
351 : }
352 294 : return expression_tree_walker(node, query_contains_extern_params_walker,
353 : context);
354 : }
|