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