Commit fb5d0580 authored by Tom Lane's avatar Tom Lane

Implement parser hooks for processing ColumnRef and ParamRef nodes, as per my

recent proposal.  As proof of concept, remove knowledge of Params from the
core parser, arranging for them to be handled entirely by parser hook
functions.  It turns out we need an additional hook for that --- I had
forgotten about the code that handles inferring a parameter's type from
context.

This is a preliminary step towards letting plpgsql handle its variables
through parser hooks.  Additional work remains to be done to expose the
facility through SPI, but I think this is all the changes needed in the core
parser.
parent 8442317b
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.119 2009/10/08 02:39:17 tgl Exp $ * $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.120 2009/10/31 01:41:31 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -2289,6 +2289,38 @@ DeconstructQualifiedName(List *names, ...@@ -2289,6 +2289,38 @@ DeconstructQualifiedName(List *names,
*objname_p = objname; *objname_p = objname;
} }
/*
* LookupNamespaceNoError
* Look up a schema name.
*
* Returns the namespace OID, or InvalidOid if not found.
*
* Note this does NOT perform any permissions check --- callers are
* responsible for being sure that an appropriate check is made.
* In the majority of cases LookupExplicitNamespace is preferable.
*/
Oid
LookupNamespaceNoError(const char *nspname)
{
/* check for pg_temp alias */
if (strcmp(nspname, "pg_temp") == 0)
{
if (OidIsValid(myTempNamespace))
return myTempNamespace;
/*
* Since this is used only for looking up existing objects, there is
* no point in trying to initialize the temp namespace here; and doing
* so might create problems for some callers. Just report "not found".
*/
return InvalidOid;
}
return GetSysCacheOid(NAMESPACENAME,
CStringGetDatum(nspname),
0, 0, 0);
}
/* /*
* LookupExplicitNamespace * LookupExplicitNamespace
* Process an explicitly-specified schema name: look up the schema * Process an explicitly-specified schema name: look up the schema
...@@ -2336,8 +2368,8 @@ LookupExplicitNamespace(const char *nspname) ...@@ -2336,8 +2368,8 @@ LookupExplicitNamespace(const char *nspname)
* LookupCreationNamespace * LookupCreationNamespace
* Look up the schema and verify we have CREATE rights on it. * Look up the schema and verify we have CREATE rights on it.
* *
* This is just like LookupExplicitNamespace except for the permission check, * This is just like LookupExplicitNamespace except for the different
* and that we are willing to create pg_temp if needed. * permission check, and that we are willing to create pg_temp if needed.
* *
* Note: calling this may result in a CommandCounterIncrement operation, * Note: calling this may result in a CommandCounterIncrement operation,
* if we have to create or clean out the temp namespace. * if we have to create or clean out the temp namespace.
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
# #
# Makefile for parser # Makefile for parser
# #
# $PostgreSQL: pgsql/src/backend/parser/Makefile,v 1.51 2009/08/28 20:26:19 petere Exp $ # $PostgreSQL: pgsql/src/backend/parser/Makefile,v 1.52 2009/10/31 01:41:31 tgl Exp $
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
...@@ -12,9 +12,10 @@ include $(top_builddir)/src/Makefile.global ...@@ -12,9 +12,10 @@ include $(top_builddir)/src/Makefile.global
override CPPFLAGS := -I$(srcdir) $(CPPFLAGS) override CPPFLAGS := -I$(srcdir) $(CPPFLAGS)
OBJS= analyze.o gram.o keywords.o parser.o parse_agg.o parse_cte.o parse_clause.o \ OBJS= analyze.o gram.o keywords.o kwlookup.o parser.o \
parse_expr.o parse_func.o parse_node.o parse_oper.o parse_relation.o \ parse_agg.o parse_clause.o parse_coerce.o parse_cte.o parse_expr.o \
parse_type.o parse_coerce.o parse_target.o parse_utilcmd.o scansup.o kwlookup.o parse_func.o parse_node.o parse_oper.o parse_param.o parse_relation.o \
parse_target.o parse_type.o parse_utilcmd.o scansup.o
FLEXFLAGS = -CF FLEXFLAGS = -CF
......
$PostgreSQL: pgsql/src/backend/parser/README,v 1.10 2008/04/09 01:00:46 momjian Exp $ $PostgreSQL: pgsql/src/backend/parser/README,v 1.11 2009/10/31 01:41:31 tgl Exp $
Parser Parser
====== ======
...@@ -10,17 +10,20 @@ to the optimizer and then executor. ...@@ -10,17 +10,20 @@ to the optimizer and then executor.
parser.c things start here parser.c things start here
scan.l break query into tokens scan.l break query into tokens
scansup.c handle escapes in input strings scansup.c handle escapes in input strings
keywords.c turn keywords into specific tokens kwlookup.c turn keywords into specific tokens
gram.y parse the tokens and fill query-type-specific structures keywords.c table of standard keywords (passed to kwlookup.c)
gram.y parse the tokens and produce a "raw" parse tree
analyze.c top level of parse analysis for optimizable queries analyze.c top level of parse analysis for optimizable queries
parse_agg.c handle aggregates, like SUM(col1), AVG(col2), ...
parse_clause.c handle clauses like WHERE, ORDER BY, GROUP BY, ... parse_clause.c handle clauses like WHERE, ORDER BY, GROUP BY, ...
parse_coerce.c handle coercing expressions to different data types parse_coerce.c handle coercing expressions to different data types
parse_cte.c handle Common Table Expressions (WITH clauses)
parse_expr.c handle expressions like col, col + 3, x = 3 or x = 4 parse_expr.c handle expressions like col, col + 3, x = 3 or x = 4
parse_oper.c handle operators in expressions
parse_agg.c handle aggregates, like SUM(col1), AVG(col2), ...
parse_func.c handle functions, table.column and column identifiers parse_func.c handle functions, table.column and column identifiers
parse_node.c create nodes for various structures parse_node.c create nodes for various structures
parse_target.c handle the result list of the query parse_oper.c handle operators in expressions
parse_param.c handle Params (for the cases used in the core backend)
parse_relation.c support routines for tables and column handling parse_relation.c support routines for tables and column handling
parse_target.c handle the result list of the query
parse_type.c support routines for data type handling parse_type.c support routines for data type handling
parse_utilcmd.c parse analysis for utility commands (done at execution time) parse_utilcmd.c parse analysis for utility commands (done at execution time)
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.395 2009/10/28 14:55:43 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.396 2009/10/31 01:41:31 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include "parser/parse_coerce.h" #include "parser/parse_coerce.h"
#include "parser/parse_cte.h" #include "parser/parse_cte.h"
#include "parser/parse_oper.h" #include "parser/parse_oper.h"
#include "parser/parse_param.h"
#include "parser/parse_relation.h" #include "parser/parse_relation.h"
#include "parser/parse_target.h" #include "parser/parse_target.h"
#include "parser/parsetree.h" #include "parser/parsetree.h"
...@@ -62,7 +63,6 @@ static Query *transformExplainStmt(ParseState *pstate, ...@@ -62,7 +63,6 @@ static Query *transformExplainStmt(ParseState *pstate,
ExplainStmt *stmt); ExplainStmt *stmt);
static void transformLockingClause(ParseState *pstate, Query *qry, static void transformLockingClause(ParseState *pstate, Query *qry,
LockingClause *lc, bool pushedDown); LockingClause *lc, bool pushedDown);
static bool check_parameter_resolution_walker(Node *node, ParseState *pstate);
/* /*
...@@ -86,9 +86,9 @@ parse_analyze(Node *parseTree, const char *sourceText, ...@@ -86,9 +86,9 @@ parse_analyze(Node *parseTree, const char *sourceText,
Assert(sourceText != NULL); /* required as of 8.4 */ Assert(sourceText != NULL); /* required as of 8.4 */
pstate->p_sourcetext = sourceText; pstate->p_sourcetext = sourceText;
pstate->p_paramtypes = paramTypes;
pstate->p_numparams = numParams; if (numParams > 0)
pstate->p_variableparams = false; parse_fixed_parameters(pstate, paramTypes, numParams);
query = transformStmt(pstate, parseTree); query = transformStmt(pstate, parseTree);
...@@ -114,18 +114,13 @@ parse_analyze_varparams(Node *parseTree, const char *sourceText, ...@@ -114,18 +114,13 @@ parse_analyze_varparams(Node *parseTree, const char *sourceText,
Assert(sourceText != NULL); /* required as of 8.4 */ Assert(sourceText != NULL); /* required as of 8.4 */
pstate->p_sourcetext = sourceText; pstate->p_sourcetext = sourceText;
pstate->p_paramtypes = *paramTypes;
pstate->p_numparams = *numParams; parse_variable_parameters(pstate, paramTypes, numParams);
pstate->p_variableparams = true;
query = transformStmt(pstate, parseTree); query = transformStmt(pstate, parseTree);
/* make sure all is well with parameter types */ /* make sure all is well with parameter types */
if (pstate->p_numparams > 0) check_variable_parameters(pstate, query);
check_parameter_resolution_walker((Node *) query, pstate);
*paramTypes = pstate->p_paramtypes;
*numParams = pstate->p_numparams;
free_parsestate(pstate); free_parsestate(pstate);
...@@ -1982,7 +1977,7 @@ transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt) ...@@ -1982,7 +1977,7 @@ transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt)
* *
* EXPLAIN is just like other utility statements in that we emit it as a * EXPLAIN is just like other utility statements in that we emit it as a
* CMD_UTILITY Query node with no transformation of the raw parse tree. * CMD_UTILITY Query node with no transformation of the raw parse tree.
* However, if p_variableparams is set, it could be that the client is * However, if p_coerce_param_hook is set, it could be that the client is
* expecting us to resolve parameter types in something like * expecting us to resolve parameter types in something like
* EXPLAIN SELECT * FROM tab WHERE col = $1 * EXPLAIN SELECT * FROM tab WHERE col = $1
* To deal with such cases, we run parse analysis and throw away the result; * To deal with such cases, we run parse analysis and throw away the result;
...@@ -1996,7 +1991,7 @@ transformExplainStmt(ParseState *pstate, ExplainStmt *stmt) ...@@ -1996,7 +1991,7 @@ transformExplainStmt(ParseState *pstate, ExplainStmt *stmt)
{ {
Query *result; Query *result;
if (pstate->p_variableparams) if (pstate->p_coerce_param_hook != NULL)
{ {
/* Since parse analysis scribbles on its input, copy the tree first! */ /* Since parse analysis scribbles on its input, copy the tree first! */
(void) transformStmt(pstate, copyObject(stmt->query)); (void) transformStmt(pstate, copyObject(stmt->query));
...@@ -2239,50 +2234,3 @@ applyLockingClause(Query *qry, Index rtindex, ...@@ -2239,50 +2234,3 @@ applyLockingClause(Query *qry, Index rtindex,
rc->pushedDown = pushedDown; rc->pushedDown = pushedDown;
qry->rowMarks = lappend(qry->rowMarks, rc); qry->rowMarks = lappend(qry->rowMarks, rc);
} }
/*
* Traverse a fully-analyzed tree to verify that parameter symbols
* match their types. We need this because some Params might still
* be UNKNOWN, if there wasn't anything to force their coercion,
* and yet other instances seen later might have gotten coerced.
*/
static bool
check_parameter_resolution_walker(Node *node, ParseState *pstate)
{
if (node == NULL)
return false;
if (IsA(node, Param))
{
Param *param = (Param *) node;
if (param->paramkind == PARAM_EXTERN)
{
int paramno = param->paramid;
if (paramno <= 0 || /* shouldn't happen, but... */
paramno > pstate->p_numparams)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_PARAMETER),
errmsg("there is no parameter $%d", paramno),
parser_errposition(pstate, param->location)));
if (param->paramtype != pstate->p_paramtypes[paramno - 1])
ereport(ERROR,
(errcode(ERRCODE_AMBIGUOUS_PARAMETER),
errmsg("could not determine data type of parameter $%d",
paramno),
parser_errposition(pstate, param->location)));
}
return false;
}
if (IsA(node, Query))
{
/* Recurse into RTE subquery or not-yet-planned sublink subquery */
return query_tree_walker((Query *) node,
check_parameter_resolution_walker,
(void *) pstate, 0);
}
return expression_tree_walker(node, check_parameter_resolution_walker,
(void *) pstate);
}
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.177 2009/06/11 14:49:00 momjian Exp $ * $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.178 2009/10/31 01:41:31 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -259,69 +259,21 @@ coerce_type(ParseState *pstate, Node *node, ...@@ -259,69 +259,21 @@ coerce_type(ParseState *pstate, Node *node,
return result; return result;
} }
if (inputTypeId == UNKNOWNOID && IsA(node, Param) && if (IsA(node, Param) &&
((Param *) node)->paramkind == PARAM_EXTERN && pstate != NULL && pstate->p_coerce_param_hook != NULL)
pstate != NULL && pstate->p_variableparams)
{ {
/* /*
* Input is a Param of previously undetermined type, and we want to * Allow the CoerceParamHook to decide what happens. It can return
* update our knowledge of the Param's type. Find the topmost * a transformed node (very possibly the same Param node), or return
* ParseState and update the state. * NULL to indicate we should proceed with normal coercion.
*/ */
Param *param = (Param *) node; result = (*pstate->p_coerce_param_hook) (pstate,
int paramno = param->paramid; (Param *) node,
ParseState *toppstate; targetTypeId,
targetTypeMod,
toppstate = pstate; location);
while (toppstate->parentParseState != NULL) if (result)
toppstate = toppstate->parentParseState; return result;
if (paramno <= 0 || /* shouldn't happen, but... */
paramno > toppstate->p_numparams)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_PARAMETER),
errmsg("there is no parameter $%d", paramno),
parser_errposition(pstate, param->location)));
if (toppstate->p_paramtypes[paramno - 1] == UNKNOWNOID)
{
/* We've successfully resolved the type */
toppstate->p_paramtypes[paramno - 1] = targetTypeId;
}
else if (toppstate->p_paramtypes[paramno - 1] == targetTypeId)
{
/* We previously resolved the type, and it matches */
}
else
{
/* Ooops */
ereport(ERROR,
(errcode(ERRCODE_AMBIGUOUS_PARAMETER),
errmsg("inconsistent types deduced for parameter $%d",
paramno),
errdetail("%s versus %s",
format_type_be(toppstate->p_paramtypes[paramno - 1]),
format_type_be(targetTypeId)),
parser_errposition(pstate, param->location)));
}
param->paramtype = targetTypeId;
/*
* Note: it is tempting here to set the Param's paramtypmod to
* targetTypeMod, but that is probably unwise because we have no
* infrastructure that enforces that the value delivered for a Param
* will match any particular typmod. Leaving it -1 ensures that a
* run-time length check/coercion will occur if needed.
*/
param->paramtypmod = -1;
/* Use the leftmost of the param's and coercion's locations */
if (location >= 0 &&
(param->location < 0 || location < param->location))
param->location = location;
return (Node *) param;
} }
pathtype = find_coercion_pathway(targetTypeId, inputTypeId, ccontext, pathtype = find_coercion_pathway(targetTypeId, inputTypeId, ccontext,
&funcId); &funcId);
......
This diff is collapsed.
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.217 2009/10/08 02:39:23 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.218 2009/10/31 01:41:31 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -33,8 +33,6 @@ ...@@ -33,8 +33,6 @@
static Oid FuncNameAsType(List *funcname); static Oid FuncNameAsType(List *funcname);
static Node *ParseComplexProjection(ParseState *pstate, char *funcname, static Node *ParseComplexProjection(ParseState *pstate, char *funcname,
Node *first_arg, int location); Node *first_arg, int location);
static void unknown_attribute(ParseState *pstate, Node *relref, char *attname,
int location);
/* /*
...@@ -53,6 +51,8 @@ static void unknown_attribute(ParseState *pstate, Node *relref, char *attname, ...@@ -53,6 +51,8 @@ static void unknown_attribute(ParseState *pstate, Node *relref, char *attname,
* not to affect the semantics. When is_column is true, we should have * not to affect the semantics. When is_column is true, we should have
* a single argument (the putative table), unqualified function name * a single argument (the putative table), unqualified function name
* equal to the column name, and no aggregate or variadic decoration. * equal to the column name, and no aggregate or variadic decoration.
* Also, when is_column is true, we return NULL on failure rather than
* reporting a no-such-function error.
* *
* The argument expressions (in fargs) must have been transformed already. * The argument expressions (in fargs) must have been transformed already.
*/ */
...@@ -253,16 +253,11 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, ...@@ -253,16 +253,11 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
/* /*
* Oops. Time to die. * Oops. Time to die.
* *
* If we are dealing with the attribute notation rel.function, give an * If we are dealing with the attribute notation rel.function,
* error message that is appropriate for that case. * let the caller handle failure.
*/ */
if (is_column) if (is_column)
{ return NULL;
Assert(nargs == 1);
Assert(list_length(funcname) == 1);
unknown_attribute(pstate, first_arg, strVal(linitial(funcname)),
location);
}
/* /*
* Else generate a detailed complaint for a function * Else generate a detailed complaint for a function
...@@ -1343,55 +1338,6 @@ ParseComplexProjection(ParseState *pstate, char *funcname, Node *first_arg, ...@@ -1343,55 +1338,6 @@ ParseComplexProjection(ParseState *pstate, char *funcname, Node *first_arg,
return NULL; /* funcname does not match any column */ return NULL; /* funcname does not match any column */
} }
/*
* helper routine for delivering "column does not exist" error message
*/
static void
unknown_attribute(ParseState *pstate, Node *relref, char *attname,
int location)
{
RangeTblEntry *rte;
if (IsA(relref, Var) &&
((Var *) relref)->varattno == InvalidAttrNumber)
{
/* Reference the RTE by alias not by actual table name */
rte = GetRTEByRangeTablePosn(pstate,
((Var *) relref)->varno,
((Var *) relref)->varlevelsup);
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("column %s.%s does not exist",
rte->eref->aliasname, attname),
parser_errposition(pstate, location)));
}
else
{
/* Have to do it by reference to the type of the expression */
Oid relTypeId = exprType(relref);
if (ISCOMPLEX(relTypeId))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("column \"%s\" not found in data type %s",
attname, format_type_be(relTypeId)),
parser_errposition(pstate, location)));
else if (relTypeId == RECORDOID)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("could not identify column \"%s\" in record data type",
attname),
parser_errposition(pstate, location)));
else
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("column notation .%s applied to type %s, "
"which is not a composite type",
attname, format_type_be(relTypeId)),
parser_errposition(pstate, location)));
}
}
/* /*
* funcname_signature_string * funcname_signature_string
* Build a string representing a function name, including arg types. * Build a string representing a function name, including arg types.
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_node.c,v 1.105 2009/06/11 14:49:00 momjian Exp $ * $PostgreSQL: pgsql/src/backend/parser/parse_node.c,v 1.106 2009/10/31 01:41:31 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -53,7 +53,12 @@ make_parsestate(ParseState *parentParseState) ...@@ -53,7 +53,12 @@ make_parsestate(ParseState *parentParseState)
if (parentParseState) if (parentParseState)
{ {
pstate->p_sourcetext = parentParseState->p_sourcetext; pstate->p_sourcetext = parentParseState->p_sourcetext;
pstate->p_variableparams = parentParseState->p_variableparams; /* all hooks are copied from parent */
pstate->p_pre_columnref_hook = parentParseState->p_pre_columnref_hook;
pstate->p_post_columnref_hook = parentParseState->p_post_columnref_hook;
pstate->p_paramref_hook = parentParseState->p_paramref_hook;
pstate->p_coerce_param_hook = parentParseState->p_coerce_param_hook;
pstate->p_ref_hook_state = parentParseState->p_ref_hook_state;
} }
return pstate; return pstate;
......
/*-------------------------------------------------------------------------
*
* parse_param.c
* handle parameters in parser
*
* This code covers two cases that are used within the core backend:
* * a fixed list of parameters with known types
* * an expandable list of parameters whose types can optionally
* be determined from context
* In both cases, only explicit $n references (ParamRef nodes) are supported.
*
* Note that other approaches to parameters are possible using the parser
* hooks defined in ParseState.
*
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_param.c,v 2.1 2009/10/31 01:41:31 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include <limits.h>
#include "catalog/pg_type.h"
#include "nodes/nodeFuncs.h"
#include "parser/parse_param.h"
#include "utils/builtins.h"
typedef struct FixedParamState
{
Oid *paramTypes; /* array of parameter type OIDs */
int numParams; /* number of array entries */
} FixedParamState;
/*
* In the varparams case, the caller-supplied OID array (if any) can be
* re-palloc'd larger at need. A zero array entry means that parameter number
* hasn't been seen, while UNKNOWNOID means the parameter has been used but
* its type is not yet known.
*/
typedef struct VarParamState
{
Oid **paramTypes; /* array of parameter type OIDs */
int *numParams; /* number of array entries */
} VarParamState;
static Node *fixed_paramref_hook(ParseState *pstate, ParamRef *pref);
static Node *variable_paramref_hook(ParseState *pstate, ParamRef *pref);
static Node *variable_coerce_param_hook(ParseState *pstate, Param *param,
Oid targetTypeId, int32 targetTypeMod,
int location);
static bool check_parameter_resolution_walker(Node *node, ParseState *pstate);
/*
* Set up to process a query containing references to fixed parameters.
*/
void
parse_fixed_parameters(ParseState *pstate,
Oid *paramTypes, int numParams)
{
FixedParamState *parstate = palloc(sizeof(FixedParamState));
parstate->paramTypes = paramTypes;
parstate->numParams = numParams;
pstate->p_ref_hook_state = (void *) parstate;
pstate->p_paramref_hook = fixed_paramref_hook;
/* no need to use p_coerce_param_hook */
}
/*
* Set up to process a query containing references to variable parameters.
*/
void
parse_variable_parameters(ParseState *pstate,
Oid **paramTypes, int *numParams)
{
VarParamState *parstate = palloc(sizeof(VarParamState));
parstate->paramTypes = paramTypes;
parstate->numParams = numParams;
pstate->p_ref_hook_state = (void *) parstate;
pstate->p_paramref_hook = variable_paramref_hook;
pstate->p_coerce_param_hook = variable_coerce_param_hook;
}
/*
* Transform a ParamRef using fixed parameter types.
*/
static Node *
fixed_paramref_hook(ParseState *pstate, ParamRef *pref)
{
FixedParamState *parstate = (FixedParamState *) pstate->p_ref_hook_state;
int paramno = pref->number;
Param *param;
/* Check parameter number is in range */
if (paramno <= 0 || paramno > parstate->numParams)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_PARAMETER),
errmsg("there is no parameter $%d", paramno),
parser_errposition(pstate, pref->location)));
param = makeNode(Param);
param->paramkind = PARAM_EXTERN;
param->paramid = paramno;
param->paramtype = parstate->paramTypes[paramno - 1];
param->paramtypmod = -1;
param->location = pref->location;
return (Node *) param;
}
/*
* Transform a ParamRef using variable parameter types.
*
* The only difference here is we must enlarge the parameter type array
* as needed.
*/
static Node *
variable_paramref_hook(ParseState *pstate, ParamRef *pref)
{
VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
int paramno = pref->number;
Oid *pptype;
Param *param;
/* Check parameter number is in range */
if (paramno <= 0 || paramno > INT_MAX / sizeof(Oid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_PARAMETER),
errmsg("there is no parameter $%d", paramno),
parser_errposition(pstate, pref->location)));
if (paramno > *parstate->numParams)
{
/* Need to enlarge param array */
if (*parstate->paramTypes)
*parstate->paramTypes = (Oid *) repalloc(*parstate->paramTypes,
paramno * sizeof(Oid));
else
*parstate->paramTypes = (Oid *) palloc(paramno * sizeof(Oid));
/* Zero out the previously-unreferenced slots */
MemSet(*parstate->paramTypes + *parstate->numParams,
0,
(paramno - *parstate->numParams) * sizeof(Oid));
*parstate->numParams = paramno;
}
/* Locate param's slot in array */
pptype = &(*parstate->paramTypes)[paramno - 1];
/* If not seen before, initialize to UNKNOWN type */
if (*pptype == InvalidOid)
*pptype = UNKNOWNOID;
param = makeNode(Param);
param->paramkind = PARAM_EXTERN;
param->paramid = paramno;
param->paramtype = *pptype;
param->paramtypmod = -1;
param->location = pref->location;
return (Node *) param;
}
/*
* Coerce a Param to a query-requested datatype, in the varparams case.
*/
static Node *
variable_coerce_param_hook(ParseState *pstate, Param *param,
Oid targetTypeId, int32 targetTypeMod,
int location)
{
if (param->paramkind == PARAM_EXTERN && param->paramtype == UNKNOWNOID)
{
/*
* Input is a Param of previously undetermined type, and we want to
* update our knowledge of the Param's type.
*/
VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
Oid *paramTypes = *parstate->paramTypes;
int paramno = param->paramid;
if (paramno <= 0 || /* shouldn't happen, but... */
paramno > *parstate->numParams)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_PARAMETER),
errmsg("there is no parameter $%d", paramno),
parser_errposition(pstate, param->location)));
if (paramTypes[paramno - 1] == UNKNOWNOID)
{
/* We've successfully resolved the type */
paramTypes[paramno - 1] = targetTypeId;
}
else if (paramTypes[paramno - 1] == targetTypeId)
{
/* We previously resolved the type, and it matches */
}
else
{
/* Ooops */
ereport(ERROR,
(errcode(ERRCODE_AMBIGUOUS_PARAMETER),
errmsg("inconsistent types deduced for parameter $%d",
paramno),
errdetail("%s versus %s",
format_type_be(paramTypes[paramno - 1]),
format_type_be(targetTypeId)),
parser_errposition(pstate, param->location)));
}
param->paramtype = targetTypeId;
/*
* Note: it is tempting here to set the Param's paramtypmod to
* targetTypeMod, but that is probably unwise because we have no
* infrastructure that enforces that the value delivered for a Param
* will match any particular typmod. Leaving it -1 ensures that a
* run-time length check/coercion will occur if needed.
*/
param->paramtypmod = -1;
/* Use the leftmost of the param's and coercion's locations */
if (location >= 0 &&
(param->location < 0 || location < param->location))
param->location = location;
return (Node *) param;
}
/* Else signal to proceed with normal coercion */
return NULL;
}
/*
* Check for consistent assignment of variable parameters after completion
* of parsing with parse_variable_parameters.
*
* Note: this code intentionally does not check that all parameter positions
* were used, nor that all got non-UNKNOWN types assigned. Caller of parser
* should enforce that if it's important.
*/
void
check_variable_parameters(ParseState *pstate, Query *query)
{
VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
/* If numParams is zero then no Params were generated, so no work */
if (*parstate->numParams > 0)
(void) query_tree_walker(query,
check_parameter_resolution_walker,
(void *) pstate, 0);
}
/*
* Traverse a fully-analyzed tree to verify that parameter symbols
* match their types. We need this because some Params might still
* be UNKNOWN, if there wasn't anything to force their coercion,
* and yet other instances seen later might have gotten coerced.
*/
static bool
check_parameter_resolution_walker(Node *node, ParseState *pstate)
{
if (node == NULL)
return false;
if (IsA(node, Param))
{
Param *param = (Param *) node;
if (param->paramkind == PARAM_EXTERN)
{
VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
int paramno = param->paramid;
if (paramno <= 0 || /* shouldn't happen, but... */
paramno > *parstate->numParams)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_PARAMETER),
errmsg("there is no parameter $%d", paramno),
parser_errposition(pstate, param->location)));
if (param->paramtype != (*parstate->paramTypes)[paramno - 1])
ereport(ERROR,
(errcode(ERRCODE_AMBIGUOUS_PARAMETER),
errmsg("could not determine data type of parameter $%d",
paramno),
parser_errposition(pstate, param->location)));
}
return false;
}
if (IsA(node, Query))
{
/* Recurse into RTE subquery or not-yet-planned sublink subquery */
return query_tree_walker((Query *) node,
check_parameter_resolution_walker,
(void *) pstate, 0);
}
return expression_tree_walker(node, check_parameter_resolution_walker,
(void *) pstate);
}
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.146 2009/10/27 17:11:18 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.147 2009/10/31 01:41:31 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -86,7 +86,17 @@ refnameRangeTblEntry(ParseState *pstate, ...@@ -86,7 +86,17 @@ refnameRangeTblEntry(ParseState *pstate,
{ {
Oid namespaceId; Oid namespaceId;
namespaceId = LookupExplicitNamespace(schemaname); /*
* We can use LookupNamespaceNoError() here because we are only
* interested in finding existing RTEs. Checking USAGE permission
* on the schema is unnecessary since it would have already been
* checked when the RTE was made. Furthermore, we want to report
* "RTE not found", not "no permissions for schema", if the name
* happens to match a schema name the user hasn't got access to.
*/
namespaceId = LookupNamespaceNoError(schemaname);
if (!OidIsValid(relId))
return NULL;
relId = get_relname_relid(refname, namespaceId); relId = get_relname_relid(refname, namespaceId);
if (!OidIsValid(relId)) if (!OidIsValid(relId))
return NULL; return NULL;
...@@ -555,32 +565,6 @@ colNameToVar(ParseState *pstate, char *colname, bool localonly, ...@@ -555,32 +565,6 @@ colNameToVar(ParseState *pstate, char *colname, bool localonly,
return result; return result;
} }
/*
* qualifiedNameToVar
* Search for a qualified column name: either refname.colname or
* schemaname.relname.colname.
*
* If found, return the appropriate Var node.
* If not found, return NULL. If the name proves ambiguous, raise error.
*/
Node *
qualifiedNameToVar(ParseState *pstate,
char *schemaname,
char *refname,
char *colname,
int location)
{
RangeTblEntry *rte;
int sublevels_up;
rte = refnameRangeTblEntry(pstate, schemaname, refname, location,
&sublevels_up);
if (rte == NULL)
return NULL;
return scanRTEForColumn(pstate, rte, colname, location);
}
/* /*
* markRTEForSelectPriv * markRTEForSelectPriv
* Mark the specified column of an RTE as requiring SELECT privilege * Mark the specified column of an RTE as requiring SELECT privilege
...@@ -2389,7 +2373,8 @@ errorMissingRTE(ParseState *pstate, RangeVar *relation) ...@@ -2389,7 +2373,8 @@ errorMissingRTE(ParseState *pstate, RangeVar *relation)
/* /*
* Check to see if there are any potential matches in the query's * Check to see if there are any potential matches in the query's
* rangetable. * rangetable. (Note: cases involving a bad schema name in the
* RangeVar will throw error immediately here. That seems OK.)
*/ */
rte = searchRangeTable(pstate, relation); rte = searchRangeTable(pstate, relation);
......
This diff is collapsed.
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/catalog/namespace.h,v 1.60 2009/10/08 02:39:23 tgl Exp $ * $PostgreSQL: pgsql/src/include/catalog/namespace.h,v 1.61 2009/10/31 01:41:31 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -89,6 +89,7 @@ extern bool TSConfigIsVisible(Oid cfgid); ...@@ -89,6 +89,7 @@ extern bool TSConfigIsVisible(Oid cfgid);
extern void DeconstructQualifiedName(List *names, extern void DeconstructQualifiedName(List *names,
char **nspname_p, char **nspname_p,
char **objname_p); char **objname_p);
extern Oid LookupNamespaceNoError(const char *nspname);
extern Oid LookupExplicitNamespace(const char *nspname); extern Oid LookupExplicitNamespace(const char *nspname);
extern Oid LookupCreationNamespace(const char *nspname); extern Oid LookupCreationNamespace(const char *nspname);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/parser/parse_node.h,v 1.65 2009/10/27 17:11:18 tgl Exp $ * $PostgreSQL: pgsql/src/include/parser/parse_node.h,v 1.66 2009/10/31 01:41:31 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -17,6 +17,20 @@ ...@@ -17,6 +17,20 @@
#include "nodes/parsenodes.h" #include "nodes/parsenodes.h"
#include "utils/relcache.h" #include "utils/relcache.h"
/*
* Function signatures for parser hooks
*/
typedef struct ParseState ParseState;
typedef Node * (*PreParseColumnRefHook) (ParseState *pstate, ColumnRef *cref);
typedef Node * (*PostParseColumnRefHook) (ParseState *pstate, ColumnRef *cref, Node *var);
typedef Node * (*ParseParamRefHook) (ParseState *pstate, ParamRef *pref);
typedef Node * (*CoerceParamHook) (ParseState *pstate, Param *param,
Oid targetTypeId, int32 targetTypeMod,
int location);
/* /*
* State information used during parse analysis * State information used during parse analysis
* *
...@@ -68,17 +82,8 @@ ...@@ -68,17 +82,8 @@
* afterwards (so that any resjunk tlist items needed for the sort/group * afterwards (so that any resjunk tlist items needed for the sort/group
* clauses end up at the end of the query tlist). A WindowDef's location in * clauses end up at the end of the query tlist). A WindowDef's location in
* this list, counting from 1, is the winref number to use to reference it. * this list, counting from 1, is the winref number to use to reference it.
*
* p_paramtypes: an array of p_numparams type OIDs for $n parameter symbols
* (zeroth entry in array corresponds to $1). If p_variableparams is true, the
* set of param types is not predetermined; in that case, a zero array entry
* means that parameter number hasn't been seen, and UNKNOWNOID means the
* parameter has been used but its type is not yet known. NOTE: in a stack
* of ParseStates, only the topmost ParseState contains paramtype info; but
* we copy the p_variableparams flag down to the child nodes for speed in
* coerce_type.
*/ */
typedef struct ParseState struct ParseState
{ {
struct ParseState *parentParseState; /* stack link */ struct ParseState *parentParseState; /* stack link */
const char *p_sourcetext; /* source text, or NULL if not available */ const char *p_sourcetext; /* source text, or NULL if not available */
...@@ -92,12 +97,9 @@ typedef struct ParseState ...@@ -92,12 +97,9 @@ typedef struct ParseState
List *p_future_ctes; /* common table exprs not yet in namespace */ List *p_future_ctes; /* common table exprs not yet in namespace */
CommonTableExpr *p_parent_cte; /* this query's containing CTE */ CommonTableExpr *p_parent_cte; /* this query's containing CTE */
List *p_windowdefs; /* raw representations of window clauses */ List *p_windowdefs; /* raw representations of window clauses */
Oid *p_paramtypes; /* OIDs of types for $n parameter symbols */
int p_numparams; /* allocated size of p_paramtypes[] */
int p_next_resno; /* next targetlist resno to assign */ int p_next_resno; /* next targetlist resno to assign */
List *p_locking_clause; /* raw FOR UPDATE/FOR SHARE info */ List *p_locking_clause; /* raw FOR UPDATE/FOR SHARE info */
Node *p_value_substitute; /* what to replace VALUE with, if any */ Node *p_value_substitute; /* what to replace VALUE with, if any */
bool p_variableparams;
bool p_hasAggs; bool p_hasAggs;
bool p_hasWindowFuncs; bool p_hasWindowFuncs;
bool p_hasSubLinks; bool p_hasSubLinks;
...@@ -106,7 +108,17 @@ typedef struct ParseState ...@@ -106,7 +108,17 @@ typedef struct ParseState
bool p_locked_from_parent; bool p_locked_from_parent;
Relation p_target_relation; Relation p_target_relation;
RangeTblEntry *p_target_rangetblentry; RangeTblEntry *p_target_rangetblentry;
} ParseState;
/*
* Optional hook functions for parser callbacks. These are null unless
* set up by the caller of make_parsestate.
*/
PreParseColumnRefHook p_pre_columnref_hook;
PostParseColumnRefHook p_post_columnref_hook;
ParseParamRefHook p_paramref_hook;
CoerceParamHook p_coerce_param_hook;
void *p_ref_hook_state; /* common passthrough link for above */
};
/* Support for parser_errposition_callback function */ /* Support for parser_errposition_callback function */
typedef struct ParseCallbackState typedef struct ParseCallbackState
......
/*-------------------------------------------------------------------------
*
* parse_param.h
* handle parameters in parser
*
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/parser/parse_param.h,v 1.1 2009/10/31 01:41:31 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef PARSE_PARAM_H
#define PARSE_PARAM_H
#include "parser/parse_node.h"
extern void parse_fixed_parameters(ParseState *pstate,
Oid *paramTypes, int numParams);
extern void parse_variable_parameters(ParseState *pstate,
Oid **paramTypes, int *numParams);
extern void check_variable_parameters(ParseState *pstate, Query *query);
#endif /* PARSE_PARAM_H */
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/parser/parse_relation.h,v 1.66 2009/10/27 17:11:18 tgl Exp $ * $PostgreSQL: pgsql/src/include/parser/parse_relation.h,v 1.67 2009/10/31 01:41:31 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -38,11 +38,6 @@ extern Node *scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, ...@@ -38,11 +38,6 @@ extern Node *scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte,
char *colname, int location); char *colname, int location);
extern Node *colNameToVar(ParseState *pstate, char *colname, bool localonly, extern Node *colNameToVar(ParseState *pstate, char *colname, bool localonly,
int location); int location);
extern Node *qualifiedNameToVar(ParseState *pstate,
char *schemaname,
char *refname,
char *colname,
int location);
extern void markVarForSelectPriv(ParseState *pstate, Var *var, extern void markVarForSelectPriv(ParseState *pstate, Var *var,
RangeTblEntry *rte); RangeTblEntry *rte);
extern Relation parserOpenTable(ParseState *pstate, const RangeVar *relation, extern Relation parserOpenTable(ParseState *pstate, const RangeVar *relation,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment