diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2001-02-14 21:35:07 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2001-02-14 21:35:07 +0000 |
commit | 4a66f9dd54694eb4d7ecce2c7e0f0c50dfde88cd (patch) | |
tree | 8810441569d5cf2e29f2a5c2b67ceb91d74deb2d /src/backend/parser/analyze.c | |
parent | d42d31e78e2f9db73edb0b0ed35cafb1c409bdbf (diff) | |
download | postgresql-4a66f9dd54694eb4d7ecce2c7e0f0c50dfde88cd.tar.gz postgresql-4a66f9dd54694eb4d7ecce2c7e0f0c50dfde88cd.zip |
Change scoping of table and join refnames to conform to SQL92: a JOIN
clause with an alias is a <subquery> and therefore hides table references
appearing within it, according to the spec. This is the same as the
preliminary patch I posted to pgsql-patches yesterday, plus some really
grotty code in ruleutils.c to reverse-list a query tree with the correct
alias name depending on context. I'd rather not have done that, but unless
we want to force another initdb for 7.1, there's no other way for now.
Diffstat (limited to 'src/backend/parser/analyze.c')
-rw-r--r-- | src/backend/parser/analyze.c | 122 |
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; |