1 /*-------------------------------------------------------------------------
4 * handle parameters in parser
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.
12 * Note that other approaches to parameters are possible using the parser
13 * hooks defined in ParseState.
15 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
16 * Portions Copyright (c) 1994, Regents of the University of California
20 * src/backend/parser/parse_param.c
22 *-------------------------------------------------------------------------
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"
37 typedef struct FixedParamState
39 const Oid
*paramTypes
; /* array of parameter type OIDs */
40 int numParams
; /* number of array entries */
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.
49 typedef struct VarParamState
51 Oid
**paramTypes
; /* array of parameter type OIDs */
52 int *numParams
; /* number of array entries */
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
,
60 static bool check_parameter_resolution_walker(Node
*node
, ParseState
*pstate
);
61 static bool query_contains_extern_params_walker(Node
*node
, void *context
);
65 * Set up to process a query containing references to fixed parameters.
68 setup_parse_fixed_parameters(ParseState
*pstate
,
69 const Oid
*paramTypes
, int numParams
)
71 FixedParamState
*parstate
= palloc(sizeof(FixedParamState
));
73 parstate
->paramTypes
= paramTypes
;
74 parstate
->numParams
= numParams
;
75 pstate
->p_ref_hook_state
= parstate
;
76 pstate
->p_paramref_hook
= fixed_paramref_hook
;
77 /* no need to use p_coerce_param_hook */
81 * Set up to process a query containing references to variable parameters.
84 setup_parse_variable_parameters(ParseState
*pstate
,
85 Oid
**paramTypes
, int *numParams
)
87 VarParamState
*parstate
= palloc(sizeof(VarParamState
));
89 parstate
->paramTypes
= paramTypes
;
90 parstate
->numParams
= numParams
;
91 pstate
->p_ref_hook_state
= parstate
;
92 pstate
->p_paramref_hook
= variable_paramref_hook
;
93 pstate
->p_coerce_param_hook
= variable_coerce_param_hook
;
97 * Transform a ParamRef using fixed parameter types.
100 fixed_paramref_hook(ParseState
*pstate
, ParamRef
*pref
)
102 FixedParamState
*parstate
= (FixedParamState
*) pstate
->p_ref_hook_state
;
103 int paramno
= pref
->number
;
106 /* Check parameter number is valid */
107 if (paramno
<= 0 || paramno
> parstate
->numParams
||
108 !OidIsValid(parstate
->paramTypes
[paramno
- 1]))
110 (errcode(ERRCODE_UNDEFINED_PARAMETER
),
111 errmsg("there is no parameter $%d", paramno
),
112 parser_errposition(pstate
, pref
->location
)));
114 param
= makeNode(Param
);
115 param
->paramkind
= PARAM_EXTERN
;
116 param
->paramid
= paramno
;
117 param
->paramtype
= parstate
->paramTypes
[paramno
- 1];
118 param
->paramtypmod
= -1;
119 param
->paramcollid
= get_typcollation(param
->paramtype
);
120 param
->location
= pref
->location
;
122 return (Node
*) param
;
126 * Transform a ParamRef using variable parameter types.
128 * The only difference here is we must enlarge the parameter type array
132 variable_paramref_hook(ParseState
*pstate
, ParamRef
*pref
)
134 VarParamState
*parstate
= (VarParamState
*) pstate
->p_ref_hook_state
;
135 int paramno
= pref
->number
;
139 /* Check parameter number is in range */
140 if (paramno
<= 0 || paramno
> MaxAllocSize
/ sizeof(Oid
))
142 (errcode(ERRCODE_UNDEFINED_PARAMETER
),
143 errmsg("there is no parameter $%d", paramno
),
144 parser_errposition(pstate
, pref
->location
)));
145 if (paramno
> *parstate
->numParams
)
147 /* Need to enlarge param array */
148 if (*parstate
->paramTypes
)
149 *parstate
->paramTypes
= repalloc0_array(*parstate
->paramTypes
, Oid
,
150 *parstate
->numParams
, paramno
);
152 *parstate
->paramTypes
= palloc0_array(Oid
, paramno
);
153 *parstate
->numParams
= paramno
;
156 /* Locate param's slot in array */
157 pptype
= &(*parstate
->paramTypes
)[paramno
- 1];
159 /* If not seen before, initialize to UNKNOWN type */
160 if (*pptype
== InvalidOid
)
161 *pptype
= UNKNOWNOID
;
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().
169 if (*pptype
== VOIDOID
&& pstate
->p_expr_kind
== EXPR_KIND_CALL_ARGUMENT
)
170 *pptype
= UNKNOWNOID
;
172 param
= makeNode(Param
);
173 param
->paramkind
= PARAM_EXTERN
;
174 param
->paramid
= paramno
;
175 param
->paramtype
= *pptype
;
176 param
->paramtypmod
= -1;
177 param
->paramcollid
= get_typcollation(param
->paramtype
);
178 param
->location
= pref
->location
;
180 return (Node
*) param
;
184 * Coerce a Param to a query-requested datatype, in the varparams case.
187 variable_coerce_param_hook(ParseState
*pstate
, Param
*param
,
188 Oid targetTypeId
, int32 targetTypeMod
,
191 if (param
->paramkind
== PARAM_EXTERN
&& param
->paramtype
== UNKNOWNOID
)
194 * Input is a Param of previously undetermined type, and we want to
195 * update our knowledge of the Param's type.
197 VarParamState
*parstate
= (VarParamState
*) pstate
->p_ref_hook_state
;
198 Oid
*paramTypes
= *parstate
->paramTypes
;
199 int paramno
= param
->paramid
;
201 if (paramno
<= 0 || /* shouldn't happen, but... */
202 paramno
> *parstate
->numParams
)
204 (errcode(ERRCODE_UNDEFINED_PARAMETER
),
205 errmsg("there is no parameter $%d", paramno
),
206 parser_errposition(pstate
, param
->location
)));
208 if (paramTypes
[paramno
- 1] == UNKNOWNOID
)
210 /* We've successfully resolved the type */
211 paramTypes
[paramno
- 1] = targetTypeId
;
213 else if (paramTypes
[paramno
- 1] == targetTypeId
)
215 /* We previously resolved the type, and it matches */
221 (errcode(ERRCODE_AMBIGUOUS_PARAMETER
),
222 errmsg("inconsistent types deduced for parameter $%d",
224 errdetail("%s versus %s",
225 format_type_be(paramTypes
[paramno
- 1]),
226 format_type_be(targetTypeId
)),
227 parser_errposition(pstate
, param
->location
)));
230 param
->paramtype
= targetTypeId
;
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.
239 param
->paramtypmod
= -1;
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.
246 param
->paramcollid
= get_typcollation(param
->paramtype
);
248 /* Use the leftmost of the param's and coercion's locations */
250 (param
->location
< 0 || location
< param
->location
))
251 param
->location
= location
;
253 return (Node
*) param
;
256 /* Else signal to proceed with normal coercion */
261 * Check for consistent assignment of variable parameters after completion
262 * of parsing with parse_variable_parameters.
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.
269 check_variable_parameters(ParseState
*pstate
, Query
*query
)
271 VarParamState
*parstate
= (VarParamState
*) pstate
->p_ref_hook_state
;
273 /* If numParams is zero then no Params were generated, so no work */
274 if (*parstate
->numParams
> 0)
275 (void) query_tree_walker(query
,
276 check_parameter_resolution_walker
,
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.
287 check_parameter_resolution_walker(Node
*node
, ParseState
*pstate
)
291 if (IsA(node
, Param
))
293 Param
*param
= (Param
*) node
;
295 if (param
->paramkind
== PARAM_EXTERN
)
297 VarParamState
*parstate
= (VarParamState
*) pstate
->p_ref_hook_state
;
298 int paramno
= param
->paramid
;
300 if (paramno
<= 0 || /* shouldn't happen, but... */
301 paramno
> *parstate
->numParams
)
303 (errcode(ERRCODE_UNDEFINED_PARAMETER
),
304 errmsg("there is no parameter $%d", paramno
),
305 parser_errposition(pstate
, param
->location
)));
307 if (param
->paramtype
!= (*parstate
->paramTypes
)[paramno
- 1])
309 (errcode(ERRCODE_AMBIGUOUS_PARAMETER
),
310 errmsg("could not determine data type of parameter $%d",
312 parser_errposition(pstate
, param
->location
)));
316 if (IsA(node
, Query
))
318 /* Recurse into RTE subquery or not-yet-planned sublink subquery */
319 return query_tree_walker((Query
*) node
,
320 check_parameter_resolution_walker
,
323 return expression_tree_walker(node
, check_parameter_resolution_walker
,
328 * Check to see if a fully-parsed query tree contains any PARAM_EXTERN Params.
331 query_contains_extern_params(Query
*query
)
333 return query_tree_walker(query
,
334 query_contains_extern_params_walker
,
339 query_contains_extern_params_walker(Node
*node
, void *context
)
343 if (IsA(node
, Param
))
345 Param
*param
= (Param
*) node
;
347 if (param
->paramkind
== PARAM_EXTERN
)
351 if (IsA(node
, Query
))
353 /* Recurse into RTE subquery or not-yet-planned sublink subquery */
354 return query_tree_walker((Query
*) node
,
355 query_contains_extern_params_walker
,
358 return expression_tree_walker(node
, query_contains_extern_params_walker
,