Commit 27a4f06a authored by Tom Lane's avatar Tom Lane

Get rid of crocky use of RangeVar nodes in parser to represent partially

transformed whole-row variables.  Cleaner to use regular whole-row Vars.
parent 94d8da8f
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.167 2004/03/24 22:40:28 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.168 2004/04/02 19:06:57 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
......@@ -2550,16 +2550,6 @@ expression_tree_walker(Node *node,
return true;
}
break;
case T_RangeVar:
/*
* Give a useful complaint if someone uses a bare relation name
* in an expression (see comments in transformColumnRef()).
*/
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("relation reference \"%s\" cannot be used in an expression",
((RangeVar *) node)->relname)));
break;
default:
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(node));
......@@ -3031,16 +3021,6 @@ expression_tree_mutator(Node *node,
return (Node *) newnode;
}
break;
case T_RangeVar:
/*
* Give a useful complaint if someone uses a bare relation name
* in an expression (see comments in transformColumnRef()).
*/
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("relation reference \"%s\" cannot be used in an expression",
((RangeVar *) node)->relname)));
break;
default:
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(node));
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.167 2004/03/24 22:40:29 tgl Exp $
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.168 2004/04/02 19:06:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -40,6 +40,8 @@ bool Transform_null_equals = false;
static Node *typecast_expression(ParseState *pstate, Node *expr,
TypeName *typename);
static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref);
static Node *transformWholeRowRef(ParseState *pstate, char *schemaname,
char *relname);
static Node *transformIndirection(ParseState *pstate, Node *basenode,
List *indirection);
......@@ -932,34 +934,29 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
{
int numnames = length(cref->fields);
Node *node;
RangeVar *rv;
int levels_up;
/*----------
* The allowed syntaxes are:
*
* A First try to resolve as unqualified column name;
* if no luck, try to resolve as unqual. table name (A.*).
* A.B A is an unqual. table name; B is either a
* if no luck, try to resolve as unqualified table name (A.*).
* A.B A is an unqualified table name; B is either a
* column or function name (trying column name first).
* A.B.C schema A, table B, col or func name C.
* A.B.C.D catalog A, schema B, table C, col or func D.
* A.* A is an unqual. table name; means whole-row value.
* A.* A is an unqualified table name; means whole-row value.
* A.B.* whole-row value of table B in schema A.
* A.B.C.* whole-row value of table C in schema B in catalog A.
*
* We do not need to cope with bare "*"; that will only be accepted by
* the grammar at the top level of a SELECT list, and transformTargetList
* will take care of it before it ever gets here.
* will take care of it before it ever gets here. Also, "A.*" etc will
* be expanded by transformTargetList if they appear at SELECT top level,
* so here we are only going to see them as function or operator inputs.
*
* Currently, if a catalog name is given then it must equal the current
* database name; we check it here and then discard it.
*
* For whole-row references, the result is an untransformed RangeVar,
* which will work as the argument to a function call, but not in any
* other context at present. (We could instead coerce to a whole-row Var,
* but that will fail for subselect and join RTEs, because there is no
* pg_type entry for their rowtypes.)
*----------
*/
switch (numnames)
......@@ -1001,16 +998,12 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
if (cref->indirection == NIL &&
refnameRangeTblEntry(pstate, NULL, name,
&levels_up) != NULL)
{
rv = makeNode(RangeVar);
rv->relname = name;
rv->inhOpt = INH_DEFAULT;
node = (Node *) rv;
}
node = transformWholeRowRef(pstate, NULL, name);
else
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("column \"%s\" does not exist", name)));
errmsg("column \"%s\" does not exist",
name)));
}
break;
}
......@@ -1022,10 +1015,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
/* Whole-row reference? */
if (strcmp(name2, "*") == 0)
{
rv = makeNode(RangeVar);
rv->relname = name1;
rv->inhOpt = INH_DEFAULT;
node = (Node *) rv;
node = transformWholeRowRef(pstate, NULL, name1);
break;
}
......@@ -1038,12 +1028,10 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
* try it as a function call. Here, we will create an
* implicit RTE for tables not already entered.
*/
rv = makeNode(RangeVar);
rv->relname = name1;
rv->inhOpt = INH_DEFAULT;
node = transformWholeRowRef(pstate, NULL, name1);
node = ParseFuncOrColumn(pstate,
makeList1(makeString(name2)),
makeList1(rv),
makeList1(node),
false, false, true);
}
break;
......@@ -1057,11 +1045,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
/* Whole-row reference? */
if (strcmp(name3, "*") == 0)
{
rv = makeNode(RangeVar);
rv->schemaname = name1;
rv->relname = name2;
rv->inhOpt = INH_DEFAULT;
node = (Node *) rv;
node = transformWholeRowRef(pstate, name1, name2);
break;
}
......@@ -1070,13 +1054,10 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
if (node == NULL)
{
/* Try it as a function call */
rv = makeNode(RangeVar);
rv->schemaname = name1;
rv->relname = name2;
rv->inhOpt = INH_DEFAULT;
node = transformWholeRowRef(pstate, name1, name2);
node = ParseFuncOrColumn(pstate,
makeList1(makeString(name3)),
makeList1(rv),
makeList1(node),
false, false, true);
}
break;
......@@ -1100,11 +1081,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
/* Whole-row reference? */
if (strcmp(name4, "*") == 0)
{
rv = makeNode(RangeVar);
rv->schemaname = name2;
rv->relname = name3;
rv->inhOpt = INH_DEFAULT;
node = (Node *) rv;
node = transformWholeRowRef(pstate, name2, name3);
break;
}
......@@ -1113,13 +1090,10 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
if (node == NULL)
{
/* Try it as a function call */
rv = makeNode(RangeVar);
rv->schemaname = name2;
rv->relname = name3;
rv->inhOpt = INH_DEFAULT;
node = transformWholeRowRef(pstate, name2, name3);
node = ParseFuncOrColumn(pstate,
makeList1(makeString(name4)),
makeList1(rv),
makeList1(node),
false, false, true);
}
break;
......@@ -1136,6 +1110,99 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
return transformIndirection(pstate, node, cref->indirection);
}
/*
* Construct a whole-row reference to represent the notation "relation.*".
*
* In simple cases, this will be a Var with varno set to the correct range
* table entry, and varattno == 0 to signal that it references the whole
* tuple. (Use of zero here is unclean, since it could easily be confused
* with error cases, but it's not worth changing now.) The vartype indicates
* a rowtype; either a named composite type, or RECORD.
*
* We also need the ability to build a row-constructor expression, but the
* infrastructure for that doesn't exist just yet.
*/
static Node *
transformWholeRowRef(ParseState *pstate, char *schemaname, char *relname)
{
Node *result;
RangeTblEntry *rte;
int vnum;
int sublevels_up;
Oid toid;
/* Look up the referenced RTE, creating it if needed */
rte = refnameRangeTblEntry(pstate, schemaname, relname,
&sublevels_up);
if (rte == NULL)
rte = addImplicitRTE(pstate, makeRangeVar(schemaname, relname));
vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
/* Build the appropriate referencing node */
switch (rte->rtekind)
{
case RTE_RELATION:
/* relation: the rowtype is a named composite type */
toid = get_rel_type_id(rte->relid);
if (!OidIsValid(toid))
elog(ERROR, "could not find type OID for relation %u",
rte->relid);
result = (Node *) makeVar(vnum,
InvalidAttrNumber,
toid,
-1,
sublevels_up);
break;
case RTE_FUNCTION:
toid = exprType(rte->funcexpr);
if (toid == RECORDOID || get_typtype(toid) == 'c')
{
/* func returns composite; same as relation case */
result = (Node *) makeVar(vnum,
InvalidAttrNumber,
toid,
-1,
sublevels_up);
}
else
{
/*
* func returns scalar; instead of making a whole-row Var,
* just reference the function's scalar output. (XXX this
* seems a tad inconsistent, especially if "f.*" was
* explicitly written ...)
*/
result = (Node *) makeVar(vnum,
1,
toid,
-1,
sublevels_up);
}
break;
default:
/*
* RTE is a join or subselect. For the moment we represent this
* as a whole-row Var of RECORD type, but this will not actually
* work; need a row-constructor expression instead.
*
* XXX after fixing, be sure that unknown_attribute still
* does the right thing.
*/
result = (Node *) makeVar(vnum,
InvalidAttrNumber,
RECORDOID,
-1,
sublevels_up);
break;
}
return result;
}
/*
* exprType -
* returns the Oid of the type of the expression. (Used for typechecking.)
......@@ -1294,19 +1361,6 @@ exprType(Node *expr)
case T_SetToDefault:
type = ((SetToDefault *) expr)->typeId;
break;
case T_RangeVar:
/*
* If someone uses a bare relation name in an expression, we
* will likely first notice a problem here (see comments in
* transformColumnRef()). Issue an appropriate error message.
*/
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("relation reference \"%s\" cannot be used in an expression",
((RangeVar *) expr)->relname)));
type = InvalidOid; /* keep compiler quiet */
break;
default:
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
type = InvalidOid; /* keep compiler quiet */
......
This diff is collapsed.
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.92 2004/01/14 23:01:55 tgl Exp $
* $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.93 2004/04/02 19:06:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -41,8 +41,6 @@ static Node *scanNameSpaceForRelid(ParseState *pstate, Node *nsnode,
Oid relid);
static void scanNameSpaceForConflict(ParseState *pstate, Node *nsnode,
RangeTblEntry *rte1, const char *aliasname1);
static Node *scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte,
char *colname);
static bool isForUpdate(ParseState *pstate, char *refname);
static bool get_rte_attribute_is_dropped(RangeTblEntry *rte,
AttrNumber attnum);
......@@ -424,6 +422,24 @@ RTERangeTablePosn(ParseState *pstate, RangeTblEntry *rte, int *sublevels_up)
return 0; /* keep compiler quiet */
}
/*
* Given an RT index and nesting depth, find the corresponding RTE.
* This is the inverse of RTERangeTablePosn.
*/
RangeTblEntry *
GetRTEByRangeTablePosn(ParseState *pstate,
int varno,
int sublevels_up)
{
while (sublevels_up-- > 0)
{
pstate = pstate->parentParseState;
Assert(pstate != NULL);
}
Assert(varno > 0 && varno <= length(pstate->p_rtable));
return rt_fetch(varno, pstate->p_rtable);
}
/*
* scanRTEForColumn
* Search the column names of a single RTE for the given name.
......@@ -439,7 +455,7 @@ RTERangeTablePosn(ParseState *pstate, RangeTblEntry *rte, int *sublevels_up)
* expression can only appear in a FROM clause, and any table named in
* FROM will be marked as requiring read access from the beginning.
*/
static Node *
Node *
scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
{
Node *result = NULL;
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.115 2004/02/13 01:08:20 tgl Exp $
* $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.116 2004/04/02 19:06:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -60,14 +60,6 @@ transformTargetEntry(ParseState *pstate,
if (expr == NULL)
expr = transformExpr(pstate, node);
if (IsA(expr, RangeVar))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("relation reference \"%s\" cannot be used as a select-list entry",
((RangeVar *) expr)->relname),
errhint("Write \"%s\".* to denote all the columns of the relation.",
((RangeVar *) expr)->relname)));
type_id = exprType(expr);
type_mod = exprTypmod(expr);
......@@ -243,21 +235,12 @@ markTargetListOrigins(ParseState *pstate, List *targetlist)
static void
markTargetListOrigin(ParseState *pstate, Resdom *res, Var *var)
{
Index levelsup;
RangeTblEntry *rte;
AttrNumber attnum;
if (var == NULL || !IsA(var, Var))
return;
levelsup = var->varlevelsup;
while (levelsup-- > 0)
{
pstate = pstate->parentParseState;
Assert(pstate != NULL);
}
Assert(var->varno > 0 &&
(int) var->varno <= length(pstate->p_rtable));
rte = rt_fetch(var->varno, pstate->p_rtable);
rte = GetRTEByRangeTablePosn(pstate, var->varno, var->varlevelsup);
attnum = var->varattno;
switch (rte->rtekind)
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/parser/parse_relation.h,v 1.42 2003/11/29 22:41:09 pgsql Exp $
* $PostgreSQL: pgsql/src/include/parser/parse_relation.h,v 1.43 2004/04/02 19:07:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -27,6 +27,11 @@ extern void checkNameSpaceConflicts(ParseState *pstate, Node *namespace1,
extern int RTERangeTablePosn(ParseState *pstate,
RangeTblEntry *rte,
int *sublevels_up);
extern RangeTblEntry *GetRTEByRangeTablePosn(ParseState *pstate,
int varno,
int sublevels_up);
extern Node *scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte,
char *colname);
extern Node *colnameToVar(ParseState *pstate, char *colname);
extern Node *qualifiedNameToVar(ParseState *pstate,
char *schemaname,
......
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