Age Owner 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-2023, 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
401 peter 67 CBC 1781 : setup_parse_fixed_parameters(ParseState *pstate,
68 : const Oid *paramTypes, int numParams)
69 : {
4908 tgl 70 1781 : FixedParamState *parstate = palloc(sizeof(FixedParamState));
71 :
72 1781 : parstate->paramTypes = paramTypes;
73 1781 : parstate->numParams = numParams;
74 1781 : pstate->p_ref_hook_state = (void *) parstate;
75 1781 : pstate->p_paramref_hook = fixed_paramref_hook;
76 : /* no need to use p_coerce_param_hook */
77 1781 : }
78 :
79 : /*
80 : * Set up to process a query containing references to variable parameters.
81 : */
82 : void
401 peter 83 4506 : setup_parse_variable_parameters(ParseState *pstate,
84 : Oid **paramTypes, int *numParams)
85 : {
4908 tgl 86 4506 : VarParamState *parstate = palloc(sizeof(VarParamState));
87 :
88 4506 : parstate->paramTypes = paramTypes;
89 4506 : parstate->numParams = numParams;
90 4506 : pstate->p_ref_hook_state = (void *) parstate;
91 4506 : pstate->p_paramref_hook = variable_paramref_hook;
92 4506 : pstate->p_coerce_param_hook = variable_coerce_param_hook;
93 4506 : }
94 :
95 : /*
96 : * Transform a ParamRef using fixed parameter types.
97 : */
98 : static Node *
99 2340 : fixed_paramref_hook(ParseState *pstate, ParamRef *pref)
100 : {
101 2340 : FixedParamState *parstate = (FixedParamState *) pstate->p_ref_hook_state;
102 2340 : int paramno = pref->number;
103 : Param *param;
104 :
105 : /* Check parameter number is valid */
4834 106 2340 : if (paramno <= 0 || paramno > parstate->numParams ||
107 2340 : !OidIsValid(parstate->paramTypes[paramno - 1]))
4908 tgl 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 :
4908 tgl 113 CBC 2340 : param = makeNode(Param);
114 2340 : param->paramkind = PARAM_EXTERN;
115 2340 : param->paramid = paramno;
116 2340 : param->paramtype = parstate->paramTypes[paramno - 1];
117 2340 : param->paramtypmod = -1;
4404 118 2340 : param->paramcollid = get_typcollation(param->paramtype);
4908 119 2340 : param->location = pref->location;
120 :
121 2340 : 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 75019 : variable_paramref_hook(ParseState *pstate, ParamRef *pref)
132 : {
133 75019 : VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
134 75019 : int paramno = pref->number;
135 : Oid *pptype;
136 : Param *param;
137 :
138 : /* Check parameter number is in range */
139 75019 : if (paramno <= 0 || paramno > INT_MAX / sizeof(Oid))
4908 tgl 140 UBC 0 : ereport(ERROR,
141 : (errcode(ERRCODE_UNDEFINED_PARAMETER),
142 : errmsg("there is no parameter $%d", paramno),
143 : parser_errposition(pstate, pref->location)));
4908 tgl 144 CBC 75019 : if (paramno > *parstate->numParams)
145 : {
146 : /* Need to enlarge param array */
147 74149 : if (*parstate->paramTypes)
148 peter 148 GNC 71366 : *parstate->paramTypes = repalloc0_array(*parstate->paramTypes, Oid,
149 : *parstate->numParams, paramno);
150 : else
151 2783 : *parstate->paramTypes = palloc0_array(Oid, paramno);
4908 tgl 152 CBC 74149 : *parstate->numParams = paramno;
153 : }
154 :
4908 tgl 155 ECB : /* Locate param's slot in array */
4908 tgl 156 CBC 75019 : pptype = &(*parstate->paramTypes)[paramno - 1];
157 :
158 : /* If not seen before, initialize to UNKNOWN type */
4908 tgl 159 GIC 75019 : if (*pptype == InvalidOid)
160 74249 : *pptype = UNKNOWNOID;
161 :
162 : /*
163 : * If the argument is of type void and it's procedure call, interpret it
894 peter 164 ECB : * as unknown. This allows the JDBC driver to not have to distinguish
894 peter 165 EUB : * function and procedure calls. See also another component of this hack
166 : * in ParseFuncOrColumn().
894 peter 167 ECB : */
894 peter 168 CBC 75019 : if (*pptype == VOIDOID && pstate->p_expr_kind == EXPR_KIND_CALL_ARGUMENT)
894 peter 169 LBC 0 : *pptype = UNKNOWNOID;
894 peter 170 ECB :
4908 tgl 171 CBC 75019 : param = makeNode(Param);
172 75019 : param->paramkind = PARAM_EXTERN;
173 75019 : param->paramid = paramno;
4908 tgl 174 GIC 75019 : param->paramtype = *pptype;
4908 tgl 175 CBC 75019 : param->paramtypmod = -1;
4404 tgl 176 GIC 75019 : param->paramcollid = get_typcollation(param->paramtype);
4908 177 75019 : param->location = pref->location;
178 :
179 75019 : return (Node *) param;
180 : }
181 :
4908 tgl 182 ECB : /*
183 : * Coerce a Param to a query-requested datatype, in the varparams case.
184 : */
185 : static Node *
4908 tgl 186 CBC 74262 : variable_coerce_param_hook(ParseState *pstate, Param *param,
187 : Oid targetTypeId, int32 targetTypeMod,
188 : int location)
189 : {
4908 tgl 190 GIC 74262 : if (param->paramkind == PARAM_EXTERN && param->paramtype == UNKNOWNOID)
191 : {
4908 tgl 192 ECB : /*
193 : * Input is a Param of previously undetermined type, and we want to
194 : * update our knowledge of the Param's type.
195 : */
4908 tgl 196 CBC 74252 : VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
197 74252 : Oid *paramTypes = *parstate->paramTypes;
4908 tgl 198 GBC 74252 : int paramno = param->paramid;
199 :
4908 tgl 200 GIC 74252 : if (paramno <= 0 || /* shouldn't happen, but... */
201 74252 : paramno > *parstate->numParams)
4908 tgl 202 UIC 0 : ereport(ERROR,
4908 tgl 203 ECB : (errcode(ERRCODE_UNDEFINED_PARAMETER),
204 : errmsg("there is no parameter $%d", paramno),
205 : parser_errposition(pstate, param->location)));
206 :
4908 tgl 207 GIC 74252 : if (paramTypes[paramno - 1] == UNKNOWNOID)
4908 tgl 208 EUB : {
209 : /* We've successfully resolved the type */
4908 tgl 210 GIC 74252 : paramTypes[paramno - 1] = targetTypeId;
211 : }
4908 tgl 212 UIC 0 : else if (paramTypes[paramno - 1] == targetTypeId)
213 : {
214 : /* We previously resolved the type, and it matches */
4908 tgl 215 EUB : }
216 : else
217 : {
218 : /* Oops */
4908 tgl 219 UIC 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]),
4908 tgl 225 ECB : format_type_be(targetTypeId)),
226 : parser_errposition(pstate, param->location)));
227 : }
228 :
4908 tgl 229 GIC 74252 : 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
4908 tgl 234 ECB : * 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 : */
4908 tgl 238 GIC 74252 : param->paramtypmod = -1;
239 :
240 : /*
4399 tgl 241 ECB : * 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 : */
4404 tgl 245 CBC 74252 : param->paramcollid = get_typcollation(param->paramtype);
4404 tgl 246 EUB :
247 : /* Use the leftmost of the param's and coercion's locations */
4908 tgl 248 CBC 74252 : if (location >= 0 &&
4908 tgl 249 GIC 576 : (param->location < 0 || location < param->location))
4908 tgl 250 UIC 0 : param->location = location;
251 :
4908 tgl 252 CBC 74252 : return (Node *) param;
253 : }
254 :
255 : /* Else signal to proceed with normal coercion */
4908 tgl 256 GIC 10 : 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
3260 bruce 264 ECB : * were used, nor that all got non-UNKNOWN types assigned. Caller of parser
265 : * should enforce that if it's important.
4908 tgl 266 : */
267 : void
4908 tgl 268 GIC 4499 : check_variable_parameters(ParseState *pstate, Query *query)
4908 tgl 269 ECB : {
4908 tgl 270 CBC 4499 : VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
271 :
272 : /* If numParams is zero then no Params were generated, so no work */
273 4499 : if (*parstate->numParams > 0)
4908 tgl 274 GIC 3474 : (void) query_tree_walker(query,
275 : check_parameter_resolution_walker,
276 : (void *) pstate, 0);
277 4499 : }
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
4908 tgl 282 ECB : * 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
4908 tgl 286 CBC 239420 : check_parameter_resolution_walker(Node *node, ParseState *pstate)
287 : {
288 239420 : if (node == NULL)
4908 tgl 289 GIC 41556 : return false;
4908 tgl 290 CBC 197864 : if (IsA(node, Param))
291 : {
292 74464 : Param *param = (Param *) node;
4908 tgl 293 ECB :
4908 tgl 294 GIC 74464 : if (param->paramkind == PARAM_EXTERN)
4908 tgl 295 ECB : {
4908 tgl 296 CBC 74462 : VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
4908 tgl 297 GBC 74462 : int paramno = param->paramid;
298 :
4908 tgl 299 GIC 74462 : if (paramno <= 0 || /* shouldn't happen, but... */
300 74462 : paramno > *parstate->numParams)
4908 tgl 301 UIC 0 : ereport(ERROR,
4908 tgl 302 ECB : (errcode(ERRCODE_UNDEFINED_PARAMETER),
4908 tgl 303 EUB : errmsg("there is no parameter $%d", paramno),
304 : parser_errposition(pstate, param->location)));
305 :
4908 tgl 306 GIC 74462 : if (param->paramtype != (*parstate->paramTypes)[paramno - 1])
4908 tgl 307 UIC 0 : ereport(ERROR,
308 : (errcode(ERRCODE_AMBIGUOUS_PARAMETER),
2118 tgl 309 ECB : errmsg("could not determine data type of parameter $%d",
310 : paramno),
4908 311 : parser_errposition(pstate, param->location)));
312 : }
4908 tgl 313 GIC 74464 : return false;
4908 tgl 314 ECB : }
4908 tgl 315 GIC 123400 : if (IsA(node, Query))
316 : {
317 : /* Recurse into RTE subquery or not-yet-planned sublink subquery */
4908 tgl 318 CBC 97 : return query_tree_walker((Query *) node,
319 : check_parameter_resolution_walker,
320 : (void *) pstate, 0);
321 : }
4908 tgl 322 GIC 123303 : return expression_tree_walker(node, check_parameter_resolution_walker,
323 : (void *) pstate);
324 : }
325 :
3649 tgl 326 ECB : /*
327 : * Check to see if a fully-parsed query tree contains any PARAM_EXTERN Params.
328 : */
329 : bool
3649 tgl 330 GIC 265 : query_contains_extern_params(Query *query)
331 : {
332 265 : return query_tree_walker(query,
333 : query_contains_extern_params_walker,
3649 tgl 334 ECB : NULL, 0);
335 : }
336 :
337 : static bool
3649 tgl 338 CBC 5283 : query_contains_extern_params_walker(Node *node, void *context)
339 : {
3649 tgl 340 GBC 5283 : if (node == NULL)
3649 tgl 341 GIC 3131 : return false;
3649 tgl 342 GBC 2152 : if (IsA(node, Param))
3649 tgl 343 EUB : {
3649 tgl 344 UBC 0 : Param *param = (Param *) node;
345 :
3649 tgl 346 LBC 0 : if (param->paramkind == PARAM_EXTERN)
3649 tgl 347 UIC 0 : return true;
348 0 : return false;
3649 tgl 349 ECB : }
3649 tgl 350 GIC 2152 : if (IsA(node, Query))
351 : {
352 : /* Recurse into RTE subquery or not-yet-planned sublink subquery */
3649 tgl 353 CBC 9 : return query_tree_walker((Query *) node,
354 : query_contains_extern_params_walker,
355 : context, 0);
356 : }
3649 tgl 357 GIC 2143 : return expression_tree_walker(node, query_contains_extern_params_walker,
358 : context);
359 : }
|