Commit a4996a89 authored by Tom Lane's avatar Tom Lane

Replace the parser's namespace tree (which formerly had the same

representation as the jointree) with two lists of RTEs, one showing
the RTEs accessible by qualified names, and the other showing the RTEs
accessible by unqualified names.  I think this is conceptually simpler
than what we did before, and it's sure a whole lot easier to search.
This seems to eliminate the parse-time bottleneck for deeply nested
JOIN structures that was exhibited by phil@vodafone.
parent efe0d080
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.284 2005/04/14 20:03:23 tgl Exp $ * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.285 2005/06/05 00:38:07 tgl Exp $
* *
* *
* INTERFACE ROUTINES * INTERFACE ROUTINES
...@@ -1486,7 +1486,7 @@ AddRelationRawConstraints(Relation rel, ...@@ -1486,7 +1486,7 @@ AddRelationRawConstraints(Relation rel,
NULL, NULL,
false, false,
true); true);
addRTEtoQuery(pstate, rte, true, true); addRTEtoQuery(pstate, rte, true, true, true);
/* /*
* Process column default expressions. * Process column default expressions.
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.159 2005/05/30 07:20:58 neilc Exp $ * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.160 2005/06/05 00:38:08 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -4720,7 +4720,7 @@ ATPrepAlterColumnType(List **wqueue, ...@@ -4720,7 +4720,7 @@ ATPrepAlterColumnType(List **wqueue,
NULL, NULL,
false, false,
true); true);
addRTEtoQuery(pstate, rte, false, true); addRTEtoQuery(pstate, rte, false, true, true);
transform = transformExpr(pstate, cmd->transform); transform = transformExpr(pstate, cmd->transform);
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, 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.321 2005/04/28 21:47:14 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.322 2005/06/05 00:38:09 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -512,7 +512,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt, ...@@ -512,7 +512,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
Query *qry = makeNode(Query); Query *qry = makeNode(Query);
Query *selectQuery = NULL; Query *selectQuery = NULL;
List *sub_rtable; List *sub_rtable;
List *sub_namespace; List *sub_relnamespace;
List *sub_varnamespace;
List *icolumns; List *icolumns;
List *attrnos; List *attrnos;
ListCell *icols; ListCell *icols;
...@@ -528,20 +529,23 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt, ...@@ -528,20 +529,23 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
* SELECT. This can only happen if we are inside a CREATE RULE, and * SELECT. This can only happen if we are inside a CREATE RULE, and
* in that case we want the rule's OLD and NEW rtable entries to * in that case we want the rule's OLD and NEW rtable entries to
* appear as part of the SELECT's rtable, not as outer references for * appear as part of the SELECT's rtable, not as outer references for
* it. (Kluge!) The SELECT's joinlist is not affected however. We * it. (Kluge!) The SELECT's joinlist is not affected however. We
* must do this before adding the target table to the INSERT's rtable. * must do this before adding the target table to the INSERT's rtable.
*/ */
if (stmt->selectStmt) if (stmt->selectStmt)
{ {
sub_rtable = pstate->p_rtable; sub_rtable = pstate->p_rtable;
pstate->p_rtable = NIL; pstate->p_rtable = NIL;
sub_namespace = pstate->p_namespace; sub_relnamespace = pstate->p_relnamespace;
pstate->p_namespace = NIL; pstate->p_relnamespace = NIL;
sub_varnamespace = pstate->p_varnamespace;
pstate->p_varnamespace = NIL;
} }
else else
{ {
sub_rtable = NIL; /* not used, but keep compiler quiet */ sub_rtable = NIL; /* not used, but keep compiler quiet */
sub_namespace = NIL; sub_relnamespace = NIL;
sub_varnamespace = NIL;
} }
/* /*
...@@ -578,7 +582,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt, ...@@ -578,7 +582,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
* through 6.5 had bugs of just that nature...) * through 6.5 had bugs of just that nature...)
*/ */
sub_pstate->p_rtable = sub_rtable; sub_pstate->p_rtable = sub_rtable;
sub_pstate->p_namespace = sub_namespace; sub_pstate->p_relnamespace = sub_relnamespace;
sub_pstate->p_varnamespace = sub_varnamespace;
/* /*
* Note: we are not expecting that extras_before and extras_after * Note: we are not expecting that extras_before and extras_after
...@@ -605,7 +610,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt, ...@@ -605,7 +610,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
rte = addRangeTableEntryForSubquery(pstate, rte = addRangeTableEntryForSubquery(pstate,
selectQuery, selectQuery,
makeAlias("*SELECT*", NIL), makeAlias("*SELECT*", NIL),
true); false);
rtr = makeNode(RangeTblRef); rtr = makeNode(RangeTblRef);
/* assume new rte is at end */ /* assume new rte is at end */
rtr->rtindex = list_length(pstate->p_rtable); rtr->rtindex = list_length(pstate->p_rtable);
...@@ -1481,8 +1486,8 @@ transformIndexStmt(ParseState *pstate, IndexStmt *stmt) ...@@ -1481,8 +1486,8 @@ transformIndexStmt(ParseState *pstate, IndexStmt *stmt)
*/ */
rte = addRangeTableEntry(pstate, stmt->relation, NULL, false, true); rte = addRangeTableEntry(pstate, stmt->relation, NULL, false, true);
/* no to join list, yes to namespace */ /* no to join list, yes to namespaces */
addRTEtoQuery(pstate, rte, false, true); addRTEtoQuery(pstate, rte, false, true, true);
stmt->whereClause = transformWhereClause(pstate, stmt->whereClause, stmt->whereClause = transformWhereClause(pstate, stmt->whereClause,
"WHERE"); "WHERE");
...@@ -1500,8 +1505,8 @@ transformIndexStmt(ParseState *pstate, IndexStmt *stmt) ...@@ -1500,8 +1505,8 @@ transformIndexStmt(ParseState *pstate, IndexStmt *stmt)
{ {
rte = addRangeTableEntry(pstate, stmt->relation, NULL, rte = addRangeTableEntry(pstate, stmt->relation, NULL,
false, true); false, true);
/* no to join list, yes to namespace */ /* no to join list, yes to namespaces */
addRTEtoQuery(pstate, rte, false, true); addRTEtoQuery(pstate, rte, false, true, true);
} }
ielem->expr = transformExpr(pstate, ielem->expr); ielem->expr = transformExpr(pstate, ielem->expr);
...@@ -1559,10 +1564,10 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt, ...@@ -1559,10 +1564,10 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
Assert(pstate->p_rtable == NIL); Assert(pstate->p_rtable == NIL);
oldrte = addRangeTableEntryForRelation(pstate, rel, oldrte = addRangeTableEntryForRelation(pstate, rel,
makeAlias("*OLD*", NIL), makeAlias("*OLD*", NIL),
false, true); false, false);
newrte = addRangeTableEntryForRelation(pstate, rel, newrte = addRangeTableEntryForRelation(pstate, rel,
makeAlias("*NEW*", NIL), makeAlias("*NEW*", NIL),
false, true); false, false);
/* Must override addRangeTableEntry's default access-check flags */ /* Must override addRangeTableEntry's default access-check flags */
oldrte->requiredPerms = 0; oldrte->requiredPerms = 0;
newrte->requiredPerms = 0; newrte->requiredPerms = 0;
...@@ -1572,24 +1577,22 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt, ...@@ -1572,24 +1577,22 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
* the one(s) that are relevant for the current kind of rule. In an * the one(s) that are relevant for the current kind of rule. In an
* UPDATE rule, quals must refer to OLD.field or NEW.field to be * UPDATE rule, quals must refer to OLD.field or NEW.field to be
* unambiguous, but there's no need to be so picky for INSERT & * unambiguous, but there's no need to be so picky for INSERT &
* DELETE. (Note we marked the RTEs "inFromCl = true" above to allow * DELETE. We do not add them to the joinlist.
* unqualified references to their fields.) We do not add them to the
* joinlist.
*/ */
switch (stmt->event) switch (stmt->event)
{ {
case CMD_SELECT: case CMD_SELECT:
addRTEtoQuery(pstate, oldrte, false, true); addRTEtoQuery(pstate, oldrte, false, true, true);
break; break;
case CMD_UPDATE: case CMD_UPDATE:
addRTEtoQuery(pstate, oldrte, false, true); addRTEtoQuery(pstate, oldrte, false, true, true);
addRTEtoQuery(pstate, newrte, false, true); addRTEtoQuery(pstate, newrte, false, true, true);
break; break;
case CMD_INSERT: case CMD_INSERT:
addRTEtoQuery(pstate, newrte, false, true); addRTEtoQuery(pstate, newrte, false, true, true);
break; break;
case CMD_DELETE: case CMD_DELETE:
addRTEtoQuery(pstate, oldrte, false, true); addRTEtoQuery(pstate, oldrte, false, true, true);
break; break;
default: default:
elog(ERROR, "unrecognized event type: %d", elog(ERROR, "unrecognized event type: %d",
...@@ -1651,10 +1654,9 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt, ...@@ -1651,10 +1654,9 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
/* /*
* Set up OLD/NEW in the rtable for this statement. The * Set up OLD/NEW in the rtable for this statement. The
* entries are marked not inFromCl because we don't want them * entries are added only to relnamespace, not varnamespace,
* to be referred to by unqualified field names nor "*" in the * because we don't want them to be referred to by unqualified
* rule actions. We must add them to the namespace, however, * field names nor "*" in the rule actions. We decide later
* or they won't be accessible at all. We decide later
* whether to put them in the joinlist. * whether to put them in the joinlist.
*/ */
oldrte = addRangeTableEntryForRelation(sub_pstate, rel, oldrte = addRangeTableEntryForRelation(sub_pstate, rel,
...@@ -1665,8 +1667,8 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt, ...@@ -1665,8 +1667,8 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
false, false); false, false);
oldrte->requiredPerms = 0; oldrte->requiredPerms = 0;
newrte->requiredPerms = 0; newrte->requiredPerms = 0;
addRTEtoQuery(sub_pstate, oldrte, false, true); addRTEtoQuery(sub_pstate, oldrte, false, true, false);
addRTEtoQuery(sub_pstate, newrte, false, true); addRTEtoQuery(sub_pstate, newrte, false, true, false);
/* Transform the rule action statement */ /* Transform the rule action statement */
top_subqry = transformStmt(sub_pstate, action, top_subqry = transformStmt(sub_pstate, action,
...@@ -1776,7 +1778,7 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt, ...@@ -1776,7 +1778,7 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
/* hack so we can use addRTEtoQuery() */ /* hack so we can use addRTEtoQuery() */
sub_pstate->p_rtable = sub_qry->rtable; sub_pstate->p_rtable = sub_qry->rtable;
sub_pstate->p_joinlist = sub_qry->jointree->fromlist; sub_pstate->p_joinlist = sub_qry->jointree->fromlist;
addRTEtoQuery(sub_pstate, oldrte, true, false); addRTEtoQuery(sub_pstate, oldrte, true, false, false);
sub_qry->jointree->fromlist = sub_pstate->p_joinlist; sub_qry->jointree->fromlist = sub_pstate->p_joinlist;
} }
...@@ -1906,10 +1908,10 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) ...@@ -1906,10 +1908,10 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
*dtlist; *dtlist;
List *targetvars, List *targetvars,
*targetnames, *targetnames,
*sv_namespace, *sv_relnamespace,
*sv_varnamespace,
*sv_rtable; *sv_rtable;
RangeTblEntry *jrte; RangeTblEntry *jrte;
RangeTblRef *jrtr;
int tllen; int tllen;
qry->commandType = CMD_SELECT; qry->commandType = CMD_SELECT;
...@@ -2027,7 +2029,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) ...@@ -2027,7 +2029,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
/* /*
* As a first step towards supporting sort clauses that are * As a first step towards supporting sort clauses that are
* expressions using the output columns, generate a namespace entry * expressions using the output columns, generate a varnamespace entry
* that makes the output columns visible. A Join RTE node is handy * that makes the output columns visible. A Join RTE node is handy
* for this, since we can easily control the Vars generated upon * for this, since we can easily control the Vars generated upon
* matches. * matches.
...@@ -2041,15 +2043,16 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) ...@@ -2041,15 +2043,16 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
JOIN_INNER, JOIN_INNER,
targetvars, targetvars,
NULL, NULL,
true); false);
jrtr = makeNode(RangeTblRef);
jrtr->rtindex = 1; /* only entry in dummy rtable */
sv_rtable = pstate->p_rtable; sv_rtable = pstate->p_rtable;
pstate->p_rtable = list_make1(jrte); pstate->p_rtable = list_make1(jrte);
sv_namespace = pstate->p_namespace; sv_relnamespace = pstate->p_relnamespace;
pstate->p_namespace = list_make1(jrtr); pstate->p_relnamespace = NIL; /* no qualified names allowed */
sv_varnamespace = pstate->p_varnamespace;
pstate->p_varnamespace = list_make1(jrte);
/* /*
* For now, we don't support resjunk sort clauses on the output of a * For now, we don't support resjunk sort clauses on the output of a
...@@ -2064,8 +2067,9 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) ...@@ -2064,8 +2067,9 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
&qry->targetList, &qry->targetList,
false /* no unknowns expected */ ); false /* no unknowns expected */ );
pstate->p_namespace = sv_namespace;
pstate->p_rtable = sv_rtable; pstate->p_rtable = sv_rtable;
pstate->p_relnamespace = sv_relnamespace;
pstate->p_varnamespace = sv_varnamespace;
if (tllen != list_length(qry->targetList)) if (tllen != list_length(qry->targetList))
ereport(ERROR, ereport(ERROR,
...@@ -2164,7 +2168,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt) ...@@ -2164,7 +2168,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt)
* happen because the namespace will be empty, but it could happen * happen because the namespace will be empty, but it could happen
* if we are inside a rule. * if we are inside a rule.
*/ */
if (pstate->p_namespace) if (pstate->p_relnamespace || pstate->p_varnamespace)
{ {
if (contain_vars_of_level((Node *) selectQuery, 1)) if (contain_vars_of_level((Node *) selectQuery, 1))
ereport(ERROR, ereport(ERROR,
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.141 2005/06/04 19:19:42 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.142 2005/06/05 00:38:09 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -47,14 +47,19 @@ static void extractRemainingColumns(List *common_colnames, ...@@ -47,14 +47,19 @@ static void extractRemainingColumns(List *common_colnames,
static Node *transformJoinUsingClause(ParseState *pstate, static Node *transformJoinUsingClause(ParseState *pstate,
List *leftVars, List *rightVars); List *leftVars, List *rightVars);
static Node *transformJoinOnClause(ParseState *pstate, JoinExpr *j, static Node *transformJoinOnClause(ParseState *pstate, JoinExpr *j,
List *containedRels); RangeTblEntry *l_rte,
static RangeTblRef *transformTableEntry(ParseState *pstate, RangeVar *r); RangeTblEntry *r_rte,
static RangeTblRef *transformRangeSubselect(ParseState *pstate, List *relnamespace,
Relids containedRels);
static RangeTblEntry *transformTableEntry(ParseState *pstate, RangeVar *r);
static RangeTblEntry *transformRangeSubselect(ParseState *pstate,
RangeSubselect *r); RangeSubselect *r);
static RangeTblRef *transformRangeFunction(ParseState *pstate, static RangeTblEntry *transformRangeFunction(ParseState *pstate,
RangeFunction *r); RangeFunction *r);
static Node *transformFromClauseItem(ParseState *pstate, Node *n, static Node *transformFromClauseItem(ParseState *pstate, Node *n,
List **containedRels); RangeTblEntry **top_rte, int *top_rti,
List **relnamespace,
Relids *containedRels);
static Node *buildMergedJoinVar(ParseState *pstate, JoinType jointype, static Node *buildMergedJoinVar(ParseState *pstate, JoinType jointype,
Var *l_colvar, Var *r_colvar); Var *l_colvar, Var *r_colvar);
static TargetEntry *findTargetlistEntry(ParseState *pstate, Node *node, static TargetEntry *findTargetlistEntry(ParseState *pstate, Node *node,
...@@ -64,12 +69,12 @@ static TargetEntry *findTargetlistEntry(ParseState *pstate, Node *node, ...@@ -64,12 +69,12 @@ static TargetEntry *findTargetlistEntry(ParseState *pstate, Node *node,
/* /*
* transformFromClause - * transformFromClause -
* Process the FROM clause and add items to the query's range table, * Process the FROM clause and add items to the query's range table,
* joinlist, and namespace. * joinlist, and namespaces.
* *
* Note: we assume that pstate's p_rtable, p_joinlist, and p_namespace lists * Note: we assume that pstate's p_rtable, p_joinlist, p_relnamespace, and
* were initialized to NIL when the pstate was created. We will add onto * p_varnamespace lists were initialized to NIL when the pstate was created.
* any entries already present --- this is needed for rule processing, as * We will add onto any entries already present --- this is needed for rule
* well as for UPDATE and DELETE. * processing, as well as for UPDATE and DELETE.
* *
* The range table may grow still further when we transform the expressions * The range table may grow still further when we transform the expressions
* in the query's quals and target list. (This is possible because in * in the query's quals and target list. (This is possible because in
...@@ -85,17 +90,27 @@ transformFromClause(ParseState *pstate, List *frmList) ...@@ -85,17 +90,27 @@ transformFromClause(ParseState *pstate, List *frmList)
* The grammar will have produced a list of RangeVars, * The grammar will have produced a list of RangeVars,
* RangeSubselects, RangeFunctions, and/or JoinExprs. Transform each * RangeSubselects, RangeFunctions, and/or JoinExprs. Transform each
* one (possibly adding entries to the rtable), check for duplicate * one (possibly adding entries to the rtable), check for duplicate
* refnames, and then add it to the joinlist and namespace. * refnames, and then add it to the joinlist and namespaces.
*/ */
foreach(fl, frmList) foreach(fl, frmList)
{ {
Node *n = lfirst(fl); Node *n = lfirst(fl);
List *containedRels; RangeTblEntry *rte;
int rtindex;
n = transformFromClauseItem(pstate, n, &containedRels); List *relnamespace;
checkNameSpaceConflicts(pstate, (Node *) pstate->p_namespace, n); Relids containedRels;
n = transformFromClauseItem(pstate, n,
&rte,
&rtindex,
&relnamespace,
&containedRels);
checkNameSpaceConflicts(pstate, pstate->p_relnamespace, relnamespace);
pstate->p_joinlist = lappend(pstate->p_joinlist, n); pstate->p_joinlist = lappend(pstate->p_joinlist, n);
pstate->p_namespace = lappend(pstate->p_namespace, n); pstate->p_relnamespace = list_concat(pstate->p_relnamespace,
relnamespace);
pstate->p_varnamespace = lappend(pstate->p_varnamespace, rte);
bms_free(containedRels);
} }
} }
...@@ -165,10 +180,10 @@ setTargetTable(ParseState *pstate, RangeVar *relation, ...@@ -165,10 +180,10 @@ setTargetTable(ParseState *pstate, RangeVar *relation,
rte->requiredPerms = requiredPerms; rte->requiredPerms = requiredPerms;
/* /*
* If UPDATE/DELETE, add table to joinlist and namespace. * If UPDATE/DELETE, add table to joinlist and namespaces.
*/ */
if (alsoSource) if (alsoSource)
addRTEtoQuery(pstate, rte, true, true); addRTEtoQuery(pstate, rte, true, true, true);
return rtindex; return rtindex;
} }
...@@ -322,10 +337,14 @@ transformJoinUsingClause(ParseState *pstate, List *leftVars, List *rightVars) ...@@ -322,10 +337,14 @@ transformJoinUsingClause(ParseState *pstate, List *leftVars, List *rightVars)
*/ */
static Node * static Node *
transformJoinOnClause(ParseState *pstate, JoinExpr *j, transformJoinOnClause(ParseState *pstate, JoinExpr *j,
List *containedRels) RangeTblEntry *l_rte,
RangeTblEntry *r_rte,
List *relnamespace,
Relids containedRels)
{ {
Node *result; Node *result;
List *save_namespace; List *save_relnamespace;
List *save_varnamespace;
Relids clause_varnos; Relids clause_varnos;
int varno; int varno;
...@@ -339,12 +358,16 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j, ...@@ -339,12 +358,16 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j,
* can't legally alter the namespace by causing implicit relation refs * can't legally alter the namespace by causing implicit relation refs
* to be added. * to be added.
*/ */
save_namespace = pstate->p_namespace; save_relnamespace = pstate->p_relnamespace;
pstate->p_namespace = list_make2(j->larg, j->rarg); save_varnamespace = pstate->p_varnamespace;
pstate->p_relnamespace = relnamespace;
pstate->p_varnamespace = list_make2(l_rte, r_rte);
result = transformWhereClause(pstate, j->quals, "JOIN/ON"); result = transformWhereClause(pstate, j->quals, "JOIN/ON");
pstate->p_namespace = save_namespace; pstate->p_relnamespace = save_relnamespace;
pstate->p_varnamespace = save_varnamespace;
/* /*
* Second, we need to check that the ON condition doesn't refer to any * Second, we need to check that the ON condition doesn't refer to any
...@@ -355,15 +378,13 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j, ...@@ -355,15 +378,13 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j,
* here.) * here.)
*/ */
clause_varnos = pull_varnos(result); clause_varnos = pull_varnos(result);
while ((varno = bms_first_member(clause_varnos)) >= 0) clause_varnos = bms_del_members(clause_varnos, containedRels);
if ((varno = bms_first_member(clause_varnos)) >= 0)
{ {
if (!list_member_int(containedRels, varno)) ereport(ERROR,
{ (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
ereport(ERROR, errmsg("JOIN/ON clause refers to \"%s\", which is not part of JOIN",
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE), rt_fetch(varno, pstate->p_rtable)->eref->aliasname)));
errmsg("JOIN/ON clause refers to \"%s\", which is not part of JOIN",
rt_fetch(varno, pstate->p_rtable)->eref->aliasname)));
}
} }
bms_free(clause_varnos); bms_free(clause_varnos);
...@@ -373,11 +394,10 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j, ...@@ -373,11 +394,10 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j,
/* /*
* transformTableEntry --- transform a RangeVar (simple relation reference) * transformTableEntry --- transform a RangeVar (simple relation reference)
*/ */
static RangeTblRef * static RangeTblEntry *
transformTableEntry(ParseState *pstate, RangeVar *r) transformTableEntry(ParseState *pstate, RangeVar *r)
{ {
RangeTblEntry *rte; RangeTblEntry *rte;
RangeTblRef *rtr;
/* /*
* mark this entry to indicate it comes from the FROM clause. In SQL, * mark this entry to indicate it comes from the FROM clause. In SQL,
...@@ -389,29 +409,19 @@ transformTableEntry(ParseState *pstate, RangeVar *r) ...@@ -389,29 +409,19 @@ transformTableEntry(ParseState *pstate, RangeVar *r)
rte = addRangeTableEntry(pstate, r, r->alias, rte = addRangeTableEntry(pstate, r, r->alias,
interpretInhOption(r->inhOpt), true); interpretInhOption(r->inhOpt), true);
/* return rte;
* We create a RangeTblRef, but we do not add it to the joinlist or
* namespace; our caller must do that if appropriate.
*/
rtr = makeNode(RangeTblRef);
/* assume new rte is at end */
rtr->rtindex = list_length(pstate->p_rtable);
Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable));
return rtr;
} }
/* /*
* transformRangeSubselect --- transform a sub-SELECT appearing in FROM * transformRangeSubselect --- transform a sub-SELECT appearing in FROM
*/ */
static RangeTblRef * static RangeTblEntry *
transformRangeSubselect(ParseState *pstate, RangeSubselect *r) transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
{ {
List *parsetrees; List *parsetrees;
Query *query; Query *query;
RangeTblEntry *rte; RangeTblEntry *rte;
RangeTblRef *rtr;
/* /*
* We require user to supply an alias for a subselect, per SQL92. To * We require user to supply an alias for a subselect, per SQL92. To
...@@ -461,7 +471,7 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r) ...@@ -461,7 +471,7 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
* visible in the current query. Also note that outer references are * visible in the current query. Also note that outer references are
* OK. * OK.
*/ */
if (pstate->p_namespace) if (pstate->p_relnamespace || pstate->p_varnamespace)
{ {
if (contain_vars_of_level((Node *) query, 1)) if (contain_vars_of_level((Node *) query, 1))
ereport(ERROR, ereport(ERROR,
...@@ -474,29 +484,19 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r) ...@@ -474,29 +484,19 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
*/ */
rte = addRangeTableEntryForSubquery(pstate, query, r->alias, true); rte = addRangeTableEntryForSubquery(pstate, query, r->alias, true);
/* return rte;
* We create a RangeTblRef, but we do not add it to the joinlist or
* namespace; our caller must do that if appropriate.
*/
rtr = makeNode(RangeTblRef);
/* assume new rte is at end */
rtr->rtindex = list_length(pstate->p_rtable);
Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable));
return rtr;
} }
/* /*
* transformRangeFunction --- transform a function call appearing in FROM * transformRangeFunction --- transform a function call appearing in FROM
*/ */
static RangeTblRef * static RangeTblEntry *
transformRangeFunction(ParseState *pstate, RangeFunction *r) transformRangeFunction(ParseState *pstate, RangeFunction *r)
{ {
Node *funcexpr; Node *funcexpr;
char *funcname; char *funcname;
RangeTblEntry *rte; RangeTblEntry *rte;
RangeTblRef *rtr;
/* /*
* Get function name for possible use as alias. We use the same * Get function name for possible use as alias. We use the same
...@@ -520,7 +520,7 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r) ...@@ -520,7 +520,7 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
* XXX this will need further work to support SQL99's LATERAL() feature, * XXX this will need further work to support SQL99's LATERAL() feature,
* wherein such references would indeed be legal. * wherein such references would indeed be legal.
*/ */
if (pstate->p_namespace) if (pstate->p_relnamespace || pstate->p_varnamespace)
{ {
if (contain_vars_of_level(funcexpr, 0)) if (contain_vars_of_level(funcexpr, 0))
ereport(ERROR, ereport(ERROR,
...@@ -558,16 +558,7 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r) ...@@ -558,16 +558,7 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
rte = addRangeTableEntryForFunction(pstate, funcname, funcexpr, rte = addRangeTableEntryForFunction(pstate, funcname, funcexpr,
r, true); r, true);
/* return rte;
* We create a RangeTblRef, but we do not add it to the joinlist or
* namespace; our caller must do that if appropriate.
*/
rtr = makeNode(RangeTblRef);
/* assume new rte is at end */
rtr->rtindex = list_length(pstate->p_rtable);
Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable));
return rtr;
} }
...@@ -575,109 +566,151 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r) ...@@ -575,109 +566,151 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
* transformFromClauseItem - * transformFromClauseItem -
* Transform a FROM-clause item, adding any required entries to the * Transform a FROM-clause item, adding any required entries to the
* range table list being built in the ParseState, and return the * range table list being built in the ParseState, and return the
* transformed item ready to include in the joinlist and namespace. * transformed item ready to include in the joinlist and namespaces.
* This routine can recurse to handle SQL92 JOIN expressions. * This routine can recurse to handle SQL92 JOIN expressions.
* *
* Aside from the primary return value (the transformed joinlist item) * The function return value is the node to add to the jointree (a
* this routine also returns an integer list of the rangetable indexes * RangeTblRef or JoinExpr). Additional output parameters are:
* of all the base and join relations represented in the joinlist item. *
* This list is needed for checking JOIN/ON conditions in higher levels. * *top_rte: receives the RTE corresponding to the jointree item.
* (We could extract this from the function return node, but it saves cycles
* to pass it back separately.)
*
* *top_rti: receives the rangetable index of top_rte. (Ditto.)
*
* *relnamespace: receives a List of the RTEs exposed as relation names
* by this item.
*
* *containedRels: receives a bitmap set of the rangetable indexes
* of all the base and join relations represented in this jointree item.
* This is needed for checking JOIN/ON conditions in higher levels.
*
* We do not need to pass back an explicit varnamespace value, because
* in all cases the varnamespace contribution is exactly top_rte.
*/ */
static Node * static Node *
transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels) transformFromClauseItem(ParseState *pstate, Node *n,
RangeTblEntry **top_rte, int *top_rti,
List **relnamespace,
Relids *containedRels)
{ {
if (IsA(n, RangeVar)) if (IsA(n, RangeVar))
{ {
/* Plain relation reference */ /* Plain relation reference */
RangeTblRef *rtr; RangeTblRef *rtr;
RangeTblEntry *rte;
int rtindex;
rtr = transformTableEntry(pstate, (RangeVar *) n); rte = transformTableEntry(pstate, (RangeVar *) n);
*containedRels = list_make1_int(rtr->rtindex); /* assume new rte is at end */
rtindex = list_length(pstate->p_rtable);
Assert(rte == rt_fetch(rtindex, pstate->p_rtable));
*top_rte = rte;
*top_rti = rtindex;
*relnamespace = list_make1(rte);
*containedRels = bms_make_singleton(rtindex);
rtr = makeNode(RangeTblRef);
rtr->rtindex = rtindex;
return (Node *) rtr; return (Node *) rtr;
} }
else if (IsA(n, RangeSubselect)) else if (IsA(n, RangeSubselect))
{ {
/* sub-SELECT is like a plain relation */ /* sub-SELECT is like a plain relation */
RangeTblRef *rtr; RangeTblRef *rtr;
RangeTblEntry *rte;
int rtindex;
rtr = transformRangeSubselect(pstate, (RangeSubselect *) n); rte = transformRangeSubselect(pstate, (RangeSubselect *) n);
*containedRels = list_make1_int(rtr->rtindex); /* assume new rte is at end */
rtindex = list_length(pstate->p_rtable);
Assert(rte == rt_fetch(rtindex, pstate->p_rtable));
*top_rte = rte;
*top_rti = rtindex;
*relnamespace = list_make1(rte);
*containedRels = bms_make_singleton(rtindex);
rtr = makeNode(RangeTblRef);
rtr->rtindex = rtindex;
return (Node *) rtr; return (Node *) rtr;
} }
else if (IsA(n, RangeFunction)) else if (IsA(n, RangeFunction))
{ {
/* function is like a plain relation */ /* function is like a plain relation */
RangeTblRef *rtr; RangeTblRef *rtr;
RangeTblEntry *rte;
int rtindex;
rtr = transformRangeFunction(pstate, (RangeFunction *) n); rte = transformRangeFunction(pstate, (RangeFunction *) n);
*containedRels = list_make1_int(rtr->rtindex); /* assume new rte is at end */
rtindex = list_length(pstate->p_rtable);
Assert(rte == rt_fetch(rtindex, pstate->p_rtable));
*top_rte = rte;
*top_rti = rtindex;
*relnamespace = list_make1(rte);
*containedRels = bms_make_singleton(rtindex);
rtr = makeNode(RangeTblRef);
rtr->rtindex = rtindex;
return (Node *) rtr; return (Node *) rtr;
} }
else if (IsA(n, JoinExpr)) else if (IsA(n, JoinExpr))
{ {
/* A newfangled join expression */ /* A newfangled join expression */
JoinExpr *j = (JoinExpr *) n; JoinExpr *j = (JoinExpr *) n;
List *my_containedRels, RangeTblEntry *l_rte;
*l_containedRels, RangeTblEntry *r_rte;
*r_containedRels, int l_rtindex;
int r_rtindex;
Relids l_containedRels,
r_containedRels,
my_containedRels;
List *l_relnamespace,
*r_relnamespace,
*my_relnamespace,
*l_colnames, *l_colnames,
*r_colnames, *r_colnames,
*res_colnames, *res_colnames,
*l_colvars, *l_colvars,
*r_colvars, *r_colvars,
*res_colvars; *res_colvars;
Index leftrti,
rightrti;
RangeTblEntry *rte; RangeTblEntry *rte;
/* /*
* Recursively process the left and right subtrees * Recursively process the left and right subtrees
*/ */
j->larg = transformFromClauseItem(pstate, j->larg, &l_containedRels); j->larg = transformFromClauseItem(pstate, j->larg,
j->rarg = transformFromClauseItem(pstate, j->rarg, &r_containedRels); &l_rte,
&l_rtindex,
/* &l_relnamespace,
* Generate combined list of relation indexes for possible use by &l_containedRels);
* transformJoinOnClause below. j->rarg = transformFromClauseItem(pstate, j->rarg,
*/ &r_rte,
my_containedRels = list_concat(l_containedRels, r_containedRels); &r_rtindex,
&r_relnamespace,
&r_containedRels);
/* /*
* Check for conflicting refnames in left and right subtrees. Must * Check for conflicting refnames in left and right subtrees. Must
* do this because higher levels will assume I hand back a self- * do this because higher levels will assume I hand back a self-
* consistent namespace subtree. * consistent namespace subtree.
*/ */
checkNameSpaceConflicts(pstate, j->larg, j->rarg); checkNameSpaceConflicts(pstate, l_relnamespace, r_relnamespace);
/*
* Generate combined relation membership info for possible use by
* transformJoinOnClause below.
*/
my_relnamespace = list_concat(l_relnamespace, r_relnamespace);
my_containedRels = bms_join(l_containedRels, r_containedRels);
pfree(r_relnamespace); /* free unneeded list header */
/* /*
* Extract column name and var lists from both subtrees * Extract column name and var lists from both subtrees
* *
* Note: expandRTE returns new lists, safe for me to modify * Note: expandRTE returns new lists, safe for me to modify
*/ */
if (IsA(j->larg, RangeTblRef)) expandRTE(l_rte, l_rtindex, 0, false,
leftrti = ((RangeTblRef *) j->larg)->rtindex;
else if (IsA(j->larg, JoinExpr))
leftrti = ((JoinExpr *) j->larg)->rtindex;
else
{
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(j->larg));
leftrti = 0; /* keep compiler quiet */
}
rte = rt_fetch(leftrti, pstate->p_rtable);
expandRTE(rte, leftrti, 0, false,
&l_colnames, &l_colvars); &l_colnames, &l_colvars);
expandRTE(r_rte, r_rtindex, 0, false,
if (IsA(j->rarg, RangeTblRef))
rightrti = ((RangeTblRef *) j->rarg)->rtindex;
else if (IsA(j->rarg, JoinExpr))
rightrti = ((JoinExpr *) j->rarg)->rtindex;
else
{
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(j->rarg));
rightrti = 0; /* keep compiler quiet */
}
rte = rt_fetch(rightrti, pstate->p_rtable);
expandRTE(rte, rightrti, 0, false,
&r_colnames, &r_colvars); &r_colnames, &r_colvars);
/* /*
...@@ -829,7 +862,10 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels) ...@@ -829,7 +862,10 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
else if (j->quals) else if (j->quals)
{ {
/* User-written ON-condition; transform it */ /* User-written ON-condition; transform it */
j->quals = transformJoinOnClause(pstate, j, my_containedRels); j->quals = transformJoinOnClause(pstate, j,
l_rte, r_rte,
my_relnamespace,
my_containedRels);
} }
else else
{ {
...@@ -877,10 +913,27 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels) ...@@ -877,10 +913,27 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
j->rtindex = list_length(pstate->p_rtable); j->rtindex = list_length(pstate->p_rtable);
Assert(rte == rt_fetch(j->rtindex, pstate->p_rtable)); Assert(rte == rt_fetch(j->rtindex, pstate->p_rtable));
*top_rte = rte;
*top_rti = j->rtindex;
/*
* Prepare returned namespace list. If the JOIN has an alias
* then it hides the contained RTEs as far as the relnamespace
* goes; otherwise, put the contained RTEs and *not* the JOIN
* into relnamespace.
*/
if (j->alias)
{
*relnamespace = list_make1(rte);
list_free(my_relnamespace);
}
else
*relnamespace = my_relnamespace;
/* /*
* Include join RTE in returned containedRels list * Include join RTE in returned containedRels set
*/ */
*containedRels = lcons_int(j->rtindex, my_containedRels); *containedRels = bms_add_member(my_containedRels, j->rtindex);
return (Node *) j; return (Node *) j;
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.110 2005/06/04 19:19:42 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.111 2005/06/05 00:38:09 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -34,12 +34,9 @@ ...@@ -34,12 +34,9 @@
/* GUC parameter */ /* GUC parameter */
bool add_missing_from; bool add_missing_from;
static Node *scanNameSpaceForRefname(ParseState *pstate, Node *nsnode, static RangeTblEntry *scanNameSpaceForRefname(ParseState *pstate,
const char *refname); const char *refname);
static Node *scanNameSpaceForRelid(ParseState *pstate, Node *nsnode, static RangeTblEntry *scanNameSpaceForRelid(ParseState *pstate, Oid relid);
Oid relid);
static void scanNameSpaceForConflict(ParseState *pstate, Node *nsnode,
RangeTblEntry *rte1, const char *aliasname1);
static bool isLockedRel(ParseState *pstate, char *refname); static bool isLockedRel(ParseState *pstate, char *refname);
static void expandRelation(Oid relid, Alias *eref, static void expandRelation(Oid relid, Alias *eref,
int rtindex, int sublevels_up, int rtindex, int sublevels_up,
...@@ -97,188 +94,92 @@ refnameRangeTblEntry(ParseState *pstate, ...@@ -97,188 +94,92 @@ refnameRangeTblEntry(ParseState *pstate,
while (pstate != NULL) while (pstate != NULL)
{ {
Node *nsnode; RangeTblEntry *result;
if (OidIsValid(relId)) if (OidIsValid(relId))
nsnode = scanNameSpaceForRelid(pstate, result = scanNameSpaceForRelid(pstate, relId);
(Node *) pstate->p_namespace,
relId);
else else
nsnode = scanNameSpaceForRefname(pstate, result = scanNameSpaceForRefname(pstate, refname);
(Node *) pstate->p_namespace,
refname);
if (nsnode) if (result)
{ return result;
/* should get an RTE or JoinExpr */
if (IsA(nsnode, RangeTblEntry))
return (RangeTblEntry *) nsnode;
Assert(IsA(nsnode, JoinExpr));
return rt_fetch(((JoinExpr *) nsnode)->rtindex, pstate->p_rtable);
}
pstate = pstate->parentParseState;
if (sublevels_up) if (sublevels_up)
(*sublevels_up)++; (*sublevels_up)++;
else else
break; break;
pstate = pstate->parentParseState;
} }
return NULL; return NULL;
} }
/* /*
* Recursively search a namespace for an RTE or joinexpr matching the * Search the query's table namespace for an RTE matching the
* given unqualified refname. Return the node if a unique match, or NULL * given unqualified refname. Return the RTE if a unique match, or NULL
* if no match. Raise error if multiple matches. * if no match. Raise error if multiple matches.
*
* The top level of p_namespace is a list, and we recurse into any joins
* that are not subqueries.
*/ */
static Node * static RangeTblEntry *
scanNameSpaceForRefname(ParseState *pstate, Node *nsnode, scanNameSpaceForRefname(ParseState *pstate, const char *refname)
const char *refname)
{ {
Node *result = NULL; RangeTblEntry *result = NULL;
Node *newresult; ListCell *l;
if (nsnode == NULL) foreach(l, pstate->p_relnamespace)
return NULL;
if (IsA(nsnode, RangeTblRef))
{ {
int varno = ((RangeTblRef *) nsnode)->rtindex; RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable);
if (strcmp(rte->eref->aliasname, refname) == 0) if (strcmp(rte->eref->aliasname, refname) == 0)
result = (Node *) rte;
}
else if (IsA(nsnode, JoinExpr))
{
JoinExpr *j = (JoinExpr *) nsnode;
if (j->alias)
{ {
if (strcmp(j->alias->aliasname, refname) == 0) if (result)
return (Node *) j; /* matched a join alias */
/*
* Tables within an aliased join are invisible from outside
* the join, according to the scope rules of SQL92 (the join
* is considered a subquery). So, stop here.
*/
return NULL;
}
result = scanNameSpaceForRefname(pstate, j->larg, refname);
newresult = scanNameSpaceForRefname(pstate, j->rarg, refname);
if (!result)
result = newresult;
else if (newresult)
ereport(ERROR,
(errcode(ERRCODE_AMBIGUOUS_ALIAS),
errmsg("table reference \"%s\" is ambiguous",
refname)));
}
else if (IsA(nsnode, List))
{
ListCell *l;
foreach(l, (List *) nsnode)
{
newresult = scanNameSpaceForRefname(pstate, lfirst(l), refname);
if (!result)
result = newresult;
else if (newresult)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_AMBIGUOUS_ALIAS), (errcode(ERRCODE_AMBIGUOUS_ALIAS),
errmsg("table reference \"%s\" is ambiguous", errmsg("table reference \"%s\" is ambiguous",
refname))); refname)));
result = rte;
} }
} }
else
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(nsnode));
return result; return result;
} }
/* /*
* Recursively search a namespace for a relation RTE matching the * Search the query's table namespace for a relation RTE matching the
* given relation OID. Return the node if a unique match, or NULL * given relation OID. Return the RTE if a unique match, or NULL
* if no match. Raise error if multiple matches (which shouldn't * if no match. Raise error if multiple matches (which shouldn't
* happen if the namespace was checked correctly when it was created). * happen if the namespace was checked correctly when it was created).
* *
* The top level of p_namespace is a list, and we recurse into any joins
* that are not subqueries.
*
* See the comments for refnameRangeTblEntry to understand why this * See the comments for refnameRangeTblEntry to understand why this
* acts the way it does. * acts the way it does.
*/ */
static Node * static RangeTblEntry *
scanNameSpaceForRelid(ParseState *pstate, Node *nsnode, Oid relid) scanNameSpaceForRelid(ParseState *pstate, Oid relid)
{ {
Node *result = NULL; RangeTblEntry *result = NULL;
Node *newresult; ListCell *l;
if (nsnode == NULL) foreach(l, pstate->p_relnamespace)
return NULL;
if (IsA(nsnode, RangeTblRef))
{ {
int varno = ((RangeTblRef *) nsnode)->rtindex; RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable);
/* yes, the test for alias==NULL should be there... */ /* yes, the test for alias==NULL should be there... */
if (rte->rtekind == RTE_RELATION && if (rte->rtekind == RTE_RELATION &&
rte->relid == relid && rte->relid == relid &&
rte->alias == NULL) rte->alias == NULL)
result = (Node *) rte;
}
else if (IsA(nsnode, JoinExpr))
{
JoinExpr *j = (JoinExpr *) nsnode;
if (j->alias)
{ {
/* if (result)
* Tables within an aliased join are invisible from outside
* the join, according to the scope rules of SQL92 (the join
* is considered a subquery). So, stop here.
*/
return NULL;
}
result = scanNameSpaceForRelid(pstate, j->larg, relid);
newresult = scanNameSpaceForRelid(pstate, j->rarg, relid);
if (!result)
result = newresult;
else if (newresult)
ereport(ERROR,
(errcode(ERRCODE_AMBIGUOUS_ALIAS),
errmsg("table reference %u is ambiguous",
relid)));
}
else if (IsA(nsnode, List))
{
ListCell *l;
foreach(l, (List *) nsnode)
{
newresult = scanNameSpaceForRelid(pstate, lfirst(l), relid);
if (!result)
result = newresult;
else if (newresult)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_AMBIGUOUS_ALIAS), (errcode(ERRCODE_AMBIGUOUS_ALIAS),
errmsg("table reference %u is ambiguous", errmsg("table reference %u is ambiguous",
relid))); relid)));
result = rte;
} }
} }
else
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(nsnode));
return result; return result;
} }
/* /*
* Recursively check for name conflicts between two namespaces or * Check for relation-name conflicts between two relnamespace lists.
* namespace subtrees. Raise an error if any is found. * Raise an error if any is found.
*
* Works by recursively scanning namespace1 for RTEs and join nodes,
* and for each one recursively scanning namespace2 for a match.
* *
* Note: we assume that each given argument does not contain conflicts * Note: we assume that each given argument does not contain conflicts
* itself; we just want to know if the two can be merged together. * itself; we just want to know if the two can be merged together.
...@@ -288,108 +189,33 @@ scanNameSpaceForRelid(ParseState *pstate, Node *nsnode, Oid relid) ...@@ -288,108 +189,33 @@ scanNameSpaceForRelid(ParseState *pstate, Node *nsnode, Oid relid)
* are for different relation OIDs (implying they are in different schemas). * are for different relation OIDs (implying they are in different schemas).
*/ */
void void
checkNameSpaceConflicts(ParseState *pstate, Node *namespace1, checkNameSpaceConflicts(ParseState *pstate, List *namespace1,
Node *namespace2) List *namespace2)
{ {
if (namespace1 == NULL) ListCell *l1;
return;
if (IsA(namespace1, RangeTblRef))
{
int varno = ((RangeTblRef *) namespace1)->rtindex;
RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable);
if (rte->rtekind == RTE_RELATION && rte->alias == NULL)
scanNameSpaceForConflict(pstate, namespace2,
rte, rte->eref->aliasname);
else
scanNameSpaceForConflict(pstate, namespace2,
NULL, rte->eref->aliasname);
}
else if (IsA(namespace1, JoinExpr))
{
JoinExpr *j = (JoinExpr *) namespace1;
if (j->alias)
{
scanNameSpaceForConflict(pstate, namespace2,
NULL, j->alias->aliasname);
/*
* Tables within an aliased join are invisible from outside
* the join, according to the scope rules of SQL92 (the join
* is considered a subquery). So, stop here.
*/
return;
}
checkNameSpaceConflicts(pstate, j->larg, namespace2);
checkNameSpaceConflicts(pstate, j->rarg, namespace2);
}
else if (IsA(namespace1, List))
{
ListCell *l;
foreach(l, (List *) namespace1)
checkNameSpaceConflicts(pstate, lfirst(l), namespace2);
}
else
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(namespace1));
}
/* foreach(l1, namespace1)
* Subroutine for checkNameSpaceConflicts: scan namespace2
*/
static void
scanNameSpaceForConflict(ParseState *pstate, Node *nsnode,
RangeTblEntry *rte1, const char *aliasname1)
{
if (nsnode == NULL)
return;
if (IsA(nsnode, RangeTblRef))
{ {
int varno = ((RangeTblRef *) nsnode)->rtindex; RangeTblEntry *rte1 = (RangeTblEntry *) lfirst(l1);
RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable); const char *aliasname1 = rte1->eref->aliasname;
ListCell *l2;
if (strcmp(rte->eref->aliasname, aliasname1) != 0)
return; /* definitely no conflict */
if (rte->rtekind == RTE_RELATION && rte->alias == NULL &&
rte1 != NULL && rte->relid != rte1->relid)
return; /* no conflict per SQL92 rule */
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_ALIAS),
errmsg("table name \"%s\" specified more than once",
aliasname1)));
}
else if (IsA(nsnode, JoinExpr))
{
JoinExpr *j = (JoinExpr *) nsnode;
if (j->alias) foreach(l2, namespace2)
{ {
if (strcmp(j->alias->aliasname, aliasname1) == 0) RangeTblEntry *rte2 = (RangeTblEntry *) lfirst(l2);
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_ALIAS), if (strcmp(rte2->eref->aliasname, aliasname1) != 0)
continue; /* definitely no conflict */
if (rte1->rtekind == RTE_RELATION && rte1->alias == NULL &&
rte2->rtekind == RTE_RELATION && rte2->alias == NULL &&
rte1->relid != rte2->relid)
continue; /* no conflict per SQL92 rule */
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_ALIAS),
errmsg("table name \"%s\" specified more than once", errmsg("table name \"%s\" specified more than once",
aliasname1))); aliasname1)));
/*
* Tables within an aliased join are invisible from outside
* the join, according to the scope rules of SQL92 (the join
* is considered a subquery). So, stop here.
*/
return;
} }
scanNameSpaceForConflict(pstate, j->larg, rte1, aliasname1);
scanNameSpaceForConflict(pstate, j->rarg, rte1, aliasname1);
} }
else if (IsA(nsnode, List))
{
ListCell *l;
foreach(l, (List *) nsnode)
scanNameSpaceForConflict(pstate, lfirst(l), rte1, aliasname1);
}
else
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(nsnode));
} }
/* /*
...@@ -541,48 +367,18 @@ colNameToVar(ParseState *pstate, char *colname, bool localonly) ...@@ -541,48 +367,18 @@ colNameToVar(ParseState *pstate, char *colname, bool localonly)
{ {
Node *result = NULL; Node *result = NULL;
ParseState *orig_pstate = pstate; ParseState *orig_pstate = pstate;
int levels_up = 0;
while (pstate != NULL) while (pstate != NULL)
{ {
ListCell *ns; ListCell *l;
/*
* We need to look only at top-level namespace items, and even for
* those, ignore RTEs that are marked as not inFromCl and not the
* query's target relation.
*/
foreach(ns, pstate->p_namespace)
{
Node *nsnode = (Node *) lfirst(ns);
Node *newresult = NULL;
if (IsA(nsnode, RangeTblRef))
{
int varno = ((RangeTblRef *) nsnode)->rtindex;
RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable);
if (!rte->inFromCl &&
rte != pstate->p_target_rangetblentry)
continue;
/* use orig_pstate here to get the right sublevels_up */
newresult = scanRTEForColumn(orig_pstate, rte, colname);
}
else if (IsA(nsnode, JoinExpr))
{
int varno = ((JoinExpr *) nsnode)->rtindex;
RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable);
/* joins are always inFromCl, so no need to check */ foreach(l, pstate->p_varnamespace)
Assert(rte->inFromCl); {
RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
Node *newresult;
/* use orig_pstate here to get the right sublevels_up */ /* use orig_pstate here to get the right sublevels_up */
newresult = scanRTEForColumn(orig_pstate, rte, colname); newresult = scanRTEForColumn(orig_pstate, rte, colname);
}
else
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(nsnode));
if (newresult) if (newresult)
{ {
...@@ -599,7 +395,6 @@ colNameToVar(ParseState *pstate, char *colname, bool localonly) ...@@ -599,7 +395,6 @@ colNameToVar(ParseState *pstate, char *colname, bool localonly)
break; /* found, or don't want to look at parent */ break; /* found, or don't want to look at parent */
pstate = pstate->parentParseState; pstate = pstate->parentParseState;
levels_up++;
} }
return result; return result;
...@@ -1136,22 +931,26 @@ isLockedRel(ParseState *pstate, char *refname) ...@@ -1136,22 +931,26 @@ isLockedRel(ParseState *pstate, char *refname)
/* /*
* Add the given RTE as a top-level entry in the pstate's join list * Add the given RTE as a top-level entry in the pstate's join list
* and/or name space list. (We assume caller has checked for any * and/or name space lists. (We assume caller has checked for any
* namespace conflict.) * namespace conflicts.)
*/ */
void void
addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte, addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte,
bool addToJoinList, bool addToNameSpace) bool addToJoinList,
bool addToRelNameSpace, bool addToVarNameSpace)
{ {
int rtindex = RTERangeTablePosn(pstate, rte, NULL);
RangeTblRef *rtr = makeNode(RangeTblRef);
rtr->rtindex = rtindex;
if (addToJoinList) if (addToJoinList)
{
int rtindex = RTERangeTablePosn(pstate, rte, NULL);
RangeTblRef *rtr = makeNode(RangeTblRef);
rtr->rtindex = rtindex;
pstate->p_joinlist = lappend(pstate->p_joinlist, rtr); pstate->p_joinlist = lappend(pstate->p_joinlist, rtr);
if (addToNameSpace) }
pstate->p_namespace = lappend(pstate->p_namespace, rtr); if (addToRelNameSpace)
pstate->p_relnamespace = lappend(pstate->p_relnamespace, rte);
if (addToVarNameSpace)
pstate->p_varnamespace = lappend(pstate->p_varnamespace, rte);
} }
/* /*
...@@ -1166,7 +965,8 @@ addImplicitRTE(ParseState *pstate, RangeVar *relation) ...@@ -1166,7 +965,8 @@ addImplicitRTE(ParseState *pstate, RangeVar *relation)
RangeTblEntry *rte; RangeTblEntry *rte;
rte = addRangeTableEntry(pstate, relation, NULL, false, false); rte = addRangeTableEntry(pstate, relation, NULL, false, false);
addRTEtoQuery(pstate, rte, true, true); /* Add to joinlist and relnamespace, but not varnamespace */
addRTEtoQuery(pstate, rte, true, true, false);
warnAutoRange(pstate, relation); warnAutoRange(pstate, relation);
return rte; return rte;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.135 2005/06/04 19:19:42 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.136 2005/06/05 00:38:10 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -750,52 +750,32 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref) ...@@ -750,52 +750,32 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref)
* ExpandAllTables() * ExpandAllTables()
* Turns '*' (in the target list) into a list of targetlist entries. * Turns '*' (in the target list) into a list of targetlist entries.
* *
* tlist entries are generated for each relation appearing at the top level * tlist entries are generated for each relation appearing in the query's
* of the query's namespace, except for RTEs marked not inFromCl. (These * varnamespace. We do not consider relnamespace because that would include
* may include NEW/OLD pseudo-entries, implicit RTEs, etc.) * input tables of aliasless JOINs, NEW/OLD pseudo-entries, implicit RTEs,
* etc.
*/ */
static List * static List *
ExpandAllTables(ParseState *pstate) ExpandAllTables(ParseState *pstate)
{ {
List *target = NIL; List *target = NIL;
bool found_table = false; ListCell *l;
ListCell *ns;
foreach(ns, pstate->p_namespace)
{
Node *n = (Node *) lfirst(ns);
int rtindex;
RangeTblEntry *rte;
if (IsA(n, RangeTblRef)) /* Check for SELECT *; */
rtindex = ((RangeTblRef *) n)->rtindex; if (!pstate->p_varnamespace)
else if (IsA(n, JoinExpr)) ereport(ERROR,
rtindex = ((JoinExpr *) n)->rtindex; (errcode(ERRCODE_SYNTAX_ERROR),
else errmsg("SELECT * with no tables specified is not valid")));
{
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(n));
rtindex = 0; /* keep compiler quiet */
}
/* foreach(l, pstate->p_varnamespace)
* Ignore added-on relations that were not listed in the FROM {
* clause. RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
*/ int rtindex = RTERangeTablePosn(pstate, rte, NULL);
rte = rt_fetch(rtindex, pstate->p_rtable);
if (!rte->inFromCl)
continue;
found_table = true;
target = list_concat(target, target = list_concat(target,
expandRelAttrs(pstate, rte, rtindex, 0)); expandRelAttrs(pstate, rte, rtindex, 0));
} }
/* Check for SELECT *; */
if (!found_table)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("SELECT * with no tables specified is not valid")));
return target; return target;
} }
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* back to source text * back to source text
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.199 2005/06/03 23:05:29 tgl Exp $ * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.200 2005/06/05 00:38:10 tgl Exp $
* *
* This software is copyrighted by Jan Wieck - Hamburg. * This software is copyrighted by Jan Wieck - Hamburg.
* *
...@@ -4009,8 +4009,8 @@ get_from_clause(Query *query, const char *prefix, deparse_context *context) ...@@ -4009,8 +4009,8 @@ get_from_clause(Query *query, const char *prefix, deparse_context *context)
* We use the query's jointree as a guide to what to print. However, * We use the query's jointree as a guide to what to print. However,
* we must ignore auto-added RTEs that are marked not inFromCl. (These * we must ignore auto-added RTEs that are marked not inFromCl. (These
* can only appear at the top level of the jointree, so it's * can only appear at the top level of the jointree, so it's
* sufficient to check here.) Also ignore the rule pseudo-RTEs for NEW * sufficient to check here.) This check also ensures we ignore
* and OLD. * the rule pseudo-RTEs for NEW and OLD.
*/ */
foreach(l, query->jointree->fromlist) foreach(l, query->jointree->fromlist)
{ {
...@@ -4023,10 +4023,6 @@ get_from_clause(Query *query, const char *prefix, deparse_context *context) ...@@ -4023,10 +4023,6 @@ get_from_clause(Query *query, const char *prefix, deparse_context *context)
if (!rte->inFromCl) if (!rte->inFromCl)
continue; continue;
if (strcmp(rte->eref->aliasname, "*NEW*") == 0)
continue;
if (strcmp(rte->eref->aliasname, "*OLD*") == 0)
continue;
} }
if (first) if (first)
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, 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/nodes/parsenodes.h,v 1.279 2005/06/03 23:05:29 tgl Exp $ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.280 2005/06/05 00:38:10 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -479,10 +479,10 @@ typedef struct DefElem ...@@ -479,10 +479,10 @@ typedef struct DefElem
* FROM clause, but POSTQUEL allows you to refer to tables not listed, * FROM clause, but POSTQUEL allows you to refer to tables not listed,
* in which case a range table entry will be generated. We still support * in which case a range table entry will be generated. We still support
* this POSTQUEL feature, although there is some doubt whether it's * this POSTQUEL feature, although there is some doubt whether it's
* convenient or merely confusing. The flag is needed since an * convenient or merely confusing. The flag is not actually needed
* implicitly-added RTE shouldn't change the namespace for unqualified * anymore during parsing, since the parser uses a separate "namespace"
* column names processed later, and it also shouldn't affect the * data structure to control visibility, but it is needed by ruleutils.c
* expansion of '*'. * to determine whether RTEs should be included in decompiled queries.
* *
* requiredPerms and checkAsUser specify run-time access permissions * requiredPerms and checkAsUser specify run-time access permissions
* checks to be performed at query startup. The user must have *all* * checks to be performed at query startup. The user must have *all*
...@@ -552,7 +552,7 @@ typedef struct RangeTblEntry ...@@ -552,7 +552,7 @@ typedef struct RangeTblEntry
Alias *alias; /* user-written alias clause, if any */ Alias *alias; /* user-written alias clause, if any */
Alias *eref; /* expanded reference names */ Alias *eref; /* expanded reference names */
bool inh; /* inheritance requested? */ bool inh; /* inheritance requested? */
bool inFromCl; /* present in FROM clause */ bool inFromCl; /* present in FROM clause? */
AclMode requiredPerms; /* bitmask of required access permissions */ AclMode requiredPerms; /* bitmask of required access permissions */
AclId checkAsUser; /* if not zero, check access as this user */ AclId checkAsUser; /* if not zero, check access as this user */
} RangeTblEntry; } RangeTblEntry;
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, 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.43 2005/04/28 21:47:18 tgl Exp $ * $PostgreSQL: pgsql/src/include/parser/parse_node.h,v 1.44 2005/06/05 00:38:11 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -27,12 +27,19 @@ ...@@ -27,12 +27,19 @@
* p_joinlist: list of join items (RangeTblRef and JoinExpr nodes) that * p_joinlist: list of join items (RangeTblRef and JoinExpr nodes) that
* will become the fromlist of the query's top-level FromExpr node. * will become the fromlist of the query's top-level FromExpr node.
* *
* p_namespace: list of join items that represents the current namespace * p_relnamespace: list of RTEs that represents the current namespace for
* for table and column lookup. This may be just a subset of the rtable + * table lookup, ie, those RTEs that are accessible by qualified names.
* joinlist, and/or may contain entries that are not yet added to the main * This may be just a subset of the rtable + joinlist, and/or may contain
* joinlist. Note that an RTE that is present in p_namespace, but does not * entries that are not yet added to the main joinlist.
* have its inFromCl flag set, is accessible only with an explicit qualifier; *
* lookups of unqualified column names should ignore it. * p_varnamespace: list of RTEs that represents the current namespace for
* column lookup, ie, those RTEs that are accessible by unqualified names.
* This is different from p_relnamespace because a JOIN without an alias does
* not hide the contained tables (so they must still be in p_relnamespace)
* but it does hide their columns (unqualified references to the columns must
* refer to the JOIN, not the member tables). Also, we put POSTQUEL-style
* implicit RTEs into p_relnamespace but not p_varnamespace, so that they
* do not affect the set of columns available for unqualified references.
* *
* p_paramtypes: an array of p_numparams type OIDs for $n parameter symbols * 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 * (zeroth entry in array corresponds to $1). If p_variableparams is true, the
...@@ -49,7 +56,8 @@ typedef struct ParseState ...@@ -49,7 +56,8 @@ typedef struct ParseState
List *p_rtable; /* range table so far */ List *p_rtable; /* range table so far */
List *p_joinlist; /* join items so far (will become FromExpr List *p_joinlist; /* join items so far (will become FromExpr
* node's fromlist) */ * node's fromlist) */
List *p_namespace; /* current lookup namespace (join items) */ List *p_relnamespace; /* current namespace for relations */
List *p_varnamespace; /* current namespace for columns */
Oid *p_paramtypes; /* OIDs of types for $n parameter symbols */ Oid *p_paramtypes; /* OIDs of types for $n parameter symbols */
int p_numparams; /* allocated size of p_paramtypes[] */ 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 */
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, 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.50 2005/06/04 19:19:42 tgl Exp $ * $PostgreSQL: pgsql/src/include/parser/parse_relation.h,v 1.51 2005/06/05 00:38:11 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -22,8 +22,8 @@ extern RangeTblEntry *refnameRangeTblEntry(ParseState *pstate, ...@@ -22,8 +22,8 @@ extern RangeTblEntry *refnameRangeTblEntry(ParseState *pstate,
const char *schemaname, const char *schemaname,
const char *refname, const char *refname,
int *sublevels_up); int *sublevels_up);
extern void checkNameSpaceConflicts(ParseState *pstate, Node *namespace1, extern void checkNameSpaceConflicts(ParseState *pstate, List *namespace1,
Node *namespace2); List *namespace2);
extern int RTERangeTablePosn(ParseState *pstate, extern int RTERangeTablePosn(ParseState *pstate,
RangeTblEntry *rte, RangeTblEntry *rte,
int *sublevels_up); int *sublevels_up);
...@@ -64,7 +64,8 @@ extern RangeTblEntry *addRangeTableEntryForJoin(ParseState *pstate, ...@@ -64,7 +64,8 @@ extern RangeTblEntry *addRangeTableEntryForJoin(ParseState *pstate,
Alias *alias, Alias *alias,
bool inFromCl); bool inFromCl);
extern void addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte, extern void addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte,
bool addToJoinList, bool addToNameSpace); bool addToJoinList,
bool addToRelNameSpace, bool addToVarNameSpace);
extern RangeTblEntry *addImplicitRTE(ParseState *pstate, RangeVar *relation); extern RangeTblEntry *addImplicitRTE(ParseState *pstate, RangeVar *relation);
extern void expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, extern void expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
bool include_dropped, bool include_dropped,
......
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