diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index c67f7f3543..f0aa9042e0 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.159 2001/02/12 20:07:21 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.160 2001/02/14 21:34:59 tgl Exp $ * * * INTERFACE ROUTINES @@ -1533,7 +1533,6 @@ StoreAttrDefault(Relation rel, AttrNumber attnum, char *adbin, bool updatePgAttribute) { Node *expr; - RangeTblEntry *rte; char *adsrc; Relation adrel; Relation idescs[Num_pg_attrdef_indices]; @@ -1551,16 +1550,12 @@ StoreAttrDefault(Relation rel, AttrNumber attnum, char *adbin, expr = stringToNode(adbin); /* - * deparse_expression needs a RangeTblEntry list, so make one + * deparse it */ - rte = makeNode(RangeTblEntry); - rte->relname = RelationGetRelationName(rel); - rte->relid = RelationGetRelid(rel); - rte->eref = makeNode(Attr); - rte->eref->relname = RelationGetRelationName(rel); - rte->inh = false; - rte->inFromCl = true; - adsrc = deparse_expression(expr, makeList1(makeList1(rte)), false); + adsrc = deparse_expression(expr, + deparse_context_for(RelationGetRelationName(rel), + RelationGetRelid(rel)), + false); values[Anum_pg_attrdef_adrelid - 1] = RelationGetRelid(rel); values[Anum_pg_attrdef_adnum - 1] = attnum; @@ -1619,7 +1614,6 @@ static void StoreRelCheck(Relation rel, char *ccname, char *ccbin) { Node *expr; - RangeTblEntry *rte; char *ccsrc; Relation rcrel; Relation idescs[Num_pg_relcheck_indices]; @@ -1634,16 +1628,12 @@ StoreRelCheck(Relation rel, char *ccname, char *ccbin) expr = (Node *) make_ands_explicit((List *) expr); /* - * deparse_expression needs a RangeTblEntry list, so make one + * deparse it */ - rte = makeNode(RangeTblEntry); - rte->relname = RelationGetRelationName(rel); - rte->relid = RelationGetRelid(rel); - rte->eref = makeNode(Attr); - rte->eref->relname = RelationGetRelationName(rel); - rte->inh = false; - rte->inFromCl = true; - ccsrc = deparse_expression(expr, makeList1(makeList1(rte)), false); + ccsrc = deparse_expression(expr, + deparse_context_for(RelationGetRelationName(rel), + RelationGetRelid(rel)), + false); values[Anum_pg_relcheck_rcrelid - 1] = RelationGetRelid(rel); values[Anum_pg_relcheck_rcname - 1] = DirectFunctionCall1(namein, @@ -1764,9 +1754,8 @@ AddRelationRawConstraints(Relation rel, * sole rangetable entry. We need a ParseState for transformExpr. */ pstate = make_parsestate(NULL); - makeRangeTable(pstate, NULL); rte = addRangeTableEntry(pstate, relname, NULL, false, true); - addRTEtoJoinList(pstate, rte); + addRTEtoQuery(pstate, rte, true, true); /* * Process column default expressions. diff --git a/src/backend/commands/command.c b/src/backend/commands/command.c index 30695a7a90..8808a03f1a 100644 --- a/src/backend/commands/command.c +++ b/src/backend/commands/command.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.120 2001/01/29 00:39:20 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.121 2001/02/14 21:35:00 tgl Exp $ * * NOTES * The PerformAddAttribute() code, like most of the relation @@ -1136,10 +1136,9 @@ AlterTableAddConstraint(char *relationName, * the expression we can pass to ExecQual */ pstate = make_parsestate(NULL); - makeRangeTable(pstate, NULL); rte = addRangeTableEntry(pstate, relationName, NULL, false, true); - addRTEtoJoinList(pstate, rte); + addRTEtoQuery(pstate, rte, true, true); /* Convert the A_EXPR in raw_expr into an EXPR */ expr = transformExpr(pstate, constr->raw_expr, diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index b1ccda71bf..11ceae19a6 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, - * else we will grab the wrong kind of initial lock if the target - * table is also mentioned in the SELECT part. + * 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. */ - lockTargetTable(pstate, stmt->relname); + 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. Note that the target + * table is not added to the joinlist or namespace. + */ + 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; diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index 0f3f9d2302..266b6da75b 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 + * 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. + * + * 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.) - * - * 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! */ 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. * - * The ParseState's link to the target relcache entry is also set here. + * 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. + * + * 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); + /* + * If UPDATE/DELETE, add table to joinlist and namespace. + */ + if (alsoSource) + addRTEtoQuery(pstate, rte, true, true); - /* lockTargetTable should have been called earlier */ - Assert(pstate->p_target_relation != NULL); - - 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) @@ -525,6 +512,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 */ @@ -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 diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index d39f06006a..93a986d835 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.89 2001/01/24 19:43:01 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.90 2001/02/14 21:35:04 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -541,7 +541,8 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection) { if (indirection == NIL) return basenode; - return (Node *) transformArraySubscripts(pstate, basenode, + return (Node *) transformArraySubscripts(pstate, + basenode, exprType(basenode), indirection, false, NULL); } @@ -558,13 +559,14 @@ static Node * transformIdent(ParseState *pstate, Ident *ident, int precedence) { Node *result = NULL; + int sublevels_up; /* * try to find the ident as a relation ... but not if subscripts * appear */ if (ident->indirection == NIL && - refnameRangeTableEntry(pstate, ident->name) != NULL) + refnameRangeOrJoinEntry(pstate, ident->name, &sublevels_up) != NULL) { ident->isRel = TRUE; result = (Node *) ident; diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index cb4914849b..e0d2b51552 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.98 2001/01/24 19:43:02 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.99 2001/02/14 21:35:04 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -427,6 +427,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, { RangeTblEntry *rte; int vnum; + Node *rteorjoin; int sublevels_up; /* @@ -434,9 +435,29 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, */ refname = ((Ident *) arg)->name; - rte = refnameRangeTableEntry(pstate, refname); - if (rte == NULL) + rteorjoin = refnameRangeOrJoinEntry(pstate, refname, + &sublevels_up); + + if (rteorjoin == NULL) + { rte = addImplicitRTE(pstate, refname); + } + else if (IsA(rteorjoin, RangeTblEntry)) + { + rte = (RangeTblEntry *) rteorjoin; + } + else if (IsA(rteorjoin, JoinExpr)) + { + elog(ERROR, + "function applied to tuple is not supported for joins"); + rte = NULL; /* keep compiler quiet */ + } + else + { + elog(ERROR, "ParseFuncOrColumn: unexpected node type %d", + nodeTag(rteorjoin)); + rte = NULL; /* keep compiler quiet */ + } vnum = RTERangeTablePosn(pstate, rte, &sublevels_up); diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c index afe8ae1b80..36e43166aa 100644 --- a/src/backend/parser/parse_node.c +++ b/src/backend/parser/parse_node.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.51 2001/01/24 19:43:02 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.52 2001/02/14 21:35:04 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -229,20 +229,22 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno) * * pstate Parse state * arrayBase Already-transformed expression for the array as a whole + * (may be NULL if we are handling an INSERT) + * arrayType OID of array's datatype * indirection Untransformed list of subscripts (must not be NIL) * forceSlice If true, treat subscript as array slice in all cases * assignFrom NULL for array fetch, else transformed expression for source. */ -ArrayRef * +ArrayRef * transformArraySubscripts(ParseState *pstate, Node *arrayBase, + Oid arrayType, List *indirection, bool forceSlice, Node *assignFrom) { - Oid typearray, - typeelement, - typeresult; + Oid elementType, + resultType; HeapTuple type_tuple_array, type_tuple_element; Form_pg_type type_struct_array, @@ -254,28 +256,26 @@ transformArraySubscripts(ParseState *pstate, ArrayRef *aref; /* Get the type tuple for the array */ - typearray = exprType(arrayBase); - type_tuple_array = SearchSysCache(TYPEOID, - ObjectIdGetDatum(typearray), + ObjectIdGetDatum(arrayType), 0, 0, 0); if (!HeapTupleIsValid(type_tuple_array)) elog(ERROR, "transformArraySubscripts: Cache lookup failed for array type %u", - typearray); + arrayType); type_struct_array = (Form_pg_type) GETSTRUCT(type_tuple_array); - typeelement = type_struct_array->typelem; - if (typeelement == InvalidOid) + elementType = type_struct_array->typelem; + if (elementType == InvalidOid) elog(ERROR, "transformArraySubscripts: type %s is not an array", NameStr(type_struct_array->typname)); /* Get the type tuple for the array element type */ type_tuple_element = SearchSysCache(TYPEOID, - ObjectIdGetDatum(typeelement), + ObjectIdGetDatum(elementType), 0, 0, 0); if (!HeapTupleIsValid(type_tuple_element)) elog(ERROR, "transformArraySubscripts: Cache lookup failed for array element type %u", - typeelement); + elementType); type_struct_element = (Form_pg_type) GETSTRUCT(type_tuple_element); /* @@ -308,9 +308,9 @@ transformArraySubscripts(ParseState *pstate, * array type if we are fetching a slice or storing. */ if (isSlice || assignFrom != NULL) - typeresult = typearray; + resultType = arrayType; else - typeresult = typeelement; + resultType = elementType; /* * Transform the subscript expressions. @@ -359,7 +359,7 @@ transformArraySubscripts(ParseState *pstate, if (assignFrom != NULL) { Oid typesource = exprType(assignFrom); - Oid typeneeded = isSlice ? typearray : typeelement; + Oid typeneeded = isSlice ? arrayType : elementType; if (typesource != InvalidOid) { @@ -385,7 +385,7 @@ transformArraySubscripts(ParseState *pstate, aref = makeNode(ArrayRef); aref->refattrlength = type_struct_array->typlen; aref->refelemlength = type_struct_element->typlen; - aref->refelemtype = typeresult; /* XXX should save element type + aref->refelemtype = resultType; /* XXX should save element type * too */ aref->refelembyval = type_struct_element->typbyval; aref->refupperindexpr = upperIndexpr; diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 1b5d0afc71..d9280529c4 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 - #include "postgres.h" +#include + #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. * - * 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. + * 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: 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)) + { + int varno = ((RangeTblRef *) nsnode)->rtindex; + RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable); + + if (strcmp(rte->eref->relname, refname) == 0) + result = (Node *) rte; + } + else if (IsA(nsnode, JoinExpr)) + { + 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(nsnode, List)) { List *l; - foreach(l, (List *) jtnode) + foreach(l, (List *) nsnode) { - result = scanJoinListForRefname(lfirst(l), refname); + result = scanNameSpaceForRefname(pstate, lfirst(l), refname); if (result) break; } } - else if (IsA(jtnode, RangeTblRef)) - { - /* ignore ... */ - } - else if (IsA(jtnode, JoinExpr)) - { - JoinExpr *j = (JoinExpr *) jtnode; - - if (j->alias && strcmp(j->alias->relname, refname) == 0) - return j; - result = scanJoinListForRefname(j->larg, refname); - if (! result) - result = scanJoinListForRefname(j->rarg, refname); - } 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. * - * NOTE that this routine will ONLY find RTEs, not join tables. + * 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: 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; - - while (pstate != NULL) + if (namespace1 == NULL) + return; + if (IsA(namespace1, RangeTblRef)) { - index = 1; - foreach(temp, pstate->p_rtable) - { - RangeTblEntry *rte = lfirst(temp); + int varno = ((RangeTblRef *) namespace1)->rtindex; + RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable); - if (strcmp(rte->eref->relname, refname) == 0) - return index; - index++; - } - pstate = pstate->parentParseState; - if (sublevels_up) - (*sublevels_up)++; - else - break; + scanNameSpaceForConflict(pstate, namespace2, rte->eref->relname); } - return 0; + else if (IsA(namespace1, JoinExpr)) + { + JoinExpr *j = (JoinExpr *) namespace1; + + if (j->alias) + { + 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; + + foreach(l, (List *) namespace1) + { + checkNameSpaceConflicts(pstate, lfirst(l), namespace2); + } + } + 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; + RangeTblRef *rtr = makeNode(RangeTblRef); - 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 */ - } - } - - /* 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); } - diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 647b176f05..6b566da747 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.64 2001/01/24 19:43:02 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.65 2001/02/14 21:35:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -212,29 +212,37 @@ updateTargetListEntry(ParseState *pstate, */ if (indirection) { - Attr *att = makeAttr(pstrdup(RelationGetRelationName(rd)), - colname); Node *arrayBase; ArrayRef *aref; - arrayBase = ParseNestedFuncOrColumn(pstate, att, EXPR_COLUMN_FIRST); - aref = transformArraySubscripts(pstate, arrayBase, - indirection, - pstate->p_is_insert, - tle->expr); if (pstate->p_is_insert) { - /* * The command is INSERT INTO table (arraycol[subscripts]) ... * so there is not really a source array value to work with. * Let the executor do something reasonable, if it can. Notice - * that we forced transformArraySubscripts to treat the - * subscripting op as an array-slice op above, so the source - * data will have been coerced to array type. + * that we force transformArraySubscripts to treat the + * subscripting op as an array-slice op below, so the source + * data will have been coerced to the array type. */ - aref->refexpr = NULL; /* signal there is no source array */ + arrayBase = NULL; /* signal there is no source array */ } + else + { + /* + * Build a Var for the array to be updated. + */ + arrayBase = (Node *) make_var(pstate, + pstate->p_target_rangetblentry, + attrno); + } + + aref = transformArraySubscripts(pstate, + arrayBase, + attrtype, + indirection, + pstate->p_is_insert, + tle->expr); tle->expr = (Node *) aref; } else @@ -385,22 +393,19 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos) /* ExpandAllTables() * Turns '*' (in the target list) into a list of targetlist entries. * - * tlist entries are generated for each relation appearing in the FROM list, - * which by now has been transformed into a joinlist. + * tlist entries are generated for each relation appearing at the top level + * of the query's namespace, except for RTEs marked not inFromCl. (These + * may include NEW/OLD pseudo-entries, implicit RTEs, etc.) */ static List * ExpandAllTables(ParseState *pstate) { List *target = NIL; - List *jt; + List *ns; - /* SELECT *; */ - if (pstate->p_joinlist == NIL) - elog(ERROR, "Wildcard with no tables specified not allowed"); - - foreach(jt, pstate->p_joinlist) + foreach(ns, pstate->p_namespace) { - Node *n = (Node *) lfirst(jt); + Node *n = (Node *) lfirst(ns); if (IsA(n, RangeTblRef)) { @@ -431,6 +436,10 @@ ExpandAllTables(ParseState *pstate) "\n\t%s", nodeToString(n)); } + /* Check for SELECT *; */ + if (target == NIL) + elog(ERROR, "Wildcard with no tables specified not allowed"); + return target; } diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 3f9264308d..872b607e87 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -3,7 +3,7 @@ * back to source text * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.71 2001/01/03 22:01:05 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.72 2001/02/14 21:35:05 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -35,11 +35,11 @@ * **********************************************************************/ +#include "postgres.h" + #include #include -#include "postgres.h" - #include "catalog/pg_index.h" #include "catalog/pg_operator.h" #include "catalog/pg_shadow.h" @@ -52,6 +52,7 @@ #include "parser/parse_expr.h" #include "parser/parsetree.h" #include "rewrite/rewriteManip.h" +#include "utils/fmgroids.h" #include "utils/lsyscache.h" @@ -59,13 +60,30 @@ * Local data types * ---------- */ + +/* Context info needed for invoking a recursive querytree display routine */ typedef struct { StringInfo buf; /* output buffer to append to */ - List *rangetables; /* List of List of RangeTblEntry */ + List *namespaces; /* List of deparse_namespace nodes */ bool varprefix; /* TRUE to print prefixes on Vars */ } deparse_context; +/* + * Each level of query context around a subtree needs a level of Var namespace. + * The rangetable is the list of actual RTEs, and the namespace indicates + * which parts of the rangetable are accessible (and under what aliases) + * in the expression currently being looked at. A Var having varlevelsup=N + * refers to the N'th item (counting from 0) in the current context's + * namespaces list. + */ +typedef struct +{ + List *rtable; /* List of RangeTblEntry nodes */ + List *namespace; /* List of joinlist items (RangeTblRef and + * JoinExpr nodes) */ +} deparse_namespace; + /* ---------- * Global data @@ -92,7 +110,7 @@ static char *query_getopclass = "SELECT * FROM pg_opclass WHERE oid = $1"; */ static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc); static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc); -static void get_query_def(Query *query, StringInfo buf, List *parentrtables); +static void get_query_def(Query *query, StringInfo buf, List *parentnamespace); static void get_select_query_def(Query *query, deparse_context *context); static void get_insert_query_def(Query *query, deparse_context *context); static void get_update_query_def(Query *query, deparse_context *context); @@ -102,7 +120,14 @@ static void get_basic_select_query(Query *query, deparse_context *context); static void get_setop_query(Node *setOp, Query *query, deparse_context *context, bool toplevel); static bool simple_distinct(List *distinctClause, List *targetList); -static RangeTblEntry *get_rte_for_var(Var *var, deparse_context *context); +static void get_names_for_var(Var *var, deparse_context *context, + char **refname, char **attname); +static bool get_alias_for_case(CaseExpr *caseexpr, deparse_context *context, + char **refname, char **attname); +static bool find_alias_in_namespace(Node *nsnode, Node *expr, + List *rangetable, int levelsup, + char **refname, char **attname); +static bool phony_equal(Node *expr1, Node *expr2, int levelsup); static void get_rule_expr(Node *node, deparse_context *context); static void get_func_expr(Expr *expr, deparse_context *context); static void get_tle_expr(TargetEntry *tle, deparse_context *context); @@ -599,30 +624,24 @@ pg_get_userbyid(PG_FUNCTION_ARGS) * expr is the node tree to be deparsed. It must be a transformed expression * tree (ie, not the raw output of gram.y). * - * rangetables is a List of Lists of RangeTblEntry nodes: first sublist is for - * varlevelsup = 0, next for varlevelsup = 1, etc. In each sublist the first - * item is for varno = 1, next varno = 2, etc. (Each sublist has the same - * format as the rtable list of a parsetree or query.) + * dpcontext is a list of deparse_namespace nodes representing the context + * for interpreting Vars in the node tree. * * forceprefix is TRUE to force all Vars to be prefixed with their table names. - * Otherwise, a prefix is printed only if there's more than one table involved - * (and someday the code might try to print one only if there's ambiguity). * * The result is a palloc'd string. * ---------- */ char * -deparse_expression(Node *expr, List *rangetables, bool forceprefix) +deparse_expression(Node *expr, List *dpcontext, bool forceprefix) { StringInfoData buf; deparse_context context; initStringInfo(&buf); context.buf = &buf; - context.rangetables = rangetables; - context.varprefix = (forceprefix || - length(rangetables) != 1 || - length((List *) lfirst(rangetables)) != 1); + context.namespaces = dpcontext; + context.varprefix = forceprefix; rulename = ""; /* in case of errors */ @@ -631,6 +650,43 @@ deparse_expression(Node *expr, List *rangetables, bool forceprefix) return buf.data; } +/* ---------- + * deparse_context_for - Build deparse context for a single relation + * + * Given the name and OID of a relation, build deparsing context for an + * expression referencing only that relation (as varno 1, varlevelsup 0). + * This is presently sufficient for the external uses of deparse_expression. + * ---------- + */ +List * +deparse_context_for(char *relname, Oid relid) +{ + deparse_namespace *dpns; + RangeTblEntry *rte; + RangeTblRef *rtr; + + dpns = (deparse_namespace *) palloc(sizeof(deparse_namespace)); + + /* Build a minimal RTE for the rel */ + rte = makeNode(RangeTblEntry); + rte->relname = relname; + rte->relid = relid; + rte->eref = makeNode(Attr); + rte->eref->relname = relname; + rte->inh = false; + rte->inFromCl = true; + /* Build one-element rtable */ + dpns->rtable = makeList1(rte); + + /* Build a namespace list referencing this RTE only */ + rtr = makeNode(RangeTblRef); + rtr->rtindex = 1; + dpns->namespace = makeList1(rtr); + + /* Return a one-deep namespace stack */ + return makeList1(dpns); +} + /* ---------- * make_ruledef - reconstruct the CREATE RULE command * for a given pg_rewrite tuple @@ -722,6 +778,7 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc) Node *qual; Query *query; deparse_context context; + deparse_namespace dpns; appendStringInfo(buf, " WHERE "); @@ -729,8 +786,10 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc) query = (Query *) lfirst(actions); context.buf = buf; - context.rangetables = makeList1(query->rtable); + context.namespaces = makeList1(&dpns); context.varprefix = (length(query->rtable) != 1); + dpns.rtable = query->rtable; + dpns.namespace = query->jointree ? query->jointree->fromlist : NIL; get_rule_expr(qual, &context); } @@ -844,14 +903,17 @@ make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc) * ---------- */ static void -get_query_def(Query *query, StringInfo buf, List *parentrtables) +get_query_def(Query *query, StringInfo buf, List *parentnamespace) { deparse_context context; + deparse_namespace dpns; context.buf = buf; - context.rangetables = lcons(query->rtable, parentrtables); - context.varprefix = (parentrtables != NIL || + context.namespaces = lcons(&dpns, parentnamespace); + context.varprefix = (parentnamespace != NIL || length(query->rtable) != 1); + dpns.rtable = query->rtable; + dpns.namespace = query->jointree ? query->jointree->fromlist : NIL; switch (query->commandType) { @@ -1025,11 +1087,10 @@ get_basic_select_query(Query *query, deparse_context *context) else { Var *var = (Var *) (tle->expr); - RangeTblEntry *rte; + char *refname; char *attname; - rte = get_rte_for_var(var, context); - attname = get_rte_attribute_name(rte, var->varattno); + get_names_for_var(var, context, &refname, &attname); tell_as = (strcmp(attname, tle->resdom->resname) != 0); } @@ -1088,7 +1149,7 @@ get_setop_query(Node *setOp, Query *query, deparse_context *context, Query *subquery = rte->subquery; Assert(subquery != NULL); - get_query_def(subquery, buf, context->rangetables); + get_query_def(subquery, buf, context->namespaces); } else if (IsA(setOp, SetOperationStmt)) { @@ -1336,20 +1397,277 @@ get_utility_query_def(Query *query, deparse_context *context) /* - * Find the RTE referenced by a (possibly nonlocal) Var. + * Get the relation refname and attname for a (possibly nonlocal) Var. + * + * This is trickier than it ought to be because of the possibility of aliases + * and limited scope of refnames. We have to try to return the correct alias + * with respect to the current namespace given by the context. */ -static RangeTblEntry * -get_rte_for_var(Var *var, deparse_context *context) +static void +get_names_for_var(Var *var, deparse_context *context, + char **refname, char **attname) { - List *rtlist = context->rangetables; + List *nslist = context->namespaces; int sup = var->varlevelsup; + deparse_namespace *dpns; + RangeTblEntry *rte; - while (sup-- > 0) - rtlist = lnext(rtlist); + /* Find appropriate nesting depth */ + while (sup-- > 0 && nslist != NIL) + nslist = lnext(nslist); + if (nslist == NIL) + elog(ERROR, "get_names_for_var: bogus varlevelsup %d", + var->varlevelsup); + dpns = (deparse_namespace *) lfirst(nslist); - return rt_fetch(var->varno, (List *) lfirst(rtlist)); + /* Scan namespace to see if we can find an alias for the var */ + if (find_alias_in_namespace((Node *) dpns->namespace, (Node *) var, + dpns->rtable, var->varlevelsup, + refname, attname)) + return; + + /* + * Otherwise, fall back on the rangetable entry. This should happen + * only for uses of special RTEs like *NEW* and *OLD*, which won't + * get placed in our namespace. + */ + rte = rt_fetch(var->varno, dpns->rtable); + *refname = rte->eref->relname; + *attname = get_rte_attribute_name(rte, var->varattno); } +/* + * Check to see if a CASE expression matches a FULL JOIN's output expression. + * If so, return the refname and alias it should be expressed as. + */ +static bool +get_alias_for_case(CaseExpr *caseexpr, deparse_context *context, + char **refname, char **attname) +{ + List *nslist; + int sup; + + /* + * This could be done more efficiently if we first groveled through the + * CASE to find varlevelsup values, but it's probably not worth the + * trouble. All this code will go away someday anyway ... + */ + + sup = 0; + foreach(nslist, context->namespaces) + { + deparse_namespace *dpns = (deparse_namespace *) lfirst(nslist); + + if (find_alias_in_namespace((Node *) dpns->namespace, + (Node *) caseexpr, + dpns->rtable, sup, + refname, attname)) + return true; + sup++; + } + return false; +} + +/* + * Recursively scan a namespace (same representation as a jointree) to see + * if we can find an alias for the given expression. If so, return the + * correct alias refname and attname. The expression may be either a plain + * Var or a CASE expression (which may be a FULL JOIN reference). + */ +static bool +find_alias_in_namespace(Node *nsnode, Node *expr, + List *rangetable, int levelsup, + char **refname, char **attname) +{ + if (nsnode == NULL) + return false; + if (IsA(nsnode, RangeTblRef)) + { + if (IsA(expr, Var)) + { + Var *var = (Var *) expr; + int rtindex = ((RangeTblRef *) nsnode)->rtindex; + + if (var->varno == rtindex && var->varlevelsup == levelsup) + { + RangeTblEntry *rte = rt_fetch(rtindex, rangetable); + + *refname = rte->eref->relname; + *attname = get_rte_attribute_name(rte, var->varattno); + return true; + } + } + } + else if (IsA(nsnode, JoinExpr)) + { + JoinExpr *j = (JoinExpr *) nsnode; + + if (j->alias) + { + List *vlist; + List *nlist; + + /* + * Does the expr match any of the output columns of the join? + * + * We can't just use equal() here, because the given expr may + * have nonzero levelsup, whereas the saved expression in the + * JoinExpr should have zero levelsup. + */ + nlist = j->colnames; + foreach(vlist, j->colvars) + { + if (phony_equal(lfirst(vlist), expr, levelsup)) + { + *refname = j->alias->relname; + *attname = strVal(lfirst(nlist)); + return true; + } + nlist = lnext(nlist); + } + /* + * 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 false; + } + if (find_alias_in_namespace(j->larg, expr, + rangetable, levelsup, + refname, attname)) + return true; + if (find_alias_in_namespace(j->rarg, expr, + rangetable, levelsup, + refname, attname)) + return true; + } + else if (IsA(nsnode, List)) + { + List *l; + + foreach(l, (List *) nsnode) + { + if (find_alias_in_namespace(lfirst(l), expr, + rangetable, levelsup, + refname, attname)) + return true; + } + } + else + elog(ERROR, "find_alias_in_namespace: unexpected node type %d", + nodeTag(nsnode)); + return false; +} + +/* + * Check for equality of two expressions, with the proviso that all Vars in + * expr1 should have varlevelsup = 0, while all Vars in expr2 should have + * varlevelsup = levelsup. + * + * In reality we only need to support equality checks on Vars and the type + * of CASE expression that is used for FULL JOIN outputs, so not all node + * types need be handled here. + * + * Otherwise, this code is a straight ripoff from equalfuncs.c. + */ +static bool +phony_equal(Node *expr1, Node *expr2, int levelsup) +{ + if (expr1 == NULL || expr2 == NULL) + return (expr1 == expr2); + if (nodeTag(expr1) != nodeTag(expr2)) + return false; + if (IsA(expr1, Var)) + { + Var *a = (Var *) expr1; + Var *b = (Var *) expr2; + + if (a->varno != b->varno) + return false; + if (a->varattno != b->varattno) + return false; + if (a->vartype != b->vartype) + return false; + if (a->vartypmod != b->vartypmod) + return false; + if (a->varlevelsup != 0 || b->varlevelsup != levelsup) + return false; + if (a->varnoold != b->varnoold) + return false; + if (a->varoattno != b->varoattno) + return false; + return true; + } + if (IsA(expr1, CaseExpr)) + { + CaseExpr *a = (CaseExpr *) expr1; + CaseExpr *b = (CaseExpr *) expr2; + + if (a->casetype != b->casetype) + return false; + if (!phony_equal(a->arg, b->arg, levelsup)) + return false; + if (!phony_equal((Node *) a->args, (Node *) b->args, levelsup)) + return false; + if (!phony_equal(a->defresult, b->defresult, levelsup)) + return false; + return true; + } + if (IsA(expr1, CaseWhen)) + { + CaseWhen *a = (CaseWhen *) expr1; + CaseWhen *b = (CaseWhen *) expr2; + + if (!phony_equal(a->expr, b->expr, levelsup)) + return false; + if (!phony_equal(a->result, b->result, levelsup)) + return false; + return true; + } + if (IsA(expr1, Expr)) + { + Expr *a = (Expr *) expr1; + Expr *b = (Expr *) expr2; + + if (a->opType != b->opType) + return false; + if (!phony_equal(a->oper, b->oper, levelsup)) + return false; + if (!phony_equal((Node *) a->args, (Node *) b->args, levelsup)) + return false; + return true; + } + if (IsA(expr1, Func)) + { + Func *a = (Func *) expr1; + Func *b = (Func *) expr2; + + if (a->funcid != b->funcid) + return false; + if (a->functype != b->functype) + return false; + return true; + } + if (IsA(expr1, List)) + { + List *la = (List *) expr1; + List *lb = (List *) expr2; + List *l; + + if (length(la) != length(lb)) + return false; + foreach(l, la) + { + if (!phony_equal(lfirst(l), lfirst(lb), levelsup)) + return false; + lb = lnext(lb); + } + return true; + } + /* If we get here, there was something weird in a JOIN's colvars list */ + elog(ERROR, "phony_equal: unexpected node type %d", nodeTag(expr1)); + return false; +} /* ---------- * get_rule_expr - Parse back an expression @@ -1381,21 +1699,21 @@ get_rule_expr(Node *node, deparse_context *context) case T_Var: { Var *var = (Var *) node; - RangeTblEntry *rte = get_rte_for_var(var, context); + char *refname; + char *attname; + get_names_for_var(var, context, &refname, &attname); if (context->varprefix) { - if (strcmp(rte->eref->relname, "*NEW*") == 0) + if (strcmp(refname, "*NEW*") == 0) appendStringInfo(buf, "new."); - else if (strcmp(rte->eref->relname, "*OLD*") == 0) + else if (strcmp(refname, "*OLD*") == 0) appendStringInfo(buf, "old."); else appendStringInfo(buf, "%s.", - quote_identifier(rte->eref->relname)); + quote_identifier(refname)); } - appendStringInfo(buf, "%s", - quote_identifier(get_rte_attribute_name(rte, - var->varattno))); + appendStringInfo(buf, "%s", quote_identifier(attname)); } break; @@ -1606,6 +1924,19 @@ get_rule_expr(Node *node, deparse_context *context) { CaseExpr *caseexpr = (CaseExpr *) node; List *temp; + char *refname; + char *attname; + + /* Hack for providing aliases for FULL JOIN outputs */ + if (get_alias_for_case(caseexpr, context, + &refname, &attname)) + { + if (context->varprefix) + appendStringInfo(buf, "%s.", + quote_identifier(refname)); + appendStringInfo(buf, "%s", quote_identifier(attname)); + break; + } appendStringInfo(buf, "CASE"); foreach(temp, caseexpr->args) @@ -1645,6 +1976,7 @@ get_func_expr(Expr *expr, deparse_context *context) { StringInfo buf = context->buf; Func *func = (Func *) (expr->oper); + Oid funcoid = func->funcid; HeapTuple proctup; Form_pg_proc procStruct; char *proname; @@ -1652,41 +1984,36 @@ get_func_expr(Expr *expr, deparse_context *context) List *l; char *sep; - /* - * Get the functions pg_proc tuple - */ - proctup = SearchSysCache(PROCOID, - ObjectIdGetDatum(func->funcid), - 0, 0, 0); - if (!HeapTupleIsValid(proctup)) - elog(ERROR, "cache lookup for proc %u failed", func->funcid); - - procStruct = (Form_pg_proc) GETSTRUCT(proctup); - proname = NameStr(procStruct->proname); - /* * nullvalue() and nonnullvalue() should get turned into special * syntax */ - if (procStruct->pronargs == 1 && procStruct->proargtypes[0] == InvalidOid) + if (funcoid == F_NULLVALUE) { - if (strcmp(proname, "nullvalue") == 0) - { - appendStringInfoChar(buf, '('); - get_rule_expr((Node *) lfirst(expr->args), context); - appendStringInfo(buf, " ISNULL)"); - ReleaseSysCache(proctup); - return; - } - if (strcmp(proname, "nonnullvalue") == 0) - { - appendStringInfoChar(buf, '('); - get_rule_expr((Node *) lfirst(expr->args), context); - appendStringInfo(buf, " NOTNULL)"); - ReleaseSysCache(proctup); - return; - } + appendStringInfoChar(buf, '('); + get_rule_expr((Node *) lfirst(expr->args), context); + appendStringInfo(buf, " ISNULL)"); + return; } + if (funcoid == F_NONNULLVALUE) + { + appendStringInfoChar(buf, '('); + get_rule_expr((Node *) lfirst(expr->args), context); + appendStringInfo(buf, " NOTNULL)"); + return; + } + + /* + * Get the functions pg_proc tuple + */ + proctup = SearchSysCache(PROCOID, + ObjectIdGetDatum(funcoid), + 0, 0, 0); + if (!HeapTupleIsValid(proctup)) + elog(ERROR, "cache lookup for proc %u failed", funcoid); + + procStruct = (Form_pg_proc) GETSTRUCT(proctup); + proname = NameStr(procStruct->proname); /* * Check to see if function is a length-coercion function for some @@ -1968,7 +2295,7 @@ get_sublink_expr(Node *node, deparse_context *context) if (need_paren) appendStringInfoChar(buf, '('); - get_query_def(query, buf, context->rangetables); + get_query_def(query, buf, context->namespaces); if (need_paren) appendStringInfo(buf, "))"); @@ -2024,6 +2351,16 @@ static void get_from_clause_item(Node *jtnode, Query *query, deparse_context *context) { StringInfo buf = context->buf; + deparse_namespace *dpns; + List *sv_namespace; + + /* + * FROM-clause items have limited visibility of query's namespace. + * Save and restore the outer namespace setting while we munge it. + */ + dpns = (deparse_namespace *) lfirst(context->namespaces); + sv_namespace = dpns->namespace; + dpns->namespace = NIL; if (IsA(jtnode, RangeTblRef)) { @@ -2042,7 +2379,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context) /* Subquery RTE */ Assert(rte->subquery != NULL); appendStringInfoChar(buf, '('); - get_query_def(rte->subquery, buf, context->rangetables); + get_query_def(rte->subquery, buf, context->namespaces); appendStringInfoChar(buf, ')'); } if (rte->alias != NULL) @@ -2053,7 +2390,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context) { List *col; - appendStringInfo(buf, " ("); + appendStringInfo(buf, "("); foreach(col, rte->alias->attrs) { if (col != rte->alias->attrs) @@ -2116,6 +2453,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context) } else if (j->quals) { + dpns->namespace = makeList2(j->larg, j->rarg); appendStringInfo(buf, " ON ("); get_rule_expr(j->quals, context); appendStringInfoChar(buf, ')'); @@ -2131,7 +2469,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context) { List *col; - appendStringInfo(buf, " ("); + appendStringInfo(buf, "("); foreach(col, j->alias->attrs) { if (col != j->alias->attrs) @@ -2146,6 +2484,8 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context) else elog(ERROR, "get_from_clause_item: unexpected node type %d", nodeTag(jtnode)); + + dpns->namespace = sv_namespace; } diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 2ca40e8cec..f5cd6ea461 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -10,7 +10,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: primnodes.h,v 1.51 2001/01/24 19:43:26 momjian Exp $ + * $Id: primnodes.h,v 1.52 2001/02/14 21:35:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -29,7 +29,7 @@ typedef struct FunctionCache *FunctionCachePtr; * ---------------------------------------------------------------- */ -/* +/*-------------------- * Resdom (Result Domain) * * Notes: @@ -50,7 +50,7 @@ typedef struct FunctionCache *FunctionCachePtr; * * Both reskey and reskeyop are typically zero during parse/plan stages. * The executor does not pay any attention to ressortgroupref. - * + *-------------------- */ typedef struct Resdom { @@ -129,7 +129,6 @@ typedef struct Expr * list. But varnoold/varoattno continue to hold the original values. * The code doesn't really need varnoold/varoattno, but they are very useful * for debugging and interpreting completed plans, so we keep them around. - * ---------------- */ #define INNER 65000 #define OUTER 65001 @@ -153,7 +152,7 @@ typedef struct Var AttrNumber varoattno; /* original value of varattno */ } Var; -/* +/*-------------------- * Oper * * NOTE: in the good old days 'opno' used to be both (or either, or @@ -169,7 +168,7 @@ typedef struct Var * Note also that opid is not necessarily filled in immediately on creation * of the node. The planner makes sure it is valid before passing the node * tree to the executor, but during parsing/planning opid is typically 0. - * + *-------------------- */ typedef struct Oper { @@ -499,10 +498,14 @@ typedef struct RangeTblRef * are not equivalent to ON() since they also affect the output column list. * * alias is an Attr node representing the AS alias-clause attached to the - * join expression, or NULL if no clause. During parse analysis, colnames - * is filled with a list of String nodes giving the column names (real or - * alias) of the output of the join, and colvars is filled with a list of - * expressions that can be copied to reference the output columns. + * join expression, or NULL if no clause. NB: presence or absence of the + * alias has a critical impact on semantics, because a join with an alias + * restricts visibility of the tables/columns inside it. + * + * During parse analysis, colnames is filled with a list of String nodes + * giving the column names (real or alias) of the output of the join, + * and colvars is filled with a list of expressions that can be copied to + * reference the output columns. *---------- */ typedef struct JoinExpr diff --git a/src/include/parser/parse_clause.h b/src/include/parser/parse_clause.h index 8f760ca38b..31d5542efc 100644 --- a/src/include/parser/parse_clause.h +++ b/src/include/parser/parse_clause.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: parse_clause.h,v 1.22 2001/01/24 19:43:27 momjian Exp $ + * $Id: parse_clause.h,v 1.23 2001/02/14 21:35:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -16,10 +16,9 @@ #include "parser/parse_node.h" -extern void makeRangeTable(ParseState *pstate, List *frmList); -extern void lockTargetTable(ParseState *pstate, char *relname); -extern void setTargetTable(ParseState *pstate, char *relname, - bool inh, bool inJoinSet); +extern void transformFromClause(ParseState *pstate, List *frmList); +extern int setTargetTable(ParseState *pstate, char *relname, + bool inh, bool alsoSource); extern bool interpretInhOption(InhOption inhOpt); extern Node *transformWhereClause(ParseState *pstate, Node *where); extern List *transformGroupClause(ParseState *pstate, List *grouplist, diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index d8fcd6ee90..bb0229abcd 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: parse_node.h,v 1.24 2001/01/24 19:43:27 momjian Exp $ + * $Id: parse_node.h,v 1.25 2001/02/14 21:35:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -18,6 +18,20 @@ /* * State information used during parse analysis + * + * p_rtable: list of RTEs that will become the rangetable of the query. + * Note that neither relname nor refname of these entries are necessarily + * unique; searching the rtable by name is a bad idea. + * + * p_joinlist: list of join items (RangeTblRef and JoinExpr nodes) that + * will become the fromlist of the query's top-level FromExpr node. + * + * p_namespace: list of join items that represents the current namespace + * for table and column lookup. This may be just a subset of the rtable + + * joinlist, and/or may contain entries that are not yet added to the main + * joinlist. Note that an RTE that is present in p_namespace, but does not + * have its inFromCl flag set, is accessible only with an explicit qualifier; + * lookups of unqualified column names should ignore it. */ typedef struct ParseState { @@ -25,6 +39,7 @@ typedef struct ParseState List *p_rtable; /* range table so far */ List *p_joinlist; /* join items so far (will become * FromExpr node's fromlist) */ + List *p_namespace; /* current lookup namespace (join items) */ int p_last_resno; /* last targetlist resno assigned */ List *p_forUpdate; /* FOR UPDATE clause, if any (see gram.y) */ bool p_hasAggs; @@ -42,6 +57,7 @@ extern Node *make_operand(char *opname, Node *tree, extern Var *make_var(ParseState *pstate, RangeTblEntry *rte, int attrno); extern ArrayRef *transformArraySubscripts(ParseState *pstate, Node *arrayBase, + Oid arrayType, List *indirection, bool forceSlice, Node *assignFrom); diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h index bfdf7e8c2a..274de9e889 100644 --- a/src/include/parser/parse_relation.h +++ b/src/include/parser/parse_relation.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: parse_relation.h,v 1.21 2001/01/24 19:43:27 momjian Exp $ + * $Id: parse_relation.h,v 1.22 2001/02/14 21:35:06 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -19,15 +19,11 @@ extern Node *refnameRangeOrJoinEntry(ParseState *pstate, char *refname, int *sublevels_up); -extern RangeTblEntry *refnameRangeTableEntry(ParseState *pstate, - char *refname); -extern int refnameRangeTablePosn(ParseState *pstate, - char *refname, - int *sublevels_up); +extern void checkNameSpaceConflicts(ParseState *pstate, Node *namespace1, + Node *namespace2); extern int RTERangeTablePosn(ParseState *pstate, RangeTblEntry *rte, int *sublevels_up); -extern JoinExpr *scanJoinListForRefname(Node *jtnode, char *refname); extern Node *colnameToVar(ParseState *pstate, char *colname); extern Node *qualifiedNameToVar(ParseState *pstate, char *refname, char *colname, bool implicitRTEOK); @@ -40,7 +36,8 @@ extern RangeTblEntry *addRangeTableEntryForSubquery(ParseState *pstate, Query *subquery, Attr *alias, bool inFromCl); -extern void addRTEtoJoinList(ParseState *pstate, RangeTblEntry *rte); +extern void addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte, + bool addToJoinList, bool addToNameSpace); extern RangeTblEntry *addImplicitRTE(ParseState *pstate, char *relname); extern void expandRTE(ParseState *pstate, RangeTblEntry *rte, List **colnames, List **colvars); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 28cb478189..dcc923a36d 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: builtins.h,v 1.146 2001/01/24 19:43:28 momjian Exp $ + * $Id: builtins.h,v 1.147 2001/02/14 21:35:06 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -325,8 +325,9 @@ extern Datum pg_get_ruledef(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); extern Datum pg_get_userbyid(PG_FUNCTION_ARGS); -extern char *deparse_expression(Node *expr, List *rangetables, +extern char *deparse_expression(Node *expr, List *dpcontext, bool forceprefix); +extern List *deparse_context_for(char *relname, Oid relid); /* selfuncs.c */ extern Datum eqsel(PG_FUNCTION_ARGS); diff --git a/src/tools/backend/index.html b/src/tools/backend/index.html index 03a5a643e0..cfcc6c6ac9 100644 --- a/src/tools/backend/index.html +++ b/src/tools/backend/index.html @@ -67,7 +67,7 @@ Each table referenced in the query is represented by a RangeTableEntry, and they are linked together to form the range table of the query, which is generated by -makeRangeTable(). Query.rtable holds the query's range table.

+transformFromClause(). Query.rtable holds the query's range table.

Certain queries, like SELECT, return columns of data. Other