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