aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/analyze.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser/analyze.c')
-rw-r--r--src/backend/parser/analyze.c122
1 files changed, 65 insertions, 57 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index b1ccda71bf7..11ceae19a68 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: analyze.c,v 1.178 2001/01/27 07:23:48 tgl Exp $
+ * $Id: analyze.c,v 1.179 2001/02/14 21:35:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -257,11 +257,10 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
qry->commandType = CMD_DELETE;
- /* set up a range table */
- lockTargetTable(pstate, stmt->relname);
- makeRangeTable(pstate, NIL);
- setTargetTable(pstate, stmt->relname,
- interpretInhOption(stmt->inhOpt), true);
+ /* set up range table with just the result rel */
+ qry->resultRelation = setTargetTable(pstate, stmt->relname,
+ interpretInhOption(stmt->inhOpt),
+ true);
qry->distinctClause = NIL;
@@ -271,7 +270,6 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
/* done building the range table and jointree */
qry->rtable = pstate->p_rtable;
qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
- qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
qry->hasSubLinks = pstate->p_hasSubLinks;
qry->hasAggs = pstate->p_hasAggs;
@@ -289,6 +287,8 @@ static Query *
transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
{
Query *qry = makeNode(Query);
+ List *sub_rtable;
+ List *sub_namespace;
List *icolumns;
List *attrnos;
List *attnos;
@@ -300,11 +300,35 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
pstate->p_is_insert = true;
/*
- * Must get write lock on target table before scanning SELECT,
+ * If a non-nil rangetable/namespace was passed in, and we are doing
+ * INSERT/SELECT, arrange to pass the rangetable/namespace down to the
+ * 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
+ * appear as part of the SELECT's rtable, not as outer references for
+ * it. (Kluge!) The SELECT's joinlist is not affected however.
+ * We must do this before adding the target table to the INSERT's rtable.
+ */
+ if (stmt->selectStmt)
+ {
+ sub_rtable = pstate->p_rtable;
+ pstate->p_rtable = NIL;
+ sub_namespace = pstate->p_namespace;
+ pstate->p_namespace = NIL;
+ }
+ else
+ {
+ sub_rtable = NIL; /* not used, but keep compiler quiet */
+ sub_namespace = NIL;
+ }
+
+ /*
+ * Must get write lock on INSERT target table before scanning SELECT,
* else we will grab the wrong kind of initial lock if the target
- * table is also mentioned in the SELECT part.
+ * table is also mentioned in the SELECT part. Note that the target
+ * table is not added to the joinlist or namespace.
*/
- lockTargetTable(pstate, stmt->relname);
+ qry->resultRelation = setTargetTable(pstate, stmt->relname,
+ false, false);
/*
* Is it INSERT ... SELECT or INSERT ... VALUES?
@@ -323,15 +347,12 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
* otherwise the behavior of SELECT within INSERT might be different
* from a stand-alone SELECT. (Indeed, Postgres up through 6.5 had
* bugs of just that nature...)
- *
- * If a non-nil rangetable was passed in, pass it down to the 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 appear as
- * part of the SELECT's rtable, not as outer references for it.
*/
- sub_pstate->p_rtable = pstate->p_rtable;
- pstate->p_rtable = NIL;
+ sub_pstate->p_rtable = sub_rtable;
+ sub_pstate->p_namespace = sub_namespace;
+
selectQuery = transformStmt(sub_pstate, stmt->selectStmt);
+
release_pstate_resources(sub_pstate);
pfree(sub_pstate);
@@ -341,7 +362,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
elog(ERROR, "INSERT ... SELECT may not specify INTO");
/*
* Make the source be a subquery in the INSERT's rangetable,
- * and add it to the joinlist.
+ * and add it to the INSERT's joinlist.
*/
rte = addRangeTableEntryForSubquery(pstate,
selectQuery,
@@ -400,13 +421,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
/*
* Now we are done with SELECT-like processing, and can get on with
* transforming the target list to match the INSERT target columns.
- *
- * In particular, it's time to add the INSERT target to the rangetable.
- * (We didn't want it there until now since it shouldn't be visible in
- * the SELECT part.) Note that the INSERT target is NOT added to the
- * joinlist, since we don't want to join over it.
*/
- setTargetTable(pstate, stmt->relname, false, false);
/* Prepare to assign non-conflicting resnos to resjunk attributes */
if (pstate->p_last_resno <= pstate->p_target_relation->rd_rel->relnatts)
@@ -495,7 +510,6 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
/* done building the range table and jointree */
qry->rtable = pstate->p_rtable;
qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
- qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
qry->hasSubLinks = pstate->p_hasSubLinks;
qry->hasAggs = pstate->p_hasAggs;
@@ -1565,27 +1579,27 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
oldrte->checkForRead = false;
newrte->checkForRead = false;
/*
- * They must be in the joinlist too for lookup purposes, but only add
+ * They must be in the namespace too for lookup purposes, but only add
* 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
* unambiguous, but there's no need to be so picky for INSERT & DELETE.
* (Note we marked the RTEs "inFromCl = true" above to allow unqualified
- * references to their fields.)
+ * references to their fields.) We do not add them to the joinlist.
*/
switch (stmt->event)
{
case CMD_SELECT:
- addRTEtoJoinList(pstate, oldrte);
+ addRTEtoQuery(pstate, oldrte, false, true);
break;
case CMD_UPDATE:
- addRTEtoJoinList(pstate, oldrte);
- addRTEtoJoinList(pstate, newrte);
+ addRTEtoQuery(pstate, oldrte, false, true);
+ addRTEtoQuery(pstate, newrte, false, true);
break;
case CMD_INSERT:
- addRTEtoJoinList(pstate, newrte);
+ addRTEtoQuery(pstate, newrte, false, true);
break;
case CMD_DELETE:
- addRTEtoJoinList(pstate, oldrte);
+ addRTEtoQuery(pstate, oldrte, false, true);
break;
default:
elog(ERROR, "transformRuleStmt: unexpected event type %d",
@@ -1638,8 +1652,9 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
* Set up OLD/NEW in the rtable for this statement. The entries
* are marked not inFromCl because we don't want them to be
* referred to by unqualified field names nor "*" in the rule
- * actions. We don't need to add them to the joinlist for
- * qualified-name lookup, either (see qualifiedNameToVar()).
+ * actions. We must add them to the namespace, however, or they
+ * won't be accessible at all. We decide later whether to put
+ * them in the joinlist.
*/
oldrte = addRangeTableEntry(sub_pstate, stmt->object->relname,
makeAttr("*OLD*", NULL),
@@ -1649,6 +1664,8 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
false, false);
oldrte->checkForRead = false;
newrte->checkForRead = false;
+ addRTEtoQuery(sub_pstate, oldrte, false, true);
+ addRTEtoQuery(sub_pstate, newrte, false, true);
/* Transform the rule action statement */
top_subqry = transformStmt(sub_pstate, lfirst(actions));
@@ -1712,10 +1729,10 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
*/
if (has_old || (has_new && stmt->event == CMD_UPDATE))
{
- /* hack so we can use addRTEtoJoinList() */
+ /* hack so we can use addRTEtoQuery() */
sub_pstate->p_rtable = sub_qry->rtable;
sub_pstate->p_joinlist = sub_qry->jointree->fromlist;
- addRTEtoJoinList(sub_pstate, oldrte);
+ addRTEtoQuery(sub_pstate, oldrte, true, false);
sub_qry->jointree->fromlist = sub_pstate->p_joinlist;
}
@@ -1779,8 +1796,8 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
/* make FOR UPDATE clause available to addRangeTableEntry */
pstate->p_forUpdate = stmt->forUpdate;
- /* set up a range table */
- makeRangeTable(pstate, stmt->fromClause);
+ /* process the FROM clause */
+ transformFromClause(pstate, stmt->fromClause);
/* transform targetlist and WHERE */
qry->targetList = transformTargetList(pstate, stmt->targetList);
@@ -2055,7 +2072,6 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt)
if (isLeaf)
{
/* Process leaf SELECT */
- List *save_rtable;
List *selectList;
Query *selectQuery;
char selectName[32];
@@ -2063,16 +2079,13 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt)
RangeTblRef *rtr;
/*
- * Transform SelectStmt into a Query. We do not want any previously
- * transformed leaf queries to be visible in the outer context of
- * this sub-query, so temporarily make the top-level pstate have an
- * empty rtable. (We needn't do the same with the joinlist because
- * we aren't entering anything in the top-level joinlist.)
+ * Transform SelectStmt into a Query.
+ *
+ * Note: previously transformed sub-queries don't affect the parsing
+ * of this sub-query, because they are not in the toplevel pstate's
+ * namespace list.
*/
- save_rtable = pstate->p_rtable;
- pstate->p_rtable = NIL;
selectList = parse_analyze((Node *) stmt, pstate);
- pstate->p_rtable = save_rtable;
Assert(length(selectList) == 1);
selectQuery = (Query *) lfirst(selectList);
@@ -2202,19 +2215,15 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
qry->commandType = CMD_UPDATE;
pstate->p_is_update = true;
+ qry->resultRelation = setTargetTable(pstate, stmt->relname,
+ interpretInhOption(stmt->inhOpt),
+ true);
+
/*
* the FROM clause is non-standard SQL syntax. We used to be able to
* do this with REPLACE in POSTQUEL so we keep the feature.
- *
- * Note: it's critical here that we process FROM before adding the
- * target table to the rtable --- otherwise, if the target is also
- * used in FROM, we'd fail to notice that it should be marked
- * checkForRead as well as checkForWrite. See setTargetTable().
*/
- lockTargetTable(pstate, stmt->relname);
- makeRangeTable(pstate, stmt->fromClause);
- setTargetTable(pstate, stmt->relname,
- interpretInhOption(stmt->inhOpt), true);
+ transformFromClause(pstate, stmt->fromClause);
qry->targetList = transformTargetList(pstate, stmt->targetList);
@@ -2222,7 +2231,6 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
qry->rtable = pstate->p_rtable;
qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
- qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
qry->hasSubLinks = pstate->p_hasSubLinks;
qry->hasAggs = pstate->p_hasAggs;