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