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);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.246 2009/10/27 17:11:18 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.247 2009/10/31 01:41:31 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -60,8 +60,8 @@ static Node *transformXmlSerialize(ParseState *pstate, XmlSerialize *xs); ...@@ -60,8 +60,8 @@ static Node *transformXmlSerialize(ParseState *pstate, XmlSerialize *xs);
static Node *transformBooleanTest(ParseState *pstate, BooleanTest *b); static Node *transformBooleanTest(ParseState *pstate, BooleanTest *b);
static Node *transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr); static Node *transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr);
static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref); static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref);
static Node *transformWholeRowRef(ParseState *pstate, char *schemaname, static Node *transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte,
char *relname, int location); int location);
static Node *transformIndirection(ParseState *pstate, Node *basenode, static Node *transformIndirection(ParseState *pstate, Node *basenode,
List *indirection); List *indirection);
static Node *transformTypeCast(ParseState *pstate, TypeCast *tc); static Node *transformTypeCast(ParseState *pstate, TypeCast *tc);
...@@ -327,11 +327,64 @@ transformExpr(ParseState *pstate, Node *expr) ...@@ -327,11 +327,64 @@ transformExpr(ParseState *pstate, Node *expr)
return result; return result;
} }
/*
* helper routine for delivering "column does not exist" error message
*
* (Usually we don't have to work this hard, but the general case of field
* selection from an arbitrary node needs it.)
*/
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)));
}
}
static Node * static Node *
transformIndirection(ParseState *pstate, Node *basenode, List *indirection) transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
{ {
Node *result = basenode; Node *result = basenode;
List *subscripts = NIL; List *subscripts = NIL;
int location = exprLocation(basenode);
ListCell *i; ListCell *i;
/* /*
...@@ -350,10 +403,12 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection) ...@@ -350,10 +403,12 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("row expansion via \"*\" is not supported here"), errmsg("row expansion via \"*\" is not supported here"),
parser_errposition(pstate, exprLocation(basenode)))); parser_errposition(pstate, location)));
} }
else else
{ {
Node *newresult;
Assert(IsA(n, String)); Assert(IsA(n, String));
/* process subscripts before this field selection */ /* process subscripts before this field selection */
...@@ -367,11 +422,14 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection) ...@@ -367,11 +422,14 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
NULL); NULL);
subscripts = NIL; subscripts = NIL;
result = ParseFuncOrColumn(pstate, newresult = ParseFuncOrColumn(pstate,
list_make1(n), list_make1(n),
list_make1(result), list_make1(result),
false, false, false, false, false, false,
NULL, true, -1); NULL, true, location);
if (newresult == NULL)
unknown_attribute(pstate, result, strVal(n), location);
result = newresult;
} }
} }
/* process trailing subscripts, if any */ /* process trailing subscripts, if any */
...@@ -387,12 +445,37 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection) ...@@ -387,12 +445,37 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
return result; return result;
} }
/*
* Transform a ColumnRef.
*
* If you find yourself changing this code, see also ExpandColumnRefStar.
*/
static Node * static Node *
transformColumnRef(ParseState *pstate, ColumnRef *cref) transformColumnRef(ParseState *pstate, ColumnRef *cref)
{ {
int numnames = list_length(cref->fields); Node *node = NULL;
Node *node; char *nspname = NULL;
char *relname = NULL;
char *colname = NULL;
RangeTblEntry *rte;
int levels_up; int levels_up;
enum {
CRERR_NO_COLUMN,
CRERR_NO_RTE,
CRERR_WRONG_DB,
CRERR_TOO_MANY
} crerr = CRERR_NO_COLUMN;
/*
* Give the PreParseColumnRefHook, if any, first shot. If it returns
* non-null then that's all, folks.
*/
if (pstate->p_pre_columnref_hook != NULL)
{
node = (*pstate->p_pre_columnref_hook) (pstate, cref);
if (node != NULL)
return node;
}
/*---------- /*----------
* The allowed syntaxes are: * The allowed syntaxes are:
...@@ -417,18 +500,17 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) ...@@ -417,18 +500,17 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
* database name; we check it here and then discard it. * database name; we check it here and then discard it.
*---------- *----------
*/ */
switch (numnames) switch (list_length(cref->fields))
{ {
case 1: case 1:
{ {
Node *field1 = (Node *) linitial(cref->fields); Node *field1 = (Node *) linitial(cref->fields);
char *name1;
Assert(IsA(field1, String)); Assert(IsA(field1, String));
name1 = strVal(field1); colname = strVal(field1);
/* Try to identify as an unqualified column */ /* Try to identify as an unqualified column */
node = colNameToVar(pstate, name1, false, cref->location); node = colNameToVar(pstate, colname, false, cref->location);
if (node == NULL) if (node == NULL)
{ {
...@@ -441,7 +523,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) ...@@ -441,7 +523,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
* have used VALUE as a column name in the past.) * have used VALUE as a column name in the past.)
*/ */
if (pstate->p_value_substitute != NULL && if (pstate->p_value_substitute != NULL &&
strcmp(name1, "value") == 0) strcmp(colname, "value") == 0)
{ {
node = (Node *) copyObject(pstate->p_value_substitute); node = (Node *) copyObject(pstate->p_value_substitute);
...@@ -464,17 +546,12 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) ...@@ -464,17 +546,12 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
* PostQUEL-inspired syntax. The preferred form now is * PostQUEL-inspired syntax. The preferred form now is
* "rel.*". * "rel.*".
*/ */
if (refnameRangeTblEntry(pstate, NULL, name1, rte = refnameRangeTblEntry(pstate, NULL, colname,
cref->location, cref->location,
&levels_up) != NULL) &levels_up);
node = transformWholeRowRef(pstate, NULL, name1, if (rte)
node = transformWholeRowRef(pstate, rte,
cref->location); cref->location);
else
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("column \"%s\" does not exist",
name1),
parser_errposition(pstate, cref->location)));
} }
break; break;
} }
...@@ -482,36 +559,38 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) ...@@ -482,36 +559,38 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
{ {
Node *field1 = (Node *) linitial(cref->fields); Node *field1 = (Node *) linitial(cref->fields);
Node *field2 = (Node *) lsecond(cref->fields); Node *field2 = (Node *) lsecond(cref->fields);
char *name1;
char *name2;
Assert(IsA(field1, String)); Assert(IsA(field1, String));
name1 = strVal(field1); relname = strVal(field1);
/* Locate the referenced RTE */
rte = refnameRangeTblEntry(pstate, nspname, relname,
cref->location,
&levels_up);
if (rte == NULL)
{
crerr = CRERR_NO_RTE;
break;
}
/* Whole-row reference? */ /* Whole-row reference? */
if (IsA(field2, A_Star)) if (IsA(field2, A_Star))
{ {
node = transformWholeRowRef(pstate, NULL, name1, node = transformWholeRowRef(pstate, rte, cref->location);
cref->location);
break; break;
} }
Assert(IsA(field2, String)); Assert(IsA(field2, String));
name2 = strVal(field2); colname = strVal(field2);
/* Try to identify as a once-qualified column */ /* Try to identify as a column of the RTE */
node = qualifiedNameToVar(pstate, NULL, name1, name2, node = scanRTEForColumn(pstate, rte, colname, cref->location);
cref->location);
if (node == NULL) if (node == NULL)
{ {
/* /* Try it as a function call on the whole row */
* Not known as a column of any range-table entry, so try node = transformWholeRowRef(pstate, rte, cref->location);
* it as a function call.
*/
node = transformWholeRowRef(pstate, NULL, name1,
cref->location);
node = ParseFuncOrColumn(pstate, node = ParseFuncOrColumn(pstate,
list_make1(makeString(name2)), list_make1(makeString(colname)),
list_make1(node), list_make1(node),
false, false, false, false, false, false,
NULL, true, cref->location); NULL, true, cref->location);
...@@ -523,36 +602,40 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) ...@@ -523,36 +602,40 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
Node *field1 = (Node *) linitial(cref->fields); Node *field1 = (Node *) linitial(cref->fields);
Node *field2 = (Node *) lsecond(cref->fields); Node *field2 = (Node *) lsecond(cref->fields);
Node *field3 = (Node *) lthird(cref->fields); Node *field3 = (Node *) lthird(cref->fields);
char *name1;
char *name2;
char *name3;
Assert(IsA(field1, String)); Assert(IsA(field1, String));
name1 = strVal(field1); nspname = strVal(field1);
Assert(IsA(field2, String)); Assert(IsA(field2, String));
name2 = strVal(field2); relname = strVal(field2);
/* Locate the referenced RTE */
rte = refnameRangeTblEntry(pstate, nspname, relname,
cref->location,
&levels_up);
if (rte == NULL)
{
crerr = CRERR_NO_RTE;
break;
}
/* Whole-row reference? */ /* Whole-row reference? */
if (IsA(field3, A_Star)) if (IsA(field3, A_Star))
{ {
node = transformWholeRowRef(pstate, name1, name2, node = transformWholeRowRef(pstate, rte, cref->location);
cref->location);
break; break;
} }
Assert(IsA(field3, String)); Assert(IsA(field3, String));
name3 = strVal(field3); colname = strVal(field3);
/* Try to identify as a twice-qualified column */ /* Try to identify as a column of the RTE */
node = qualifiedNameToVar(pstate, name1, name2, name3, node = scanRTEForColumn(pstate, rte, colname, cref->location);
cref->location);
if (node == NULL) if (node == NULL)
{ {
/* Try it as a function call */ /* Try it as a function call on the whole row */
node = transformWholeRowRef(pstate, name1, name2, node = transformWholeRowRef(pstate, rte, cref->location);
cref->location);
node = ParseFuncOrColumn(pstate, node = ParseFuncOrColumn(pstate,
list_make1(makeString(name3)), list_make1(makeString(colname)),
list_make1(node), list_make1(node),
false, false, false, false, false, false,
NULL, true, cref->location); NULL, true, cref->location);
...@@ -565,49 +648,52 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) ...@@ -565,49 +648,52 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
Node *field2 = (Node *) lsecond(cref->fields); Node *field2 = (Node *) lsecond(cref->fields);
Node *field3 = (Node *) lthird(cref->fields); Node *field3 = (Node *) lthird(cref->fields);
Node *field4 = (Node *) lfourth(cref->fields); Node *field4 = (Node *) lfourth(cref->fields);
char *name1; char *catname;
char *name2;
char *name3;
char *name4;
Assert(IsA(field1, String)); Assert(IsA(field1, String));
name1 = strVal(field1); catname = strVal(field1);
Assert(IsA(field2, String)); Assert(IsA(field2, String));
name2 = strVal(field2); nspname = strVal(field2);
Assert(IsA(field3, String)); Assert(IsA(field3, String));
name3 = strVal(field3); relname = strVal(field3);
/* /*
* We check the catalog name and then ignore it. * We check the catalog name and then ignore it.
*/ */
if (strcmp(name1, get_database_name(MyDatabaseId)) != 0) if (strcmp(catname, get_database_name(MyDatabaseId)) != 0)
ereport(ERROR, {
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), crerr = CRERR_WRONG_DB;
errmsg("cross-database references are not implemented: %s", break;
NameListToString(cref->fields)), }
parser_errposition(pstate, cref->location)));
/* Locate the referenced RTE */
rte = refnameRangeTblEntry(pstate, nspname, relname,
cref->location,
&levels_up);
if (rte == NULL)
{
crerr = CRERR_NO_RTE;
break;
}
/* Whole-row reference? */ /* Whole-row reference? */
if (IsA(field4, A_Star)) if (IsA(field4, A_Star))
{ {
node = transformWholeRowRef(pstate, name2, name3, node = transformWholeRowRef(pstate, rte, cref->location);
cref->location);
break; break;
} }
Assert(IsA(field4, String)); Assert(IsA(field4, String));
name4 = strVal(field4); colname = strVal(field4);
/* Try to identify as a twice-qualified column */ /* Try to identify as a column of the RTE */
node = qualifiedNameToVar(pstate, name2, name3, name4, node = scanRTEForColumn(pstate, rte, colname, cref->location);
cref->location);
if (node == NULL) if (node == NULL)
{ {
/* Try it as a function call */ /* Try it as a function call on the whole row */
node = transformWholeRowRef(pstate, name2, name3, node = transformWholeRowRef(pstate, rte, cref->location);
cref->location);
node = ParseFuncOrColumn(pstate, node = ParseFuncOrColumn(pstate,
list_make1(makeString(name4)), list_make1(makeString(colname)),
list_make1(node), list_make1(node),
false, false, false, false, false, false,
NULL, true, cref->location); NULL, true, cref->location);
...@@ -615,86 +701,101 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) ...@@ -615,86 +701,101 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
break; break;
} }
default: default:
ereport(ERROR, crerr = CRERR_TOO_MANY; /* too many dotted names */
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("improper qualified name (too many dotted names): %s",
NameListToString(cref->fields)),
parser_errposition(pstate, cref->location)));
node = NULL; /* keep compiler quiet */
break; break;
} }
return node;
}
/*
* Locate the parameter type info for the given parameter number, and
* return a pointer to it.
*/
static Oid *
find_param_type(ParseState *pstate, int paramno, int location)
{
Oid *result;
/* /*
* Find topmost ParseState, which is where paramtype info lives. * Now give the PostParseColumnRefHook, if any, a chance. We pass the
* translation-so-far so that it can throw an error if it wishes in the
* case that it has a conflicting interpretation of the ColumnRef.
* (If it just translates anyway, we'll throw an error, because we can't
* undo whatever effects the preceding steps may have had on the pstate.)
* If it returns NULL, use the standard translation, or throw a suitable
* error if there is none.
*/ */
while (pstate->parentParseState != NULL) if (pstate->p_post_columnref_hook != NULL)
pstate = pstate->parentParseState; {
Node *hookresult;
/* Check parameter number is in range */ hookresult = (*pstate->p_post_columnref_hook) (pstate, cref, node);
if (paramno <= 0) /* probably can't happen? */ if (node == NULL)
node = hookresult;
else if (hookresult != NULL)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_PARAMETER), (errcode(ERRCODE_AMBIGUOUS_COLUMN),
errmsg("there is no parameter $%d", paramno), errmsg("column reference \"%s\" is ambiguous",
parser_errposition(pstate, location))); NameListToString(cref->fields)),
if (paramno > pstate->p_numparams) parser_errposition(pstate, cref->location)));
}
/*
* Throw error if no translation found.
*/
if (node == NULL)
{ {
if (!pstate->p_variableparams) switch (crerr)
{
case CRERR_NO_COLUMN:
if (relname)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_PARAMETER), (errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("there is no parameter $%d", paramno), errmsg("column %s.%s does not exist",
parser_errposition(pstate, location))); relname, colname),
/* Okay to enlarge param array */ parser_errposition(pstate, cref->location)));
if (pstate->p_paramtypes)
pstate->p_paramtypes = (Oid *) repalloc(pstate->p_paramtypes,
paramno * sizeof(Oid));
else else
pstate->p_paramtypes = (Oid *) palloc(paramno * sizeof(Oid)); ereport(ERROR,
/* Zero out the previously-unreferenced slots */ (errcode(ERRCODE_UNDEFINED_COLUMN),
MemSet(pstate->p_paramtypes + pstate->p_numparams, errmsg("column \"%s\" does not exist",
0, colname),
(paramno - pstate->p_numparams) * sizeof(Oid)); parser_errposition(pstate, cref->location)));
pstate->p_numparams = paramno; break;
case CRERR_NO_RTE:
errorMissingRTE(pstate, makeRangeVar(nspname, relname,
cref->location));
break;
case CRERR_WRONG_DB:
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cross-database references are not implemented: %s",
NameListToString(cref->fields)),
parser_errposition(pstate, cref->location)));
break;
case CRERR_TOO_MANY:
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("improper qualified name (too many dotted names): %s",
NameListToString(cref->fields)),
parser_errposition(pstate, cref->location)));
break;
} }
result = &pstate->p_paramtypes[paramno - 1];
if (pstate->p_variableparams)
{
/* If not seen before, initialize to UNKNOWN type */
if (*result == InvalidOid)
*result = UNKNOWNOID;
} }
return result; return node;
} }
static Node * static Node *
transformParamRef(ParseState *pstate, ParamRef *pref) transformParamRef(ParseState *pstate, ParamRef *pref)
{ {
int paramno = pref->number; Node *result;
Oid *pptype = find_param_type(pstate, paramno, pref->location);
Param *param;
param = makeNode(Param); /*
param->paramkind = PARAM_EXTERN; * The core parser knows nothing about Params. If a hook is supplied,
param->paramid = paramno; * call it. If not, or if the hook returns NULL, throw a generic error.
param->paramtype = *pptype; */
param->paramtypmod = -1; if (pstate->p_paramref_hook != NULL)
param->location = pref->location; result = (*pstate->p_paramref_hook) (pstate, pref);
else
result = NULL;
return (Node *) param; if (result == NULL)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_PARAMETER),
errmsg("there is no parameter $%d", pref->number),
parser_errposition(pstate, pref->location)));
return result;
} }
/* Test whether an a_expr is a plain NULL constant or not */ /* Test whether an a_expr is a plain NULL constant or not */
...@@ -1861,27 +1962,34 @@ transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr) ...@@ -1861,27 +1962,34 @@ transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr)
&sublevels_up); &sublevels_up);
Assert(sublevels_up == 0); Assert(sublevels_up == 0);
/* If a parameter is used, it must be of type REFCURSOR */ /*
* If a parameter is used, it must be of type REFCURSOR. To verify
* that the parameter hooks think so, build a dummy ParamRef and
* transform it.
*/
if (cexpr->cursor_name == NULL) if (cexpr->cursor_name == NULL)
{ {
Oid *pptype = find_param_type(pstate, cexpr->cursor_param, -1); ParamRef *p = makeNode(ParamRef);
Node *n;
if (pstate->p_variableparams && *pptype == UNKNOWNOID)
{ p->number = cexpr->cursor_param;
/* resolve unknown param type as REFCURSOR */ p->location = -1;
*pptype = REFCURSOROID; n = transformParamRef(pstate, p);
} /* Allow the parameter type to be inferred if it's unknown */
else if (*pptype != REFCURSOROID) if (exprType(n) == UNKNOWNOID)
{ n = coerce_type(pstate, n, UNKNOWNOID,
REFCURSOROID, -1,
COERCION_IMPLICIT, COERCE_IMPLICIT_CAST,
-1);
if (exprType(n) != REFCURSOROID)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_AMBIGUOUS_PARAMETER), (errcode(ERRCODE_AMBIGUOUS_PARAMETER),
errmsg("inconsistent types deduced for parameter $%d", errmsg("inconsistent types deduced for parameter $%d",
cexpr->cursor_param), cexpr->cursor_param),
errdetail("%s versus %s", errdetail("%s versus %s",
format_type_be(*pptype), format_type_be(exprType(n)),
format_type_be(REFCURSOROID)))); format_type_be(REFCURSOROID))));
} }
}
return (Node *) cexpr; return (Node *) cexpr;
} }
...@@ -1896,23 +2004,14 @@ transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr) ...@@ -1896,23 +2004,14 @@ transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr)
* a rowtype; either a named composite type, or RECORD. * a rowtype; either a named composite type, or RECORD.
*/ */
static Node * static Node *
transformWholeRowRef(ParseState *pstate, char *schemaname, char *relname, transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte, int location)
int location)
{ {
Var *result; Var *result;
RangeTblEntry *rte;
int vnum; int vnum;
int sublevels_up; int sublevels_up;
Oid toid; Oid toid;
/* Look up the referenced RTE, failing if not present */ /* Find the RTE's rangetable location */
rte = refnameRangeTblEntry(pstate, schemaname, relname, location,
&sublevels_up);
if (rte == NULL)
errorMissingRTE(pstate,
makeRangeVar(schemaname, relname, location));
vnum = RTERangeTablePosn(pstate, rte, &sublevels_up); vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
......
...@@ -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);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.173 2009/10/21 20:22:38 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.174 2009/10/31 01:41:31 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -48,6 +48,10 @@ static List *ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, ...@@ -48,6 +48,10 @@ static List *ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref,
static List *ExpandAllTables(ParseState *pstate, int location); static List *ExpandAllTables(ParseState *pstate, int location);
static List *ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind, static List *ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind,
bool targetlist); bool targetlist);
static List *ExpandSingleTable(ParseState *pstate, RangeTblEntry *rte,
int location, bool targetlist);
static List *ExpandRowReference(ParseState *pstate, Node *expr,
bool targetlist);
static int FigureColnameInternal(Node *node, char **name); static int FigureColnameInternal(Node *node, char **name);
...@@ -879,90 +883,135 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, ...@@ -879,90 +883,135 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref,
* Target item is relation.*, expand that table * Target item is relation.*, expand that table
* *
* (e.g., SELECT emp.*, dname FROM emp, dept) * (e.g., SELECT emp.*, dname FROM emp, dept)
*
* Note: this code is a lot like transformColumnRef; it's tempting
* to call that instead and then replace the resulting whole-row Var
* with a list of Vars. However, that would leave us with the
* RTE's selectedCols bitmap showing the whole row as needing
* select permission, as well as the individual columns. That would
* be incorrect (since columns added later shouldn't need select
* permissions). We could try to remove the whole-row permission bit
* after the fact, but duplicating code is less messy.
*/ */
char *schemaname; char *nspname = NULL;
char *relname; char *relname = NULL;
RangeTblEntry *rte; RangeTblEntry *rte = NULL;
int sublevels_up; int levels_up;
int rtindex; enum {
CRSERR_NO_RTE,
CRSERR_WRONG_DB,
CRSERR_TOO_MANY
} crserr = CRSERR_NO_RTE;
/*
* Give the PreParseColumnRefHook, if any, first shot. If it returns
* non-null then we should use that expression.
*/
if (pstate->p_pre_columnref_hook != NULL)
{
Node *node;
node = (*pstate->p_pre_columnref_hook) (pstate, cref);
if (node != NULL)
return ExpandRowReference(pstate, node, targetlist);
}
switch (numnames) switch (numnames)
{ {
case 2: case 2:
schemaname = NULL;
relname = strVal(linitial(fields)); relname = strVal(linitial(fields));
rte = refnameRangeTblEntry(pstate, nspname, relname,
cref->location,
&levels_up);
break; break;
case 3: case 3:
schemaname = strVal(linitial(fields)); nspname = strVal(linitial(fields));
relname = strVal(lsecond(fields)); relname = strVal(lsecond(fields));
rte = refnameRangeTblEntry(pstate, nspname, relname,
cref->location,
&levels_up);
break; break;
case 4: case 4:
{ {
char *name1 = strVal(linitial(fields)); char *catname = strVal(linitial(fields));
/* /*
* We check the catalog name and then ignore it. * We check the catalog name and then ignore it.
*/ */
if (strcmp(name1, get_database_name(MyDatabaseId)) != 0) if (strcmp(catname, get_database_name(MyDatabaseId)) != 0)
ereport(ERROR, {
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), crserr = CRSERR_WRONG_DB;
errmsg("cross-database references are not implemented: %s", break;
NameListToString(fields)), }
parser_errposition(pstate, cref->location))); nspname = strVal(lsecond(fields));
schemaname = strVal(lsecond(fields));
relname = strVal(lthird(fields)); relname = strVal(lthird(fields));
rte = refnameRangeTblEntry(pstate, nspname, relname,
cref->location,
&levels_up);
break; break;
} }
default: default:
ereport(ERROR, crserr = CRSERR_TOO_MANY;
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("improper qualified name (too many dotted names): %s",
NameListToString(fields)),
parser_errposition(pstate, cref->location)));
schemaname = NULL; /* keep compiler quiet */
relname = NULL;
break; break;
} }
rte = refnameRangeTblEntry(pstate, schemaname, relname, cref->location, /*
&sublevels_up); * Now give the PostParseColumnRefHook, if any, a chance.
if (rte == NULL) * We cheat a bit by passing the RangeTblEntry, not a Var,
errorMissingRTE(pstate, * as the planned translation. (A single Var wouldn't be
makeRangeVar(schemaname, relname, cref->location)); * strictly correct anyway. This convention allows hooks
* that really care to know what is happening.)
rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up); */
if (pstate->p_post_columnref_hook != NULL)
{
Node *node;
if (targetlist) node = (*pstate->p_post_columnref_hook) (pstate, cref,
(Node *) rte);
if (node != NULL)
{ {
/* expandRelAttrs handles permissions marking */ if (rte != NULL)
return expandRelAttrs(pstate, rte, rtindex, sublevels_up, ereport(ERROR,
cref->location); (errcode(ERRCODE_AMBIGUOUS_COLUMN),
errmsg("column reference \"%s\" is ambiguous",
NameListToString(cref->fields)),
parser_errposition(pstate, cref->location)));
return ExpandRowReference(pstate, node, targetlist);
}
} }
else
{
List *vars;
ListCell *l;
expandRTE(rte, rtindex, sublevels_up, cref->location, false,
NULL, &vars);
/* /*
* Require read access to the table. This is normally redundant * Throw error if no translation found.
* with the markVarForSelectPriv calls below, but not if the table
* has zero columns.
*/ */
rte->requiredPerms |= ACL_SELECT; if (rte == NULL)
/* Require read access to each column */
foreach(l, vars)
{ {
Var *var = (Var *) lfirst(l); switch (crserr)
{
markVarForSelectPriv(pstate, var, rte); case CRSERR_NO_RTE:
errorMissingRTE(pstate, makeRangeVar(nspname, relname,
cref->location));
break;
case CRSERR_WRONG_DB:
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cross-database references are not implemented: %s",
NameListToString(cref->fields)),
parser_errposition(pstate, cref->location)));
break;
case CRSERR_TOO_MANY:
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("improper qualified name (too many dotted names): %s",
NameListToString(cref->fields)),
parser_errposition(pstate, cref->location)));
break;
} }
return vars;
} }
/*
* OK, expand the RTE into fields.
*/
return ExpandSingleTable(pstate, rte, cref->location, targetlist);
} }
} }
...@@ -1015,11 +1064,7 @@ static List * ...@@ -1015,11 +1064,7 @@ static List *
ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind, ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind,
bool targetlist) bool targetlist)
{ {
List *result = NIL;
Node *expr; Node *expr;
TupleDesc tupleDesc;
int numAttrs;
int i;
/* Strip off the '*' to create a reference to the rowtype object */ /* Strip off the '*' to create a reference to the rowtype object */
ind = copyObject(ind); ind = copyObject(ind);
...@@ -1029,7 +1074,102 @@ ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind, ...@@ -1029,7 +1074,102 @@ ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind,
/* And transform that */ /* And transform that */
expr = transformExpr(pstate, (Node *) ind); expr = transformExpr(pstate, (Node *) ind);
/* Expand the rowtype expression into individual fields */
return ExpandRowReference(pstate, expr, targetlist);
}
/*
* ExpandSingleTable()
* Transforms foo.* into a list of expressions or targetlist entries.
*
* This handles the case where foo has been determined to be a simple
* reference to an RTE, so we can just generate Vars for the expressions.
*
* The referenced columns are marked as requiring SELECT access.
*/
static List *
ExpandSingleTable(ParseState *pstate, RangeTblEntry *rte,
int location, bool targetlist)
{
int sublevels_up;
int rtindex;
rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up);
if (targetlist)
{
/* expandRelAttrs handles permissions marking */
return expandRelAttrs(pstate, rte, rtindex, sublevels_up,
location);
}
else
{
List *vars;
ListCell *l;
expandRTE(rte, rtindex, sublevels_up, location, false,
NULL, &vars);
/*
* Require read access to the table. This is normally redundant
* with the markVarForSelectPriv calls below, but not if the table
* has zero columns.
*/
rte->requiredPerms |= ACL_SELECT;
/* Require read access to each column */
foreach(l, vars)
{
Var *var = (Var *) lfirst(l);
markVarForSelectPriv(pstate, var, rte);
}
return vars;
}
}
/*
* ExpandRowReference()
* Transforms foo.* into a list of expressions or targetlist entries.
*
* This handles the case where foo is an arbitrary expression of composite
* type.
*/
static List *
ExpandRowReference(ParseState *pstate, Node *expr,
bool targetlist)
{
List *result = NIL;
TupleDesc tupleDesc;
int numAttrs;
int i;
/* /*
* If the rowtype expression is a whole-row Var, we can expand the fields
* as simple Vars. Note: if the RTE is a relation, this case leaves us
* with the RTE's selectedCols bitmap showing the whole row as needing
* select permission, as well as the individual columns. However, we can
* only get here for weird notations like (table.*).*, so it's not worth
* trying to clean up --- arguably, the permissions marking is correct
* anyway for such cases.
*/
if (IsA(expr, Var) &&
((Var *) expr)->varattno == InvalidAttrNumber)
{
Var *var = (Var *) expr;
RangeTblEntry *rte;
rte = GetRTEByRangeTablePosn(pstate, var->varno, var->varlevelsup);
return ExpandSingleTable(pstate, rte, var->location, targetlist);
}
/*
* Otherwise we have to do it the hard way. Our current implementation
* is to generate multiple copies of the expression and do FieldSelects.
* (This can be pretty inefficient if the expression involves nontrivial
* computation :-(.)
*
* Verify it's a composite type, and get the tupdesc. We use * Verify it's a composite type, and get the tupdesc. We use
* get_expr_result_type() because that can handle references to functions * get_expr_result_type() because that can handle references to functions
* returning anonymous record types. If that fails, use * returning anonymous record types. If that fails, use
...@@ -1053,56 +1193,30 @@ ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind, ...@@ -1053,56 +1193,30 @@ ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind,
for (i = 0; i < numAttrs; i++) for (i = 0; i < numAttrs; i++)
{ {
Form_pg_attribute att = tupleDesc->attrs[i]; Form_pg_attribute att = tupleDesc->attrs[i];
Node *fieldnode; FieldSelect *fselect;
if (att->attisdropped) if (att->attisdropped)
continue; continue;
/* fselect = makeNode(FieldSelect);
* If we got a whole-row Var from the rowtype reference, we can expand
* the fields as simple Vars. Otherwise we must generate multiple
* copies of the rowtype reference and do FieldSelects.
*/
if (IsA(expr, Var) &&
((Var *) expr)->varattno == InvalidAttrNumber)
{
Var *var = (Var *) expr;
Var *newvar;
newvar = makeVar(var->varno,
i + 1,
att->atttypid,
att->atttypmod,
var->varlevelsup);
newvar->location = var->location;
fieldnode = (Node *) newvar;
}
else
{
FieldSelect *fselect = makeNode(FieldSelect);
fselect->arg = (Expr *) copyObject(expr); fselect->arg = (Expr *) copyObject(expr);
fselect->fieldnum = i + 1; fselect->fieldnum = i + 1;
fselect->resulttype = att->atttypid; fselect->resulttype = att->atttypid;
fselect->resulttypmod = att->atttypmod; fselect->resulttypmod = att->atttypmod;
fieldnode = (Node *) fselect;
}
if (targetlist) if (targetlist)
{ {
/* add TargetEntry decoration */ /* add TargetEntry decoration */
TargetEntry *te; TargetEntry *te;
te = makeTargetEntry((Expr *) fieldnode, te = makeTargetEntry((Expr *) fselect,
(AttrNumber) pstate->p_next_resno++, (AttrNumber) pstate->p_next_resno++,
pstrdup(NameStr(att->attname)), pstrdup(NameStr(att->attname)),
false); false);
result = lappend(result, te); result = lappend(result, te);
} }
else else
result = lappend(result, fieldnode); result = lappend(result, fselect);
} }
return result; return result;
......
...@@ -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