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