Age Owner Branch data TLA 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
772 peter@eisentraut.org 67 :CBC 2030 : setup_parse_fixed_parameters(ParseState *pstate,
68 : : const Oid *paramTypes, int numParams)
69 : : {
5279 tgl@sss.pgh.pa.us 70 : 2030 : FixedParamState *parstate = palloc(sizeof(FixedParamState));
71 : :
72 : 2030 : parstate->paramTypes = paramTypes;
73 : 2030 : parstate->numParams = numParams;
74 : 2030 : pstate->p_ref_hook_state = (void *) parstate;
75 : 2030 : pstate->p_paramref_hook = fixed_paramref_hook;
76 : : /* no need to use p_coerce_param_hook */
77 : 2030 : }
78 : :
79 : : /*
80 : : * Set up to process a query containing references to variable parameters.
81 : : */
82 : : void
772 peter@eisentraut.org 83 : 4591 : setup_parse_variable_parameters(ParseState *pstate,
84 : : Oid **paramTypes, int *numParams)
85 : : {
5279 tgl@sss.pgh.pa.us 86 : 4591 : VarParamState *parstate = palloc(sizeof(VarParamState));
87 : :
88 : 4591 : parstate->paramTypes = paramTypes;
89 : 4591 : parstate->numParams = numParams;
90 : 4591 : pstate->p_ref_hook_state = (void *) parstate;
91 : 4591 : pstate->p_paramref_hook = variable_paramref_hook;
92 : 4591 : pstate->p_coerce_param_hook = variable_coerce_param_hook;
93 : 4591 : }
94 : :
95 : : /*
96 : : * Transform a ParamRef using fixed parameter types.
97 : : */
98 : : static Node *
99 : 2925 : fixed_paramref_hook(ParseState *pstate, ParamRef *pref)
100 : : {
101 : 2925 : FixedParamState *parstate = (FixedParamState *) pstate->p_ref_hook_state;
102 : 2925 : int paramno = pref->number;
103 : : Param *param;
104 : :
105 : : /* Check parameter number is valid */
5205 106 [ + - + - ]: 2925 : if (paramno <= 0 || paramno > parstate->numParams ||
107 [ - + ]: 2925 : !OidIsValid(parstate->paramTypes[paramno - 1]))
5279 tgl@sss.pgh.pa.us 108 [ # # ]:UBC 0 : ereport(ERROR,
109 : : (errcode(ERRCODE_UNDEFINED_PARAMETER),
110 : : errmsg("there is no parameter $%d", paramno),
111 : : parser_errposition(pstate, pref->location)));
112 : :
5279 tgl@sss.pgh.pa.us 113 :CBC 2925 : param = makeNode(Param);
114 : 2925 : param->paramkind = PARAM_EXTERN;
115 : 2925 : param->paramid = paramno;
116 : 2925 : param->paramtype = parstate->paramTypes[paramno - 1];
117 : 2925 : param->paramtypmod = -1;
4775 118 : 2925 : param->paramcollid = get_typcollation(param->paramtype);
5279 119 : 2925 : param->location = pref->location;
120 : :
121 : 2925 : 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 : 5126 : variable_paramref_hook(ParseState *pstate, ParamRef *pref)
132 : : {
133 : 5126 : VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
134 : 5126 : int paramno = pref->number;
135 : : Oid *pptype;
136 : : Param *param;
137 : :
138 : : /* Check parameter number is in range */
139 [ + - - + ]: 5126 : if (paramno <= 0 || paramno > INT_MAX / sizeof(Oid))
5279 tgl@sss.pgh.pa.us 140 [ # # ]:UBC 0 : ereport(ERROR,
141 : : (errcode(ERRCODE_UNDEFINED_PARAMETER),
142 : : errmsg("there is no parameter $%d", paramno),
143 : : parser_errposition(pstate, pref->location)));
5279 tgl@sss.pgh.pa.us 144 [ + + ]:CBC 5126 : if (paramno > *parstate->numParams)
145 : : {
146 : : /* Need to enlarge param array */
147 [ + + ]: 4184 : if (*parstate->paramTypes)
519 peter@eisentraut.org 148 : 1394 : *parstate->paramTypes = repalloc0_array(*parstate->paramTypes, Oid,
149 : : *parstate->numParams, paramno);
150 : : else
151 : 2790 : *parstate->paramTypes = palloc0_array(Oid, paramno);
5279 tgl@sss.pgh.pa.us 152 : 4184 : *parstate->numParams = paramno;
153 : : }
154 : :
155 : : /* Locate param's slot in array */
156 : 5126 : pptype = &(*parstate->paramTypes)[paramno - 1];
157 : :
158 : : /* If not seen before, initialize to UNKNOWN type */
159 [ + + ]: 5126 : if (*pptype == InvalidOid)
160 : 4284 : *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 : : */
1265 peter@eisentraut.org 168 [ - + - - ]: 5126 : if (*pptype == VOIDOID && pstate->p_expr_kind == EXPR_KIND_CALL_ARGUMENT)
1265 peter@eisentraut.org 169 :UBC 0 : *pptype = UNKNOWNOID;
170 : :
5279 tgl@sss.pgh.pa.us 171 :CBC 5126 : param = makeNode(Param);
172 : 5126 : param->paramkind = PARAM_EXTERN;
173 : 5126 : param->paramid = paramno;
174 : 5126 : param->paramtype = *pptype;
175 : 5126 : param->paramtypmod = -1;
4775 176 : 5126 : param->paramcollid = get_typcollation(param->paramtype);
5279 177 : 5126 : param->location = pref->location;
178 : :
179 : 5126 : return (Node *) param;
180 : : }
181 : :
182 : : /*
183 : : * Coerce a Param to a query-requested datatype, in the varparams case.
184 : : */
185 : : static Node *
186 : 4303 : variable_coerce_param_hook(ParseState *pstate, Param *param,
187 : : Oid targetTypeId, int32 targetTypeMod,
188 : : int location)
189 : : {
190 [ + - + + ]: 4303 : 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 : 4287 : VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
197 : 4287 : Oid *paramTypes = *parstate->paramTypes;
198 : 4287 : int paramno = param->paramid;
199 : :
200 [ + - ]: 4287 : if (paramno <= 0 || /* shouldn't happen, but... */
201 [ - + ]: 4287 : paramno > *parstate->numParams)
5279 tgl@sss.pgh.pa.us 202 [ # # ]:UBC 0 : ereport(ERROR,
203 : : (errcode(ERRCODE_UNDEFINED_PARAMETER),
204 : : errmsg("there is no parameter $%d", paramno),
205 : : parser_errposition(pstate, param->location)));
206 : :
5279 tgl@sss.pgh.pa.us 207 [ + - ]:CBC 4287 : if (paramTypes[paramno - 1] == UNKNOWNOID)
208 : : {
209 : : /* We've successfully resolved the type */
210 : 4287 : paramTypes[paramno - 1] = targetTypeId;
211 : : }
5279 tgl@sss.pgh.pa.us 212 [ # # ]:UBC 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 : :
5279 tgl@sss.pgh.pa.us 229 :CBC 4287 : 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 : 4287 : 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 : : */
4775 245 : 4287 : param->paramcollid = get_typcollation(param->paramtype);
246 : :
247 : : /* Use the leftmost of the param's and coercion's locations */
5279 248 [ + + ]: 4287 : if (location >= 0 &&
249 [ + - - + ]: 577 : (param->location < 0 || location < param->location))
5279 tgl@sss.pgh.pa.us 250 :UBC 0 : param->location = location;
251 : :
5279 tgl@sss.pgh.pa.us 252 :CBC 4287 : return (Node *) param;
253 : : }
254 : :
255 : : /* Else signal to proceed with normal coercion */
256 : 16 : 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 : 4583 : check_variable_parameters(ParseState *pstate, Query *query)
269 : : {
270 : 4583 : VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
271 : :
272 : : /* If numParams is zero then no Params were generated, so no work */
273 [ + + ]: 4583 : if (*parstate->numParams > 0)
274 : 3541 : (void) query_tree_walker(query,
275 : : check_parameter_resolution_walker,
276 : : (void *) pstate, 0);
277 : 4583 : }
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 : 107093 : check_parameter_resolution_walker(Node *node, ParseState *pstate)
287 : : {
288 [ + + ]: 107093 : if (node == NULL)
289 : 46052 : return false;
290 [ + + ]: 61041 : if (IsA(node, Param))
291 : : {
292 : 4570 : Param *param = (Param *) node;
293 : :
294 [ + + ]: 4570 : if (param->paramkind == PARAM_EXTERN)
295 : : {
296 : 4568 : VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
297 : 4568 : int paramno = param->paramid;
298 : :
299 [ + - ]: 4568 : if (paramno <= 0 || /* shouldn't happen, but... */
300 [ - + ]: 4568 : paramno > *parstate->numParams)
5279 tgl@sss.pgh.pa.us 301 [ # # ]:UBC 0 : ereport(ERROR,
302 : : (errcode(ERRCODE_UNDEFINED_PARAMETER),
303 : : errmsg("there is no parameter $%d", paramno),
304 : : parser_errposition(pstate, param->location)));
305 : :
5279 tgl@sss.pgh.pa.us 306 [ - + ]:CBC 4568 : if (param->paramtype != (*parstate->paramTypes)[paramno - 1])
5279 tgl@sss.pgh.pa.us 307 [ # # ]:UBC 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 : : }
5279 tgl@sss.pgh.pa.us 313 :CBC 4570 : return false;
314 : : }
315 [ + + ]: 56471 : if (IsA(node, Query))
316 : : {
317 : : /* Recurse into RTE subquery or not-yet-planned sublink subquery */
318 : 98 : return query_tree_walker((Query *) node,
319 : : check_parameter_resolution_walker,
320 : : (void *) pstate, 0);
321 : : }
322 : 56373 : 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
4020 330 : 272 : query_contains_extern_params(Query *query)
331 : : {
332 : 272 : return query_tree_walker(query,
333 : : query_contains_extern_params_walker,
334 : : NULL, 0);
335 : : }
336 : :
337 : : static bool
338 : 5679 : query_contains_extern_params_walker(Node *node, void *context)
339 : : {
340 [ + + ]: 5679 : if (node == NULL)
341 : 3487 : return false;
342 [ - + ]: 2192 : if (IsA(node, Param))
343 : : {
4020 tgl@sss.pgh.pa.us 344 :UBC 0 : Param *param = (Param *) node;
345 : :
346 [ # # ]: 0 : if (param->paramkind == PARAM_EXTERN)
347 : 0 : return true;
348 : 0 : return false;
349 : : }
4020 tgl@sss.pgh.pa.us 350 [ + + ]:CBC 2192 : if (IsA(node, Query))
351 : : {
352 : : /* Recurse into RTE subquery or not-yet-planned sublink subquery */
353 : 9 : return query_tree_walker((Query *) node,
354 : : query_contains_extern_params_walker,
355 : : context, 0);
356 : : }
357 : 2183 : return expression_tree_walker(node, query_contains_extern_params_walker,
358 : : context);
359 : : }
|