diff options
Diffstat (limited to 'src/backend/parser/parse_relation.c')
-rw-r--r-- | src/backend/parser/parse_relation.c | 300 |
1 files changed, 144 insertions, 156 deletions
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 1b5d0afc719..d9280529c4f 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -8,14 +8,14 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.51 2001/01/24 19:43:02 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.52 2001/02/14 21:35:04 tgl Exp $ * *------------------------------------------------------------------------- */ -#include <ctype.h> - #include "postgres.h" +#include <ctype.h> + #include "access/heapam.h" #include "access/htup.h" #include "catalog/pg_type.h" @@ -30,6 +30,8 @@ #include "utils/lsyscache.h" +static Node *scanNameSpaceForRefname(ParseState *pstate, Node *nsnode, + char *refname); static Node *scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname); static Node *scanJoinForColumn(JoinExpr *join, char *colname, @@ -93,25 +95,13 @@ refnameRangeOrJoinEntry(ParseState *pstate, while (pstate != NULL) { - List *temp; - JoinExpr *join; + Node *rte; - /* - * Check the rangetable for RTEs; if no match, recursively scan - * the joinlist for join tables. We assume that no duplicate - * entries have been made in any one nesting level. - */ - foreach(temp, pstate->p_rtable) - { - RangeTblEntry *rte = lfirst(temp); - - if (strcmp(rte->eref->relname, refname) == 0) - return (Node *) rte; - } - - join = scanJoinListForRefname((Node *) pstate->p_joinlist, refname); - if (join) - return (Node *) join; + rte = scanNameSpaceForRefname(pstate, + (Node *) pstate->p_namespace, + refname); + if (rte) + return rte; pstate = pstate->parentParseState; if (sublevels_up) @@ -123,108 +113,129 @@ refnameRangeOrJoinEntry(ParseState *pstate, } /* - * Recursively search a joinlist for a joinexpr with given refname + * Recursively search a namespace for an RTE or joinexpr with given refname. + * + * The top level of p_namespace is a list, and we recurse into any joins + * that are not subqueries. It is also possible to pass an individual + * join subtree (useful when checking for name conflicts within a scope). * - * Note that during parse analysis, we don't expect to find a FromExpr node - * in p_joinlist; its top level is just a bare List. + * Note: we do not worry about the possibility of multiple matches; + * we assume the code that built the namespace checked for duplicates. */ -JoinExpr * -scanJoinListForRefname(Node *jtnode, char *refname) +static Node * +scanNameSpaceForRefname(ParseState *pstate, Node *nsnode, + char *refname) { - JoinExpr *result = NULL; + Node *result = NULL; - if (jtnode == NULL) + if (nsnode == NULL) return NULL; - if (IsA(jtnode, List)) + if (IsA(nsnode, RangeTblRef)) { - List *l; + int varno = ((RangeTblRef *) nsnode)->rtindex; + RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable); - foreach(l, (List *) jtnode) - { - result = scanJoinListForRefname(lfirst(l), refname); - if (result) - break; - } + if (strcmp(rte->eref->relname, refname) == 0) + result = (Node *) rte; } - else if (IsA(jtnode, RangeTblRef)) + else if (IsA(nsnode, JoinExpr)) { - /* ignore ... */ + JoinExpr *j = (JoinExpr *) nsnode; + + if (j->alias) + { + if (strcmp(j->alias->relname, refname) == 0) + 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); + if (! result) + result = scanNameSpaceForRefname(pstate, j->rarg, refname); } - else if (IsA(jtnode, JoinExpr)) + else if (IsA(nsnode, List)) { - JoinExpr *j = (JoinExpr *) jtnode; + List *l; - if (j->alias && strcmp(j->alias->relname, refname) == 0) - return j; - result = scanJoinListForRefname(j->larg, refname); - if (! result) - result = scanJoinListForRefname(j->rarg, refname); + foreach(l, (List *) nsnode) + { + result = scanNameSpaceForRefname(pstate, lfirst(l), refname); + if (result) + break; + } } else - elog(ERROR, "scanJoinListForRefname: unexpected node type %d", - nodeTag(jtnode)); + elog(ERROR, "scanNameSpaceForRefname: unexpected node type %d", + nodeTag(nsnode)); return result; } -/* - * given refname, return a pointer to the range table entry. - * - * NOTE that this routine will ONLY find RTEs, not join tables. - */ -RangeTblEntry * -refnameRangeTableEntry(ParseState *pstate, char *refname) +/* Convenience subroutine for checkNameSpaceConflicts */ +static void +scanNameSpaceForConflict(ParseState *pstate, Node *nsnode, + char *refname) { - List *temp; - - while (pstate != NULL) - { - foreach(temp, pstate->p_rtable) - { - RangeTblEntry *rte = lfirst(temp); - - if (strcmp(rte->eref->relname, refname) == 0) - return rte; - } - pstate = pstate->parentParseState; - } - return NULL; + if (scanNameSpaceForRefname(pstate, nsnode, refname) != NULL) + elog(ERROR, "Table name \"%s\" specified more than once", refname); } /* - * given refname, return RT index (starting with 1) of the relation, - * and optionally get its nesting depth (0 = current). If sublevels_up - * is NULL, only consider rels at the current nesting level. - * A zero result means name not found. + * Recursively check for refname conflicts between two namespaces or + * namespace subtrees. Raise an error if any is found. + * + * Works by recursively scanning namespace1 in the same way that + * scanNameSpaceForRefname does, and then looking in namespace2 for + * a match to each refname found in namespace1. * - * NOTE that this routine will ONLY find RTEs, not join tables. + * Note: we assume that each given argument does not contain conflicts + * itself; we just want to know if the two can be merged together. */ -int -refnameRangeTablePosn(ParseState *pstate, char *refname, int *sublevels_up) +void +checkNameSpaceConflicts(ParseState *pstate, Node *namespace1, + Node *namespace2) { - int index; - List *temp; - - if (sublevels_up) - *sublevels_up = 0; + if (namespace1 == NULL) + return; + if (IsA(namespace1, RangeTblRef)) + { + int varno = ((RangeTblRef *) namespace1)->rtindex; + RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable); - while (pstate != NULL) + scanNameSpaceForConflict(pstate, namespace2, rte->eref->relname); + } + else if (IsA(namespace1, JoinExpr)) { - index = 1; - foreach(temp, pstate->p_rtable) + JoinExpr *j = (JoinExpr *) namespace1; + + if (j->alias) { - RangeTblEntry *rte = lfirst(temp); + scanNameSpaceForConflict(pstate, namespace2, j->alias->relname); + /* + * 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)) + { + List *l; - if (strcmp(rte->eref->relname, refname) == 0) - return index; - index++; + foreach(l, (List *) namespace1) + { + checkNameSpaceConflicts(pstate, lfirst(l), namespace2); } - pstate = pstate->parentParseState; - if (sublevels_up) - (*sublevels_up)++; - else - break; } - return 0; + else + elog(ERROR, "checkNameSpaceConflicts: unexpected node type %d", + nodeTag(namespace1)); } /* @@ -257,6 +268,7 @@ RTERangeTablePosn(ParseState *pstate, RangeTblEntry *rte, int *sublevels_up) else break; } + elog(ERROR, "RTERangeTablePosn: RTE not found (internal error)"); return 0; /* keep compiler quiet */ } @@ -369,21 +381,21 @@ colnameToVar(ParseState *pstate, char *colname) while (pstate != NULL) { - List *jt; + List *ns; /* - * We want to look only at top-level jointree items, and even for + * 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(jt, pstate->p_joinlist) + foreach(ns, pstate->p_namespace) { - Node *jtnode = (Node *) lfirst(jt); + Node *nsnode = (Node *) lfirst(ns); Node *newresult = NULL; - if (IsA(jtnode, RangeTblRef)) + if (IsA(nsnode, RangeTblRef)) { - int varno = ((RangeTblRef *) jtnode)->rtindex; + int varno = ((RangeTblRef *) nsnode)->rtindex; RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable); if (! rte->inFromCl && @@ -393,15 +405,15 @@ colnameToVar(ParseState *pstate, char *colname) /* use orig_pstate here to get the right sublevels_up */ newresult = scanRTEForColumn(orig_pstate, rte, colname); } - else if (IsA(jtnode, JoinExpr)) + else if (IsA(nsnode, JoinExpr)) { - JoinExpr *j = (JoinExpr *) jtnode; + JoinExpr *j = (JoinExpr *) nsnode; newresult = scanJoinForColumn(j, colname, levels_up); } else elog(ERROR, "colnameToVar: unexpected node type %d", - nodeTag(jtnode)); + nodeTag(nsnode)); if (newresult) { @@ -451,7 +463,7 @@ qualifiedNameToVar(ParseState *pstate, char *refname, char *colname, colname); else if (IsA(rteorjoin, JoinExpr)) result = scanJoinForColumn((JoinExpr *) rteorjoin, - colname, sublevels_up); + colname, sublevels_up); else { elog(ERROR, "qualifiedNameToVar: unexpected node type %d", @@ -465,10 +477,11 @@ qualifiedNameToVar(ParseState *pstate, char *refname, char *colname, /* * Add an entry for a relation to the pstate's range table (p_rtable). * - * If the specified refname is already present, raise error. + * If pstate is NULL, we just build an RTE and return it without adding it + * to an rtable list. * - * If pstate is NULL, we just build an RTE and return it without worrying - * about membership in an rtable list. + * Note: formerly this checked for refname conflicts, but that's wrong. + * Caller is responsible for checking for conflicts in the appropriate scope. */ RangeTblEntry * addRangeTableEntry(ParseState *pstate, @@ -477,27 +490,15 @@ addRangeTableEntry(ParseState *pstate, bool inh, bool inFromCl) { + RangeTblEntry *rte = makeNode(RangeTblEntry); char *refname = alias ? alias->relname : relname; LOCKMODE lockmode; Relation rel; - RangeTblEntry *rte; Attr *eref; int maxattrs; int numaliases; int varattno; - /* Check for conflicting RTE or jointable alias (at level 0 only) */ - if (pstate != NULL) - { - Node *rteorjoin = refnameRangeOrJoinEntry(pstate, refname, NULL); - - if (rteorjoin) - elog(ERROR, "Table name \"%s\" specified more than once", - refname); - } - - rte = makeNode(RangeTblEntry); - rte->relname = relname; rte->alias = alias; rte->subquery = NULL; @@ -559,7 +560,8 @@ addRangeTableEntry(ParseState *pstate, rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ /* - * Add completed RTE to range table list. + * Add completed RTE to pstate's range table list, but not to join list + * nor namespace --- caller must do that if appropriate. */ if (pstate != NULL) pstate->p_rtable = lappend(pstate->p_rtable, rte); @@ -579,25 +581,13 @@ addRangeTableEntryForSubquery(ParseState *pstate, Attr *alias, bool inFromCl) { + RangeTblEntry *rte = makeNode(RangeTblEntry); char *refname = alias->relname; - RangeTblEntry *rte; Attr *eref; int numaliases; int varattno; List *tlistitem; - /* Check for conflicting RTE or jointable alias (at level 0 only) */ - if (pstate != NULL) - { - Node *rteorjoin = refnameRangeOrJoinEntry(pstate, refname, NULL); - - if (rteorjoin) - elog(ERROR, "Table name \"%s\" specified more than once", - refname); - } - - rte = makeNode(RangeTblEntry); - rte->relname = NULL; rte->relid = InvalidOid; rte->subquery = subquery; @@ -647,7 +637,8 @@ addRangeTableEntryForSubquery(ParseState *pstate, rte->checkAsUser = InvalidOid; /* - * Add completed RTE to range table list. + * Add completed RTE to pstate's range table list, but not to join list + * nor namespace --- caller must do that if appropriate. */ if (pstate != NULL) pstate->p_rtable = lappend(pstate->p_rtable, rte); @@ -691,37 +682,30 @@ isForUpdate(ParseState *pstate, char *relname) } /* - * Add the given RTE as a top-level entry in the pstate's join list, - * unless there already is an entry for it. + * 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 + * namespace conflict.) */ void -addRTEtoJoinList(ParseState *pstate, RangeTblEntry *rte) +addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte, + bool addToJoinList, bool addToNameSpace) { int rtindex = RTERangeTablePosn(pstate, rte, NULL); - List *jt; - RangeTblRef *rtr; - - foreach(jt, pstate->p_joinlist) - { - Node *n = (Node *) lfirst(jt); - - if (IsA(n, RangeTblRef)) - { - if (rtindex == ((RangeTblRef *) n)->rtindex) - return; /* it's already being joined to */ - } - } + RangeTblRef *rtr = makeNode(RangeTblRef); - /* Not present, so add it */ - rtr = makeNode(RangeTblRef); rtr->rtindex = rtindex; - pstate->p_joinlist = lappend(pstate->p_joinlist, rtr); + + if (addToJoinList) + pstate->p_joinlist = lappend(pstate->p_joinlist, rtr); + if (addToNameSpace) + pstate->p_namespace = lappend(pstate->p_namespace, rtr); } /* * Add a POSTQUEL-style implicit RTE. * - * We assume caller has already checked that there is no such RTE now. + * We assume caller has already checked that there is no RTE or join with + * a conflicting name. */ RangeTblEntry * addImplicitRTE(ParseState *pstate, char *relname) @@ -729,7 +713,7 @@ addImplicitRTE(ParseState *pstate, char *relname) RangeTblEntry *rte; rte = addRangeTableEntry(pstate, relname, NULL, false, false); - addRTEtoJoinList(pstate, rte); + addRTEtoQuery(pstate, rte, true, true); warnAutoRange(pstate, relname); return rte; @@ -922,6 +906,11 @@ expandNamesVars(ParseState *pstate, List *names, List *vars) * This is unlike get_attname() because we use aliases if available. * In particular, it will work on an RTE for a subselect, whereas * get_attname() only works on real relations. + * + * XXX Actually, this is completely bogus, because refnames of RTEs are + * not guaranteed unique, and may not even have scope across the whole + * query. Cleanest fix would be to add refname/attname to Var nodes and + * just print those, rather than indulging in this hack. * ---------- */ char * @@ -1088,4 +1077,3 @@ warnAutoRange(ParseState *pstate, char *refname) pstate->parentParseState != NULL ? " in subquery" : "", refname); } - |