aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/parse_clause.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser/parse_clause.c')
-rw-r--r--src/backend/parser/parse_clause.c208
1 files changed, 94 insertions, 114 deletions
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 0f3f9d23028..266b6da75b5 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.75 2001/01/24 19:43:01 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.76 2001/02/14 21:35:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -58,26 +58,30 @@ static bool exprIsInSortList(Node *expr, List *sortList, List *targetList);
/*
- * makeRangeTable -
- * Build the initial range table from the FROM clause.
+ * transformFromClause -
+ * Process the FROM clause and add items to the query's range table,
+ * joinlist, and namespace.
*
- * The range table constructed here may grow as we transform the expressions
- * in the query's quals and target list. (Note that this happens because in
- * POSTQUEL, we allow references to relations not specified in the
- * from-clause. PostgreSQL keeps this extension to standard SQL.)
+ * Note: we assume that pstate's p_rtable, p_joinlist, and p_namespace lists
+ * were initialized to NIL when the pstate was created. We will add onto
+ * any entries already present --- this is needed for rule processing, as
+ * well as for UPDATE and DELETE.
*
- * Note: we assume that pstate's p_rtable and p_joinlist lists were
- * initialized to NIL when the pstate was created. We will add onto
- * any entries already present --- this is needed for rule processing!
+ * 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
+ * POSTQUEL, we allowed references to relations not specified in the
+ * from-clause. PostgreSQL keeps this extension to standard SQL.)
*/
void
-makeRangeTable(ParseState *pstate, List *frmList)
+transformFromClause(ParseState *pstate, List *frmList)
{
List *fl;
/*
* The grammar will have produced a list of RangeVars, RangeSubselects,
- * and/or JoinExprs. Transform each one, and then add it to the joinlist.
+ * and/or JoinExprs. Transform each one (possibly adding entries to the
+ * rtable), check for duplicate refnames, and then add it to the joinlist
+ * and namespace.
*/
foreach(fl, frmList)
{
@@ -85,27 +89,41 @@ makeRangeTable(ParseState *pstate, List *frmList)
List *containedRels;
n = transformFromClauseItem(pstate, n, &containedRels);
+ checkNameSpaceConflicts(pstate, (Node *) pstate->p_namespace, n);
pstate->p_joinlist = lappend(pstate->p_joinlist, n);
+ pstate->p_namespace = lappend(pstate->p_namespace, n);
}
}
/*
- * lockTargetTable
- * Find the target relation of INSERT/UPDATE/DELETE and acquire write
- * lock on it. This must be done before building the range table,
- * in case the target is also mentioned as a source relation --- we
- * want to be sure to grab the write lock before any read lock.
+ * setTargetTable
+ * Add the target relation of INSERT/UPDATE/DELETE to the range table,
+ * and make the special links to it in the ParseState.
+ *
+ * We also open the target relation and acquire a write lock on it.
+ * This must be done before processing the FROM list, in case the target
+ * is also mentioned as a source relation --- we want to be sure to grab
+ * the write lock before any read lock.
*
- * The ParseState's link to the target relcache entry is also set here.
+ * If alsoSource is true, add the target to the query's joinlist and
+ * namespace. For INSERT, we don't want the target to be joined to;
+ * it's a destination of tuples, not a source. For UPDATE/DELETE,
+ * we do need to scan or join the target. (NOTE: we do not bother
+ * to check for namespace conflict; we assume that the namespace was
+ * initially empty in these cases.)
+ *
+ * Returns the rangetable index of the target relation.
*/
-void
-lockTargetTable(ParseState *pstate, char *relname)
+int
+setTargetTable(ParseState *pstate, char *relname,
+ bool inh, bool alsoSource)
{
+ RangeTblEntry *rte;
+ int rtindex;
+
/* Close old target; this could only happen for multi-action rules */
if (pstate->p_target_relation != NULL)
heap_close(pstate->p_target_relation, NoLock);
- pstate->p_target_relation = NULL;
- pstate->p_target_rangetblentry = NULL; /* setTargetTable will set this */
/*
* Open target rel and grab suitable lock (which we will hold till
@@ -115,62 +133,36 @@ lockTargetTable(ParseState *pstate, char *relname)
* but *not* release the lock.
*/
pstate->p_target_relation = heap_openr(relname, RowExclusiveLock);
-}
-/*
- * setTargetTable
- * Add the target relation of INSERT/UPDATE/DELETE to the range table,
- * and make the special links to it in the ParseState.
- *
- * inJoinSet says whether to add the target to the join list.
- * For INSERT, we don't want the target to be joined to; it's a
- * destination of tuples, not a source. For UPDATE/DELETE, we do
- * need to scan or join the target.
- */
-void
-setTargetTable(ParseState *pstate, char *relname, bool inh, bool inJoinSet)
-{
- RangeTblEntry *rte;
+ /*
+ * Now build an RTE.
+ */
+ rte = addRangeTableEntry(pstate, relname, NULL, inh, false);
+ pstate->p_target_rangetblentry = rte;
- /* look for relname only at current nesting level... */
- if (refnameRangeTablePosn(pstate, relname, NULL) == 0)
- {
- rte = addRangeTableEntry(pstate, relname, NULL, inh, false);
- /*
- * Since the rel wasn't in the rangetable already, it's not being
- * read; override addRangeTableEntry's default checkForRead.
- *
- * If we find an explicit reference to the rel later during
- * parse analysis, scanRTEForColumn will change checkForRead
- * to 'true' again. That can't happen for INSERT but it is
- * possible for UPDATE and DELETE.
- */
- rte->checkForRead = false;
- }
- else
- {
- rte = refnameRangeTableEntry(pstate, relname);
- /*
- * Since the rel was in the rangetable already, it's being read
- * as well as written. Therefore, leave checkForRead true.
- *
- * Force inh to the desired setting for the target (XXX is this
- * reasonable? It's *necessary* that INSERT target not be marked
- * inheritable, but otherwise not too clear what to do if conflict?)
- */
- rte->inh = inh;
- }
+ /* assume new rte is at end */
+ rtindex = length(pstate->p_rtable);
+ Assert(rte == rt_fetch(rtindex, pstate->p_rtable));
- /* Mark target table as requiring write access. */
+ /*
+ * Override addRangeTableEntry's default checkForRead, and instead
+ * mark target table as requiring write access.
+ *
+ * If we find an explicit reference to the rel later during
+ * parse analysis, scanRTEForColumn will change checkForRead
+ * to 'true' again. That can't happen for INSERT but it is
+ * possible for UPDATE and DELETE.
+ */
+ rte->checkForRead = false;
rte->checkForWrite = true;
- if (inJoinSet)
- addRTEtoJoinList(pstate, rte);
-
- /* lockTargetTable should have been called earlier */
- Assert(pstate->p_target_relation != NULL);
+ /*
+ * If UPDATE/DELETE, add table to joinlist and namespace.
+ */
+ if (alsoSource)
+ addRTEtoQuery(pstate, rte, true, true);
- pstate->p_target_rangetblentry = rte;
+ return rtindex;
}
/*
@@ -313,22 +305,21 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j,
List *containedRels)
{
Node *result;
- List *sv_joinlist;
+ List *save_namespace;
List *clause_varnos,
*l;
/*
- * This is a tad tricky, for two reasons. First, at the point where
- * we're called, the two subtrees of the JOIN node aren't yet part of
- * the pstate's joinlist, which means that transformExpr() won't resolve
- * unqualified references to their columns correctly. We fix this in a
- * slightly klugy way: temporarily make the pstate's joinlist consist of
- * just those two subtrees (which creates exactly the namespace the ON
- * clause should see). This is OK only because the ON clause can't
- * legally alter the joinlist by causing relation refs to be added.
+ * This is a tad tricky, for two reasons. First, the namespace that
+ * the join expression should see is just the two subtrees of the JOIN
+ * plus any outer references from upper pstate levels. So, temporarily
+ * set this pstate's namespace accordingly. (We need not check for
+ * refname conflicts, because transformFromClauseItem() already did.)
+ * NOTE: this code is OK only because the ON clause can't legally alter
+ * the namespace by causing implicit relation refs to be added.
*/
- sv_joinlist = pstate->p_joinlist;
- pstate->p_joinlist = makeList2(j->larg, j->rarg);
+ save_namespace = pstate->p_namespace;
+ pstate->p_namespace = makeList2(j->larg, j->rarg);
/* This part is just like transformWhereClause() */
result = transformExpr(pstate, j->quals, EXPR_COLUMN_FIRST);
@@ -338,14 +329,14 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j,
typeidTypeName(exprType(result)));
}
- pstate->p_joinlist = sv_joinlist;
+ pstate->p_namespace = save_namespace;
/*
* Second, we need to check that the ON condition doesn't refer to any
* rels outside the input subtrees of the JOIN. It could do that despite
- * our hack on the joinlist if it uses fully-qualified names. So, grovel
+ * our hack on the namespace if it uses fully-qualified names. So, grovel
* through the transformed clause and make sure there are no bogus
- * references.
+ * references. (Outer references are OK, and are ignored here.)
*/
clause_varnos = pull_varnos(result);
foreach(l, clause_varnos)
@@ -384,8 +375,8 @@ transformTableEntry(ParseState *pstate, RangeVar *r)
interpretInhOption(r->inhOpt), true);
/*
- * We create a RangeTblRef, but we do not add it to the joinlist here.
- * makeRangeTable will do so, if we are at top level of the FROM clause.
+ * 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 */
@@ -402,8 +393,7 @@ transformTableEntry(ParseState *pstate, RangeVar *r)
static RangeTblRef *
transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
{
- List *save_rtable;
- List *save_joinlist;
+ List *save_namespace;
List *parsetrees;
Query *query;
RangeTblEntry *rte;
@@ -424,15 +414,12 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
* does not include other FROM items). But it does need to be able to
* see any further-up parent states, so we can't just pass a null parent
* pstate link. So, temporarily make the current query level have an
- * empty rtable and joinlist.
+ * empty namespace.
*/
- save_rtable = pstate->p_rtable;
- save_joinlist = pstate->p_joinlist;
- pstate->p_rtable = NIL;
- pstate->p_joinlist = NIL;
+ save_namespace = pstate->p_namespace;
+ pstate->p_namespace = NIL;
parsetrees = parse_analyze(r->subquery, pstate);
- pstate->p_rtable = save_rtable;
- pstate->p_joinlist = save_joinlist;
+ pstate->p_namespace = save_namespace;
/*
* Check that we got something reasonable. Some of these conditions
@@ -456,8 +443,8 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
rte = addRangeTableEntryForSubquery(pstate, query, r->name, true);
/*
- * We create a RangeTblRef, but we do not add it to the joinlist here.
- * makeRangeTable will do so, if we are at top level of the FROM clause.
+ * 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 */
@@ -472,7 +459,7 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
* transformFromClauseItem -
* Transform a FROM-clause item, adding any required entries to the
* range table list being built in the ParseState, and return the
- * transformed item ready to include in the joinlist.
+ * transformed item ready to include in the joinlist and namespace.
* This routine can recurse to handle SQL92 JOIN expressions.
*
* Aside from the primary return value (the transformed joinlist item)
@@ -526,6 +513,13 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
*containedRels = nconc(l_containedRels, r_containedRels);
/*
+ * Check for conflicting refnames in left and right subtrees. Must
+ * do this because higher levels will assume I hand back a self-
+ * consistent namespace subtree.
+ */
+ checkNameSpaceConflicts(pstate, j->larg, j->rarg);
+
+ /*
* Extract column name and var lists from both subtrees
*/
if (IsA(j->larg, JoinExpr))
@@ -733,23 +727,9 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
/*
* Process alias (AS clause), if any.
- *
- * The given table alias must be unique in the current nesting level,
- * ie it cannot match any RTE refname or jointable alias. This is
- * a bit painful to check because my own child joins are not yet in
- * the pstate's joinlist, so they have to be scanned separately.
*/
if (j->alias)
{
- /* Check against previously created RTEs and joinlist entries */
- if (refnameRangeOrJoinEntry(pstate, j->alias->relname, NULL))
- elog(ERROR, "Table name \"%s\" specified more than once",
- j->alias->relname);
- /* Check children */
- if (scanJoinListForRefname(j->larg, j->alias->relname) ||
- scanJoinListForRefname(j->rarg, j->alias->relname))
- elog(ERROR, "Table name \"%s\" specified more than once",
- j->alias->relname);
/*
* If a column alias list is specified, substitute the alias
* names into my output-column list