Header And Logo

PostgreSQL
| The world's most advanced open source database.

parse_param.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * parse_param.c
00004  *    handle parameters in parser
00005  *
00006  * This code covers two cases that are used within the core backend:
00007  *      * a fixed list of parameters with known types
00008  *      * an expandable list of parameters whose types can optionally
00009  *        be determined from context
00010  * In both cases, only explicit $n references (ParamRef nodes) are supported.
00011  *
00012  * Note that other approaches to parameters are possible using the parser
00013  * hooks defined in ParseState.
00014  *
00015  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00016  * Portions Copyright (c) 1994, Regents of the University of California
00017  *
00018  *
00019  * IDENTIFICATION
00020  *    src/backend/parser/parse_param.c
00021  *
00022  *-------------------------------------------------------------------------
00023  */
00024 
00025 #include "postgres.h"
00026 
00027 #include <limits.h>
00028 
00029 #include "catalog/pg_type.h"
00030 #include "nodes/nodeFuncs.h"
00031 #include "parser/parse_param.h"
00032 #include "utils/builtins.h"
00033 #include "utils/lsyscache.h"
00034 
00035 
00036 typedef struct FixedParamState
00037 {
00038     Oid        *paramTypes;     /* array of parameter type OIDs */
00039     int         numParams;      /* number of array entries */
00040 } FixedParamState;
00041 
00042 /*
00043  * In the varparams case, the caller-supplied OID array (if any) can be
00044  * re-palloc'd larger at need.  A zero array entry means that parameter number
00045  * hasn't been seen, while UNKNOWNOID means the parameter has been used but
00046  * its type is not yet known.
00047  */
00048 typedef struct VarParamState
00049 {
00050     Oid       **paramTypes;     /* array of parameter type OIDs */
00051     int        *numParams;      /* number of array entries */
00052 } VarParamState;
00053 
00054 static Node *fixed_paramref_hook(ParseState *pstate, ParamRef *pref);
00055 static Node *variable_paramref_hook(ParseState *pstate, ParamRef *pref);
00056 static Node *variable_coerce_param_hook(ParseState *pstate, Param *param,
00057                            Oid targetTypeId, int32 targetTypeMod,
00058                            int location);
00059 static bool check_parameter_resolution_walker(Node *node, ParseState *pstate);
00060 static bool query_contains_extern_params_walker(Node *node, void *context);
00061 
00062 
00063 /*
00064  * Set up to process a query containing references to fixed parameters.
00065  */
00066 void
00067 parse_fixed_parameters(ParseState *pstate,
00068                        Oid *paramTypes, int numParams)
00069 {
00070     FixedParamState *parstate = palloc(sizeof(FixedParamState));
00071 
00072     parstate->paramTypes = paramTypes;
00073     parstate->numParams = numParams;
00074     pstate->p_ref_hook_state = (void *) parstate;
00075     pstate->p_paramref_hook = fixed_paramref_hook;
00076     /* no need to use p_coerce_param_hook */
00077 }
00078 
00079 /*
00080  * Set up to process a query containing references to variable parameters.
00081  */
00082 void
00083 parse_variable_parameters(ParseState *pstate,
00084                           Oid **paramTypes, int *numParams)
00085 {
00086     VarParamState *parstate = palloc(sizeof(VarParamState));
00087 
00088     parstate->paramTypes = paramTypes;
00089     parstate->numParams = numParams;
00090     pstate->p_ref_hook_state = (void *) parstate;
00091     pstate->p_paramref_hook = variable_paramref_hook;
00092     pstate->p_coerce_param_hook = variable_coerce_param_hook;
00093 }
00094 
00095 /*
00096  * Transform a ParamRef using fixed parameter types.
00097  */
00098 static Node *
00099 fixed_paramref_hook(ParseState *pstate, ParamRef *pref)
00100 {
00101     FixedParamState *parstate = (FixedParamState *) pstate->p_ref_hook_state;
00102     int         paramno = pref->number;
00103     Param      *param;
00104 
00105     /* Check parameter number is valid */
00106     if (paramno <= 0 || paramno > parstate->numParams ||
00107         !OidIsValid(parstate->paramTypes[paramno - 1]))
00108         ereport(ERROR,
00109                 (errcode(ERRCODE_UNDEFINED_PARAMETER),
00110                  errmsg("there is no parameter $%d", paramno),
00111                  parser_errposition(pstate, pref->location)));
00112 
00113     param = makeNode(Param);
00114     param->paramkind = PARAM_EXTERN;
00115     param->paramid = paramno;
00116     param->paramtype = parstate->paramTypes[paramno - 1];
00117     param->paramtypmod = -1;
00118     param->paramcollid = get_typcollation(param->paramtype);
00119     param->location = pref->location;
00120 
00121     return (Node *) param;
00122 }
00123 
00124 /*
00125  * Transform a ParamRef using variable parameter types.
00126  *
00127  * The only difference here is we must enlarge the parameter type array
00128  * as needed.
00129  */
00130 static Node *
00131 variable_paramref_hook(ParseState *pstate, ParamRef *pref)
00132 {
00133     VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
00134     int         paramno = pref->number;
00135     Oid        *pptype;
00136     Param      *param;
00137 
00138     /* Check parameter number is in range */
00139     if (paramno <= 0 || paramno > INT_MAX / sizeof(Oid))
00140         ereport(ERROR,
00141                 (errcode(ERRCODE_UNDEFINED_PARAMETER),
00142                  errmsg("there is no parameter $%d", paramno),
00143                  parser_errposition(pstate, pref->location)));
00144     if (paramno > *parstate->numParams)
00145     {
00146         /* Need to enlarge param array */
00147         if (*parstate->paramTypes)
00148             *parstate->paramTypes = (Oid *) repalloc(*parstate->paramTypes,
00149                                                      paramno * sizeof(Oid));
00150         else
00151             *parstate->paramTypes = (Oid *) palloc(paramno * sizeof(Oid));
00152         /* Zero out the previously-unreferenced slots */
00153         MemSet(*parstate->paramTypes + *parstate->numParams,
00154                0,
00155                (paramno - *parstate->numParams) * sizeof(Oid));
00156         *parstate->numParams = paramno;
00157     }
00158 
00159     /* Locate param's slot in array */
00160     pptype = &(*parstate->paramTypes)[paramno - 1];
00161 
00162     /* If not seen before, initialize to UNKNOWN type */
00163     if (*pptype == InvalidOid)
00164         *pptype = UNKNOWNOID;
00165 
00166     param = makeNode(Param);
00167     param->paramkind = PARAM_EXTERN;
00168     param->paramid = paramno;
00169     param->paramtype = *pptype;
00170     param->paramtypmod = -1;
00171     param->paramcollid = get_typcollation(param->paramtype);
00172     param->location = pref->location;
00173 
00174     return (Node *) param;
00175 }
00176 
00177 /*
00178  * Coerce a Param to a query-requested datatype, in the varparams case.
00179  */
00180 static Node *
00181 variable_coerce_param_hook(ParseState *pstate, Param *param,
00182                            Oid targetTypeId, int32 targetTypeMod,
00183                            int location)
00184 {
00185     if (param->paramkind == PARAM_EXTERN && param->paramtype == UNKNOWNOID)
00186     {
00187         /*
00188          * Input is a Param of previously undetermined type, and we want to
00189          * update our knowledge of the Param's type.
00190          */
00191         VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
00192         Oid        *paramTypes = *parstate->paramTypes;
00193         int         paramno = param->paramid;
00194 
00195         if (paramno <= 0 ||     /* shouldn't happen, but... */
00196             paramno > *parstate->numParams)
00197             ereport(ERROR,
00198                     (errcode(ERRCODE_UNDEFINED_PARAMETER),
00199                      errmsg("there is no parameter $%d", paramno),
00200                      parser_errposition(pstate, param->location)));
00201 
00202         if (paramTypes[paramno - 1] == UNKNOWNOID)
00203         {
00204             /* We've successfully resolved the type */
00205             paramTypes[paramno - 1] = targetTypeId;
00206         }
00207         else if (paramTypes[paramno - 1] == targetTypeId)
00208         {
00209             /* We previously resolved the type, and it matches */
00210         }
00211         else
00212         {
00213             /* Ooops */
00214             ereport(ERROR,
00215                     (errcode(ERRCODE_AMBIGUOUS_PARAMETER),
00216                      errmsg("inconsistent types deduced for parameter $%d",
00217                             paramno),
00218                      errdetail("%s versus %s",
00219                                format_type_be(paramTypes[paramno - 1]),
00220                                format_type_be(targetTypeId)),
00221                      parser_errposition(pstate, param->location)));
00222         }
00223 
00224         param->paramtype = targetTypeId;
00225 
00226         /*
00227          * Note: it is tempting here to set the Param's paramtypmod to
00228          * targetTypeMod, but that is probably unwise because we have no
00229          * infrastructure that enforces that the value delivered for a Param
00230          * will match any particular typmod.  Leaving it -1 ensures that a
00231          * run-time length check/coercion will occur if needed.
00232          */
00233         param->paramtypmod = -1;
00234 
00235         /*
00236          * This module always sets a Param's collation to be the default for
00237          * its datatype.  If that's not what you want, you should be using the
00238          * more general parser substitution hooks.
00239          */
00240         param->paramcollid = get_typcollation(param->paramtype);
00241 
00242         /* Use the leftmost of the param's and coercion's locations */
00243         if (location >= 0 &&
00244             (param->location < 0 || location < param->location))
00245             param->location = location;
00246 
00247         return (Node *) param;
00248     }
00249 
00250     /* Else signal to proceed with normal coercion */
00251     return NULL;
00252 }
00253 
00254 /*
00255  * Check for consistent assignment of variable parameters after completion
00256  * of parsing with parse_variable_parameters.
00257  *
00258  * Note: this code intentionally does not check that all parameter positions
00259  * were used, nor that all got non-UNKNOWN types assigned.  Caller of parser
00260  * should enforce that if it's important.
00261  */
00262 void
00263 check_variable_parameters(ParseState *pstate, Query *query)
00264 {
00265     VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
00266 
00267     /* If numParams is zero then no Params were generated, so no work */
00268     if (*parstate->numParams > 0)
00269         (void) query_tree_walker(query,
00270                                  check_parameter_resolution_walker,
00271                                  (void *) pstate, 0);
00272 }
00273 
00274 /*
00275  * Traverse a fully-analyzed tree to verify that parameter symbols
00276  * match their types.  We need this because some Params might still
00277  * be UNKNOWN, if there wasn't anything to force their coercion,
00278  * and yet other instances seen later might have gotten coerced.
00279  */
00280 static bool
00281 check_parameter_resolution_walker(Node *node, ParseState *pstate)
00282 {
00283     if (node == NULL)
00284         return false;
00285     if (IsA(node, Param))
00286     {
00287         Param      *param = (Param *) node;
00288 
00289         if (param->paramkind == PARAM_EXTERN)
00290         {
00291             VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
00292             int         paramno = param->paramid;
00293 
00294             if (paramno <= 0 || /* shouldn't happen, but... */
00295                 paramno > *parstate->numParams)
00296                 ereport(ERROR,
00297                         (errcode(ERRCODE_UNDEFINED_PARAMETER),
00298                          errmsg("there is no parameter $%d", paramno),
00299                          parser_errposition(pstate, param->location)));
00300 
00301             if (param->paramtype != (*parstate->paramTypes)[paramno - 1])
00302                 ereport(ERROR,
00303                         (errcode(ERRCODE_AMBIGUOUS_PARAMETER),
00304                      errmsg("could not determine data type of parameter $%d",
00305                             paramno),
00306                          parser_errposition(pstate, param->location)));
00307         }
00308         return false;
00309     }
00310     if (IsA(node, Query))
00311     {
00312         /* Recurse into RTE subquery or not-yet-planned sublink subquery */
00313         return query_tree_walker((Query *) node,
00314                                  check_parameter_resolution_walker,
00315                                  (void *) pstate, 0);
00316     }
00317     return expression_tree_walker(node, check_parameter_resolution_walker,
00318                                   (void *) pstate);
00319 }
00320 
00321 /*
00322  * Check to see if a fully-parsed query tree contains any PARAM_EXTERN Params.
00323  */
00324 bool
00325 query_contains_extern_params(Query *query)
00326 {
00327     return query_tree_walker(query,
00328                              query_contains_extern_params_walker,
00329                              NULL, 0);
00330 }
00331 
00332 static bool
00333 query_contains_extern_params_walker(Node *node, void *context)
00334 {
00335     if (node == NULL)
00336         return false;
00337     if (IsA(node, Param))
00338     {
00339         Param      *param = (Param *) node;
00340 
00341         if (param->paramkind == PARAM_EXTERN)
00342             return true;
00343         return false;
00344     }
00345     if (IsA(node, Query))
00346     {
00347         /* Recurse into RTE subquery or not-yet-planned sublink subquery */
00348         return query_tree_walker((Query *) node,
00349                                  query_contains_extern_params_walker,
00350                                  context, 0);
00351     }
00352     return expression_tree_walker(node, query_contains_extern_params_walker,
00353                                   context);
00354 }