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-2025, 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
1282 peter@eisentraut.org 68 :CBC 1961 : setup_parse_fixed_parameters(ParseState *pstate,
69 : : const Oid *paramTypes, int numParams)
70 : : {
5789 tgl@sss.pgh.pa.us 71 : 1961 : FixedParamState *parstate = palloc(sizeof(FixedParamState));
72 : :
73 : 1961 : parstate->paramTypes = paramTypes;
74 : 1961 : parstate->numParams = numParams;
282 peter@eisentraut.org 75 : 1961 : pstate->p_ref_hook_state = parstate;
5789 tgl@sss.pgh.pa.us 76 : 1961 : pstate->p_paramref_hook = fixed_paramref_hook;
77 : : /* no need to use p_coerce_param_hook */
78 : 1961 : }
79 : :
80 : : /*
81 : : * Set up to process a query containing references to variable parameters.
82 : : */
83 : : void
1282 peter@eisentraut.org 84 : 5210 : setup_parse_variable_parameters(ParseState *pstate,
85 : : Oid **paramTypes, int *numParams)
86 : : {
5789 tgl@sss.pgh.pa.us 87 : 5210 : VarParamState *parstate = palloc(sizeof(VarParamState));
88 : :
89 : 5210 : parstate->paramTypes = paramTypes;
90 : 5210 : parstate->numParams = numParams;
282 peter@eisentraut.org 91 : 5210 : pstate->p_ref_hook_state = parstate;
5789 tgl@sss.pgh.pa.us 92 : 5210 : pstate->p_paramref_hook = variable_paramref_hook;
93 : 5210 : pstate->p_coerce_param_hook = variable_coerce_param_hook;
94 : 5210 : }
95 : :
96 : : /*
97 : : * Transform a ParamRef using fixed parameter types.
98 : : */
99 : : static Node *
100 : 2826 : fixed_paramref_hook(ParseState *pstate, ParamRef *pref)
101 : : {
102 : 2826 : FixedParamState *parstate = (FixedParamState *) pstate->p_ref_hook_state;
103 : 2826 : int paramno = pref->number;
104 : : Param *param;
105 : :
106 : : /* Check parameter number is valid */
5715 107 [ + - + - ]: 2826 : if (paramno <= 0 || paramno > parstate->numParams ||
108 [ - + ]: 2826 : !OidIsValid(parstate->paramTypes[paramno - 1]))
5789 tgl@sss.pgh.pa.us 109 [ # # ]:UBC 0 : ereport(ERROR,
110 : : (errcode(ERRCODE_UNDEFINED_PARAMETER),
111 : : errmsg("there is no parameter $%d", paramno),
112 : : parser_errposition(pstate, pref->location)));
113 : :
5789 tgl@sss.pgh.pa.us 114 :CBC 2826 : param = makeNode(Param);
115 : 2826 : param->paramkind = PARAM_EXTERN;
116 : 2826 : param->paramid = paramno;
117 : 2826 : param->paramtype = parstate->paramTypes[paramno - 1];
118 : 2826 : param->paramtypmod = -1;
5285 119 : 2826 : param->paramcollid = get_typcollation(param->paramtype);
5789 120 : 2826 : param->location = pref->location;
121 : :
122 : 2826 : 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 : 5756 : variable_paramref_hook(ParseState *pstate, ParamRef *pref)
133 : : {
134 : 5756 : VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
135 : 5756 : int paramno = pref->number;
136 : : Oid *pptype;
137 : : Param *param;
138 : :
139 : : /* Check parameter number is in range */
431 peter@eisentraut.org 140 [ + - - + ]: 5756 : if (paramno <= 0 || paramno > MaxAllocSize / sizeof(Oid))
5789 tgl@sss.pgh.pa.us 141 [ # # ]:UBC 0 : ereport(ERROR,
142 : : (errcode(ERRCODE_UNDEFINED_PARAMETER),
143 : : errmsg("there is no parameter $%d", paramno),
144 : : parser_errposition(pstate, pref->location)));
5789 tgl@sss.pgh.pa.us 145 [ + + ]:CBC 5756 : if (paramno > *parstate->numParams)
146 : : {
147 : : /* Need to enlarge param array */
148 [ + + ]: 4510 : if (*parstate->paramTypes)
1029 peter@eisentraut.org 149 : 1457 : *parstate->paramTypes = repalloc0_array(*parstate->paramTypes, Oid,
150 : : *parstate->numParams, paramno);
151 : : else
152 : 3053 : *parstate->paramTypes = palloc0_array(Oid, paramno);
5789 tgl@sss.pgh.pa.us 153 : 4510 : *parstate->numParams = paramno;
154 : : }
155 : :
156 : : /* Locate param's slot in array */
157 : 5756 : pptype = &(*parstate->paramTypes)[paramno - 1];
158 : :
159 : : /* If not seen before, initialize to UNKNOWN type */
160 [ + + ]: 5756 : if (*pptype == InvalidOid)
161 : 4620 : *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 : : */
1775 peter@eisentraut.org 169 [ - + - - ]: 5756 : if (*pptype == VOIDOID && pstate->p_expr_kind == EXPR_KIND_CALL_ARGUMENT)
1775 peter@eisentraut.org 170 :UBC 0 : *pptype = UNKNOWNOID;
171 : :
5789 tgl@sss.pgh.pa.us 172 :CBC 5756 : param = makeNode(Param);
173 : 5756 : param->paramkind = PARAM_EXTERN;
174 : 5756 : param->paramid = paramno;
175 : 5756 : param->paramtype = *pptype;
176 : 5756 : param->paramtypmod = -1;
5285 177 : 5756 : param->paramcollid = get_typcollation(param->paramtype);
5789 178 : 5756 : param->location = pref->location;
179 : :
180 : 5756 : return (Node *) param;
181 : : }
182 : :
183 : : /*
184 : : * Coerce a Param to a query-requested datatype, in the varparams case.
185 : : */
186 : : static Node *
187 : 4665 : variable_coerce_param_hook(ParseState *pstate, Param *param,
188 : : Oid targetTypeId, int32 targetTypeMod,
189 : : int location)
190 : : {
191 [ + - + + ]: 4665 : 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 : 4623 : VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
198 : 4623 : Oid *paramTypes = *parstate->paramTypes;
199 : 4623 : int paramno = param->paramid;
200 : :
201 [ + - ]: 4623 : if (paramno <= 0 || /* shouldn't happen, but... */
202 [ - + ]: 4623 : paramno > *parstate->numParams)
5789 tgl@sss.pgh.pa.us 203 [ # # ]:UBC 0 : ereport(ERROR,
204 : : (errcode(ERRCODE_UNDEFINED_PARAMETER),
205 : : errmsg("there is no parameter $%d", paramno),
206 : : parser_errposition(pstate, param->location)));
207 : :
5789 tgl@sss.pgh.pa.us 208 [ + - ]:CBC 4623 : if (paramTypes[paramno - 1] == UNKNOWNOID)
209 : : {
210 : : /* We've successfully resolved the type */
211 : 4623 : paramTypes[paramno - 1] = targetTypeId;
212 : : }
5789 tgl@sss.pgh.pa.us 213 [ # # ]:UBC 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 : :
5789 tgl@sss.pgh.pa.us 230 :CBC 4623 : 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 : 4623 : 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 : : */
5285 246 : 4623 : param->paramcollid = get_typcollation(param->paramtype);
247 : :
248 : : /* Use the leftmost of the param's and coercion's locations */
5789 249 [ + + ]: 4623 : if (location >= 0 &&
250 [ + - - + ]: 614 : (param->location < 0 || location < param->location))
5789 tgl@sss.pgh.pa.us 251 :UBC 0 : param->location = location;
252 : :
5789 tgl@sss.pgh.pa.us 253 :CBC 4623 : return (Node *) param;
254 : : }
255 : :
256 : : /* Else signal to proceed with normal coercion */
257 : 42 : 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 : 5202 : check_variable_parameters(ParseState *pstate, Query *query)
270 : : {
271 : 5202 : VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
272 : :
273 : : /* If numParams is zero then no Params were generated, so no work */
274 [ + + ]: 5202 : if (*parstate->numParams > 0)
275 : 3951 : (void) query_tree_walker(query,
276 : : check_parameter_resolution_walker,
277 : : pstate, 0);
278 : 5202 : }
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 : 122437 : check_parameter_resolution_walker(Node *node, ParseState *pstate)
288 : : {
289 [ + + ]: 122437 : if (node == NULL)
290 : 51116 : return false;
291 [ + + ]: 71321 : if (IsA(node, Param))
292 : : {
293 : 5204 : Param *param = (Param *) node;
294 : :
295 [ + + ]: 5204 : if (param->paramkind == PARAM_EXTERN)
296 : : {
297 : 5199 : VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
298 : 5199 : int paramno = param->paramid;
299 : :
300 [ + - ]: 5199 : if (paramno <= 0 || /* shouldn't happen, but... */
301 [ - + ]: 5199 : paramno > *parstate->numParams)
5789 tgl@sss.pgh.pa.us 302 [ # # ]:UBC 0 : ereport(ERROR,
303 : : (errcode(ERRCODE_UNDEFINED_PARAMETER),
304 : : errmsg("there is no parameter $%d", paramno),
305 : : parser_errposition(pstate, param->location)));
306 : :
5789 tgl@sss.pgh.pa.us 307 [ - + ]:CBC 5199 : if (param->paramtype != (*parstate->paramTypes)[paramno - 1])
5789 tgl@sss.pgh.pa.us 308 [ # # ]:UBC 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 : : }
5789 tgl@sss.pgh.pa.us 314 :CBC 5204 : return false;
315 : : }
316 [ + + ]: 66117 : if (IsA(node, Query))
317 : : {
318 : : /* Recurse into RTE subquery or not-yet-planned sublink subquery */
319 : 105 : return query_tree_walker((Query *) node,
320 : : check_parameter_resolution_walker,
321 : : pstate, 0);
322 : : }
323 : 66012 : return expression_tree_walker(node, check_parameter_resolution_walker,
324 : : pstate);
325 : : }
326 : :
327 : : /*
328 : : * Check to see if a fully-parsed query tree contains any PARAM_EXTERN Params.
329 : : */
330 : : bool
4530 331 : 281 : query_contains_extern_params(Query *query)
332 : : {
333 : 281 : return query_tree_walker(query,
334 : : query_contains_extern_params_walker,
335 : : NULL, 0);
336 : : }
337 : :
338 : : static bool
339 : 5913 : query_contains_extern_params_walker(Node *node, void *context)
340 : : {
341 [ + + ]: 5913 : if (node == NULL)
342 : 3615 : return false;
343 [ - + ]: 2298 : if (IsA(node, Param))
344 : : {
4530 tgl@sss.pgh.pa.us 345 :UBC 0 : Param *param = (Param *) node;
346 : :
347 [ # # ]: 0 : if (param->paramkind == PARAM_EXTERN)
348 : 0 : return true;
349 : 0 : return false;
350 : : }
4530 tgl@sss.pgh.pa.us 351 [ + + ]:CBC 2298 : if (IsA(node, Query))
352 : : {
353 : : /* Recurse into RTE subquery or not-yet-planned sublink subquery */
354 : 9 : return query_tree_walker((Query *) node,
355 : : query_contains_extern_params_walker,
356 : : context, 0);
357 : : }
358 : 2289 : return expression_tree_walker(node, query_contains_extern_params_walker,
359 : : context);
360 : : }
|