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-2024, 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 : const 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 4044 : setup_parse_fixed_parameters(ParseState *pstate,
68 : const Oid *paramTypes, int numParams)
69 : {
70 4044 : FixedParamState *parstate = palloc(sizeof(FixedParamState));
71 :
72 4044 : parstate->paramTypes = paramTypes;
73 4044 : parstate->numParams = numParams;
74 4044 : pstate->p_ref_hook_state = (void *) parstate;
75 4044 : pstate->p_paramref_hook = fixed_paramref_hook;
76 : /* no need to use p_coerce_param_hook */
77 4044 : }
78 :
79 : /*
80 : * Set up to process a query containing references to variable parameters.
81 : */
82 : void
83 11722 : setup_parse_variable_parameters(ParseState *pstate,
84 : Oid **paramTypes, int *numParams)
85 : {
86 11722 : VarParamState *parstate = palloc(sizeof(VarParamState));
87 :
88 11722 : parstate->paramTypes = paramTypes;
89 11722 : parstate->numParams = numParams;
90 11722 : pstate->p_ref_hook_state = (void *) parstate;
91 11722 : pstate->p_paramref_hook = variable_paramref_hook;
92 11722 : pstate->p_coerce_param_hook = variable_coerce_param_hook;
93 11722 : }
94 :
95 : /*
96 : * Transform a ParamRef using fixed parameter types.
97 : */
98 : static Node *
99 5880 : fixed_paramref_hook(ParseState *pstate, ParamRef *pref)
100 : {
101 5880 : FixedParamState *parstate = (FixedParamState *) pstate->p_ref_hook_state;
102 5880 : int paramno = pref->number;
103 : Param *param;
104 :
105 : /* Check parameter number is valid */
106 5880 : if (paramno <= 0 || paramno > parstate->numParams ||
107 5880 : !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 5880 : param = makeNode(Param);
114 5880 : param->paramkind = PARAM_EXTERN;
115 5880 : param->paramid = paramno;
116 5880 : param->paramtype = parstate->paramTypes[paramno - 1];
117 5880 : param->paramtypmod = -1;
118 5880 : param->paramcollid = get_typcollation(param->paramtype);
119 5880 : param->location = pref->location;
120 :
121 5880 : 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 13664 : variable_paramref_hook(ParseState *pstate, ParamRef *pref)
132 : {
133 13664 : VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
134 13664 : int paramno = pref->number;
135 : Oid *pptype;
136 : Param *param;
137 :
138 : /* Check parameter number is in range */
139 13664 : 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 13664 : if (paramno > *parstate->numParams)
145 : {
146 : /* Need to enlarge param array */
147 11826 : if (*parstate->paramTypes)
148 3702 : *parstate->paramTypes = repalloc0_array(*parstate->paramTypes, Oid,
149 : *parstate->numParams, paramno);
150 : else
151 8124 : *parstate->paramTypes = palloc0_array(Oid, paramno);
152 11826 : *parstate->numParams = paramno;
153 : }
154 :
155 : /* Locate param's slot in array */
156 13664 : pptype = &(*parstate->paramTypes)[paramno - 1];
157 :
158 : /* If not seen before, initialize to UNKNOWN type */
159 13664 : if (*pptype == InvalidOid)
160 12026 : *pptype = UNKNOWNOID;
161 :
162 : /*
163 : * If the argument is of type void and it's procedure call, interpret it
164 : * as unknown. This allows the JDBC driver to not have to distinguish
165 : * function and procedure calls. See also another component of this hack
166 : * in ParseFuncOrColumn().
167 : */
168 13664 : if (*pptype == VOIDOID && pstate->p_expr_kind == EXPR_KIND_CALL_ARGUMENT)
169 0 : *pptype = UNKNOWNOID;
170 :
171 13664 : param = makeNode(Param);
172 13664 : param->paramkind = PARAM_EXTERN;
173 13664 : param->paramid = paramno;
174 13664 : param->paramtype = *pptype;
175 13664 : param->paramtypmod = -1;
176 13664 : param->paramcollid = get_typcollation(param->paramtype);
177 13664 : param->location = pref->location;
178 :
179 13664 : return (Node *) param;
180 : }
181 :
182 : /*
183 : * Coerce a Param to a query-requested datatype, in the varparams case.
184 : */
185 : static Node *
186 12064 : variable_coerce_param_hook(ParseState *pstate, Param *param,
187 : Oid targetTypeId, int32 targetTypeMod,
188 : int location)
189 : {
190 12064 : if (param->paramkind == PARAM_EXTERN && param->paramtype == UNKNOWNOID)
191 : {
192 : /*
193 : * Input is a Param of previously undetermined type, and we want to
194 : * update our knowledge of the Param's type.
195 : */
196 12032 : VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
197 12032 : Oid *paramTypes = *parstate->paramTypes;
198 12032 : int paramno = param->paramid;
199 :
200 12032 : if (paramno <= 0 || /* shouldn't happen, but... */
201 12032 : paramno > *parstate->numParams)
202 0 : ereport(ERROR,
203 : (errcode(ERRCODE_UNDEFINED_PARAMETER),
204 : errmsg("there is no parameter $%d", paramno),
205 : parser_errposition(pstate, param->location)));
206 :
207 12032 : if (paramTypes[paramno - 1] == UNKNOWNOID)
208 : {
209 : /* We've successfully resolved the type */
210 12032 : paramTypes[paramno - 1] = targetTypeId;
211 : }
212 0 : else if (paramTypes[paramno - 1] == targetTypeId)
213 : {
214 : /* We previously resolved the type, and it matches */
215 : }
216 : else
217 : {
218 : /* Oops */
219 0 : ereport(ERROR,
220 : (errcode(ERRCODE_AMBIGUOUS_PARAMETER),
221 : errmsg("inconsistent types deduced for parameter $%d",
222 : paramno),
223 : errdetail("%s versus %s",
224 : format_type_be(paramTypes[paramno - 1]),
225 : format_type_be(targetTypeId)),
226 : parser_errposition(pstate, param->location)));
227 : }
228 :
229 12032 : param->paramtype = targetTypeId;
230 :
231 : /*
232 : * Note: it is tempting here to set the Param's paramtypmod to
233 : * targetTypeMod, but that is probably unwise because we have no
234 : * infrastructure that enforces that the value delivered for a Param
235 : * will match any particular typmod. Leaving it -1 ensures that a
236 : * run-time length check/coercion will occur if needed.
237 : */
238 12032 : param->paramtypmod = -1;
239 :
240 : /*
241 : * This module always sets a Param's collation to be the default for
242 : * its datatype. If that's not what you want, you should be using the
243 : * more general parser substitution hooks.
244 : */
245 12032 : param->paramcollid = get_typcollation(param->paramtype);
246 :
247 : /* Use the leftmost of the param's and coercion's locations */
248 12032 : if (location >= 0 &&
249 1156 : (param->location < 0 || location < param->location))
250 0 : param->location = location;
251 :
252 12032 : return (Node *) param;
253 : }
254 :
255 : /* Else signal to proceed with normal coercion */
256 32 : return NULL;
257 : }
258 :
259 : /*
260 : * Check for consistent assignment of variable parameters after completion
261 : * of parsing with parse_variable_parameters.
262 : *
263 : * Note: this code intentionally does not check that all parameter positions
264 : * were used, nor that all got non-UNKNOWN types assigned. Caller of parser
265 : * should enforce that if it's important.
266 : */
267 : void
268 11706 : check_variable_parameters(ParseState *pstate, Query *query)
269 : {
270 11706 : VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
271 :
272 : /* If numParams is zero then no Params were generated, so no work */
273 11706 : if (*parstate->numParams > 0)
274 9570 : (void) query_tree_walker(query,
275 : check_parameter_resolution_walker,
276 : (void *) pstate, 0);
277 11706 : }
278 :
279 : /*
280 : * Traverse a fully-analyzed tree to verify that parameter symbols
281 : * match their types. We need this because some Params might still
282 : * be UNKNOWN, if there wasn't anything to force their coercion,
283 : * and yet other instances seen later might have gotten coerced.
284 : */
285 : static bool
286 245224 : check_parameter_resolution_walker(Node *node, ParseState *pstate)
287 : {
288 245224 : if (node == NULL)
289 114570 : return false;
290 130654 : if (IsA(node, Param))
291 : {
292 12530 : Param *param = (Param *) node;
293 :
294 12530 : if (param->paramkind == PARAM_EXTERN)
295 : {
296 12526 : VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
297 12526 : int paramno = param->paramid;
298 :
299 12526 : if (paramno <= 0 || /* shouldn't happen, but... */
300 12526 : paramno > *parstate->numParams)
301 0 : ereport(ERROR,
302 : (errcode(ERRCODE_UNDEFINED_PARAMETER),
303 : errmsg("there is no parameter $%d", paramno),
304 : parser_errposition(pstate, param->location)));
305 :
306 12526 : if (param->paramtype != (*parstate->paramTypes)[paramno - 1])
307 0 : ereport(ERROR,
308 : (errcode(ERRCODE_AMBIGUOUS_PARAMETER),
309 : errmsg("could not determine data type of parameter $%d",
310 : paramno),
311 : parser_errposition(pstate, param->location)));
312 : }
313 12530 : return false;
314 : }
315 118124 : if (IsA(node, Query))
316 : {
317 : /* Recurse into RTE subquery or not-yet-planned sublink subquery */
318 194 : return query_tree_walker((Query *) node,
319 : check_parameter_resolution_walker,
320 : (void *) pstate, 0);
321 : }
322 117930 : return expression_tree_walker(node, check_parameter_resolution_walker,
323 : (void *) pstate);
324 : }
325 :
326 : /*
327 : * Check to see if a fully-parsed query tree contains any PARAM_EXTERN Params.
328 : */
329 : bool
330 544 : query_contains_extern_params(Query *query)
331 : {
332 544 : return query_tree_walker(query,
333 : query_contains_extern_params_walker,
334 : NULL, 0);
335 : }
336 :
337 : static bool
338 10796 : query_contains_extern_params_walker(Node *node, void *context)
339 : {
340 10796 : if (node == NULL)
341 6412 : return false;
342 4384 : if (IsA(node, Param))
343 : {
344 0 : Param *param = (Param *) node;
345 :
346 0 : if (param->paramkind == PARAM_EXTERN)
347 0 : return true;
348 0 : return false;
349 : }
350 4384 : if (IsA(node, Query))
351 : {
352 : /* Recurse into RTE subquery or not-yet-planned sublink subquery */
353 18 : return query_tree_walker((Query *) node,
354 : query_contains_extern_params_walker,
355 : context, 0);
356 : }
357 4366 : return expression_tree_walker(node, query_contains_extern_params_walker,
358 : context);
359 : }
|