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 | |
parent | d42d31e78e2f9db73edb0b0ed35cafb1c409bdbf (diff) |
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')
-rw-r--r-- | src/backend/parser/analyze.c | 122 | ||||
-rw-r--r-- | src/backend/parser/parse_clause.c | 208 | ||||
-rw-r--r-- | src/backend/parser/parse_expr.c | 8 | ||||
-rw-r--r-- | src/backend/parser/parse_func.c | 27 | ||||
-rw-r--r-- | src/backend/parser/parse_node.c | 34 | ||||
-rw-r--r-- | src/backend/parser/parse_relation.c | 300 | ||||
-rw-r--r-- | src/backend/parser/parse_target.c | 53 |
7 files changed, 380 insertions, 372 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; diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index 0f3f9d23028..266b6da75b5 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.75 2001/01/24 19:43:01 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.76 2001/02/14 21:35:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -58,26 +58,30 @@ static bool exprIsInSortList(Node *expr, List *sortList, List *targetList); /* - * makeRangeTable - - * Build the initial range table from the FROM clause. + * transformFromClause - + * Process the FROM clause and add items to the query's range table, + * joinlist, and namespace. * - * The range table constructed here may grow as we transform the expressions - * in the query's quals and target list. (Note that this happens because in - * POSTQUEL, we allow references to relations not specified in the - * from-clause. PostgreSQL keeps this extension to standard SQL.) + * Note: we assume that pstate's p_rtable, p_joinlist, and p_namespace lists + * were initialized to NIL when the pstate was created. We will add onto + * any entries already present --- this is needed for rule processing, as + * well as for UPDATE and DELETE. * - * Note: we assume that pstate's p_rtable and p_joinlist lists were - * initialized to NIL when the pstate was created. We will add onto - * any entries already present --- this is needed for rule processing! + * The range table may grow still further when we transform the expressions + * in the query's quals and target list. (This is possible because in + * POSTQUEL, we allowed references to relations not specified in the + * from-clause. PostgreSQL keeps this extension to standard SQL.) */ void -makeRangeTable(ParseState *pstate, List *frmList) +transformFromClause(ParseState *pstate, List *frmList) { List *fl; /* * The grammar will have produced a list of RangeVars, RangeSubselects, - * and/or JoinExprs. Transform each one, and then add it to the joinlist. + * and/or JoinExprs. Transform each one (possibly adding entries to the + * rtable), check for duplicate refnames, and then add it to the joinlist + * and namespace. */ foreach(fl, frmList) { @@ -85,27 +89,41 @@ makeRangeTable(ParseState *pstate, List *frmList) List *containedRels; n = transformFromClauseItem(pstate, n, &containedRels); + checkNameSpaceConflicts(pstate, (Node *) pstate->p_namespace, n); pstate->p_joinlist = lappend(pstate->p_joinlist, n); + pstate->p_namespace = lappend(pstate->p_namespace, n); } } /* - * lockTargetTable - * Find the target relation of INSERT/UPDATE/DELETE and acquire write - * lock on it. This must be done before building the range table, - * in case the target is also mentioned as a source relation --- we - * want to be sure to grab the write lock before any read lock. + * setTargetTable + * Add the target relation of INSERT/UPDATE/DELETE to the range table, + * and make the special links to it in the ParseState. + * + * We also open the target relation and acquire a write lock on it. + * This must be done before processing the FROM list, in case the target + * is also mentioned as a source relation --- we want to be sure to grab + * the write lock before any read lock. * - * The ParseState's link to the target relcache entry is also set here. + * If alsoSource is true, add the target to the query's joinlist and + * namespace. For INSERT, we don't want the target to be joined to; + * it's a destination of tuples, not a source. For UPDATE/DELETE, + * we do need to scan or join the target. (NOTE: we do not bother + * to check for namespace conflict; we assume that the namespace was + * initially empty in these cases.) + * + * Returns the rangetable index of the target relation. */ -void -lockTargetTable(ParseState *pstate, char *relname) +int +setTargetTable(ParseState *pstate, char *relname, + bool inh, bool alsoSource) { + RangeTblEntry *rte; + int rtindex; + /* Close old target; this could only happen for multi-action rules */ if (pstate->p_target_relation != NULL) heap_close(pstate->p_target_relation, NoLock); - pstate->p_target_relation = NULL; - pstate->p_target_rangetblentry = NULL; /* setTargetTable will set this */ /* * Open target rel and grab suitable lock (which we will hold till @@ -115,62 +133,36 @@ lockTargetTable(ParseState *pstate, char *relname) * but *not* release the lock. */ pstate->p_target_relation = heap_openr(relname, RowExclusiveLock); -} -/* - * setTargetTable - * Add the target relation of INSERT/UPDATE/DELETE to the range table, - * and make the special links to it in the ParseState. - * - * inJoinSet says whether to add the target to the join list. - * For INSERT, we don't want the target to be joined to; it's a - * destination of tuples, not a source. For UPDATE/DELETE, we do - * need to scan or join the target. - */ -void -setTargetTable(ParseState *pstate, char *relname, bool inh, bool inJoinSet) -{ - RangeTblEntry *rte; + /* + * Now build an RTE. + */ + rte = addRangeTableEntry(pstate, relname, NULL, inh, false); + pstate->p_target_rangetblentry = rte; - /* look for relname only at current nesting level... */ - if (refnameRangeTablePosn(pstate, relname, NULL) == 0) - { - rte = addRangeTableEntry(pstate, relname, NULL, inh, false); - /* - * Since the rel wasn't in the rangetable already, it's not being - * read; override addRangeTableEntry's default checkForRead. - * - * If we find an explicit reference to the rel later during - * parse analysis, scanRTEForColumn will change checkForRead - * to 'true' again. That can't happen for INSERT but it is - * possible for UPDATE and DELETE. - */ - rte->checkForRead = false; - } - else - { - rte = refnameRangeTableEntry(pstate, relname); - /* - * Since the rel was in the rangetable already, it's being read - * as well as written. Therefore, leave checkForRead true. - * - * Force inh to the desired setting for the target (XXX is this - * reasonable? It's *necessary* that INSERT target not be marked - * inheritable, but otherwise not too clear what to do if conflict?) - */ - rte->inh = inh; - } + /* assume new rte is at end */ + rtindex = length(pstate->p_rtable); + Assert(rte == rt_fetch(rtindex, pstate->p_rtable)); - /* Mark target table as requiring write access. */ + /* + * Override addRangeTableEntry's default checkForRead, and instead + * mark target table as requiring write access. + * + * If we find an explicit reference to the rel later during + * parse analysis, scanRTEForColumn will change checkForRead + * to 'true' again. That can't happen for INSERT but it is + * possible for UPDATE and DELETE. + */ + rte->checkForRead = false; rte->checkForWrite = true; - if (inJoinSet) - addRTEtoJoinList(pstate, rte); - - /* lockTargetTable should have been called earlier */ - Assert(pstate->p_target_relation != NULL); + /* + * If UPDATE/DELETE, add table to joinlist and namespace. + */ + if (alsoSource) + addRTEtoQuery(pstate, rte, true, true); - pstate->p_target_rangetblentry = rte; + return rtindex; } /* @@ -313,22 +305,21 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j, List *containedRels) { Node *result; - List *sv_joinlist; + List *save_namespace; List *clause_varnos, *l; /* - * This is a tad tricky, for two reasons. First, at the point where - * we're called, the two subtrees of the JOIN node aren't yet part of - * the pstate's joinlist, which means that transformExpr() won't resolve - * unqualified references to their columns correctly. We fix this in a - * slightly klugy way: temporarily make the pstate's joinlist consist of - * just those two subtrees (which creates exactly the namespace the ON - * clause should see). This is OK only because the ON clause can't - * legally alter the joinlist by causing relation refs to be added. + * This is a tad tricky, for two reasons. First, the namespace that + * the join expression should see is just the two subtrees of the JOIN + * plus any outer references from upper pstate levels. So, temporarily + * set this pstate's namespace accordingly. (We need not check for + * refname conflicts, because transformFromClauseItem() already did.) + * NOTE: this code is OK only because the ON clause can't legally alter + * the namespace by causing implicit relation refs to be added. */ - sv_joinlist = pstate->p_joinlist; - pstate->p_joinlist = makeList2(j->larg, j->rarg); + save_namespace = pstate->p_namespace; + pstate->p_namespace = makeList2(j->larg, j->rarg); /* This part is just like transformWhereClause() */ result = transformExpr(pstate, j->quals, EXPR_COLUMN_FIRST); @@ -338,14 +329,14 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j, typeidTypeName(exprType(result))); } - pstate->p_joinlist = sv_joinlist; + pstate->p_namespace = save_namespace; /* * Second, we need to check that the ON condition doesn't refer to any * rels outside the input subtrees of the JOIN. It could do that despite - * our hack on the joinlist if it uses fully-qualified names. So, grovel + * our hack on the namespace if it uses fully-qualified names. So, grovel * through the transformed clause and make sure there are no bogus - * references. + * references. (Outer references are OK, and are ignored here.) */ clause_varnos = pull_varnos(result); foreach(l, clause_varnos) @@ -384,8 +375,8 @@ transformTableEntry(ParseState *pstate, RangeVar *r) interpretInhOption(r->inhOpt), true); /* - * We create a RangeTblRef, but we do not add it to the joinlist here. - * makeRangeTable will do so, if we are at top level of the FROM clause. + * We create a RangeTblRef, but we do not add it to the joinlist or + * namespace; our caller must do that if appropriate. */ rtr = makeNode(RangeTblRef); /* assume new rte is at end */ @@ -402,8 +393,7 @@ transformTableEntry(ParseState *pstate, RangeVar *r) static RangeTblRef * transformRangeSubselect(ParseState *pstate, RangeSubselect *r) { - List *save_rtable; - List *save_joinlist; + List *save_namespace; List *parsetrees; Query *query; RangeTblEntry *rte; @@ -424,15 +414,12 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r) * does not include other FROM items). But it does need to be able to * see any further-up parent states, so we can't just pass a null parent * pstate link. So, temporarily make the current query level have an - * empty rtable and joinlist. + * empty namespace. */ - save_rtable = pstate->p_rtable; - save_joinlist = pstate->p_joinlist; - pstate->p_rtable = NIL; - pstate->p_joinlist = NIL; + save_namespace = pstate->p_namespace; + pstate->p_namespace = NIL; parsetrees = parse_analyze(r->subquery, pstate); - pstate->p_rtable = save_rtable; - pstate->p_joinlist = save_joinlist; + pstate->p_namespace = save_namespace; /* * Check that we got something reasonable. Some of these conditions @@ -456,8 +443,8 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r) rte = addRangeTableEntryForSubquery(pstate, query, r->name, true); /* - * We create a RangeTblRef, but we do not add it to the joinlist here. - * makeRangeTable will do so, if we are at top level of the FROM clause. + * We create a RangeTblRef, but we do not add it to the joinlist or + * namespace; our caller must do that if appropriate. */ rtr = makeNode(RangeTblRef); /* assume new rte is at end */ @@ -472,7 +459,7 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r) * transformFromClauseItem - * Transform a FROM-clause item, adding any required entries to the * range table list being built in the ParseState, and return the - * transformed item ready to include in the joinlist. + * transformed item ready to include in the joinlist and namespace. * This routine can recurse to handle SQL92 JOIN expressions. * * Aside from the primary return value (the transformed joinlist item) @@ -526,6 +513,13 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels) *containedRels = nconc(l_containedRels, r_containedRels); /* + * Check for conflicting refnames in left and right subtrees. Must + * do this because higher levels will assume I hand back a self- + * consistent namespace subtree. + */ + checkNameSpaceConflicts(pstate, j->larg, j->rarg); + + /* * Extract column name and var lists from both subtrees */ if (IsA(j->larg, JoinExpr)) @@ -733,23 +727,9 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels) /* * Process alias (AS clause), if any. - * - * The given table alias must be unique in the current nesting level, - * ie it cannot match any RTE refname or jointable alias. This is - * a bit painful to check because my own child joins are not yet in - * the pstate's joinlist, so they have to be scanned separately. */ if (j->alias) { - /* Check against previously created RTEs and joinlist entries */ - if (refnameRangeOrJoinEntry(pstate, j->alias->relname, NULL)) - elog(ERROR, "Table name \"%s\" specified more than once", - j->alias->relname); - /* Check children */ - if (scanJoinListForRefname(j->larg, j->alias->relname) || - scanJoinListForRefname(j->rarg, j->alias->relname)) - elog(ERROR, "Table name \"%s\" specified more than once", - j->alias->relname); /* * If a column alias list is specified, substitute the alias * names into my output-column list diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index d39f06006aa..93a986d835e 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 cb4914849b4..e0d2b515526 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 afe8ae1b801..36e43166aa9 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 1b5d0afc719..d9280529c4f 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -8,14 +8,14 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.51 2001/01/24 19:43:02 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.52 2001/02/14 21:35:04 tgl Exp $ * *------------------------------------------------------------------------- */ -#include <ctype.h> - #include "postgres.h" +#include <ctype.h> + #include "access/heapam.h" #include "access/htup.h" #include "catalog/pg_type.h" @@ -30,6 +30,8 @@ #include "utils/lsyscache.h" +static Node *scanNameSpaceForRefname(ParseState *pstate, Node *nsnode, + char *refname); static Node *scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname); static Node *scanJoinForColumn(JoinExpr *join, char *colname, @@ -93,25 +95,13 @@ refnameRangeOrJoinEntry(ParseState *pstate, while (pstate != NULL) { - List *temp; - JoinExpr *join; + Node *rte; - /* - * Check the rangetable for RTEs; if no match, recursively scan - * the joinlist for join tables. We assume that no duplicate - * entries have been made in any one nesting level. - */ - foreach(temp, pstate->p_rtable) - { - RangeTblEntry *rte = lfirst(temp); - - if (strcmp(rte->eref->relname, refname) == 0) - return (Node *) rte; - } - - join = scanJoinListForRefname((Node *) pstate->p_joinlist, refname); - if (join) - return (Node *) join; + rte = scanNameSpaceForRefname(pstate, + (Node *) pstate->p_namespace, + refname); + if (rte) + return rte; pstate = pstate->parentParseState; if (sublevels_up) @@ -123,108 +113,129 @@ refnameRangeOrJoinEntry(ParseState *pstate, } /* - * Recursively search a joinlist for a joinexpr with given refname + * Recursively search a namespace for an RTE or joinexpr with given refname. + * + * The top level of p_namespace is a list, and we recurse into any joins + * that are not subqueries. It is also possible to pass an individual + * join subtree (useful when checking for name conflicts within a scope). * - * Note that during parse analysis, we don't expect to find a FromExpr node - * in p_joinlist; its top level is just a bare List. + * Note: we do not worry about the possibility of multiple matches; + * we assume the code that built the namespace checked for duplicates. */ -JoinExpr * -scanJoinListForRefname(Node *jtnode, char *refname) +static Node * +scanNameSpaceForRefname(ParseState *pstate, Node *nsnode, + char *refname) { - JoinExpr *result = NULL; + Node *result = NULL; - if (jtnode == NULL) + if (nsnode == NULL) return NULL; - if (IsA(jtnode, List)) + if (IsA(nsnode, RangeTblRef)) { - List *l; + int varno = ((RangeTblRef *) nsnode)->rtindex; + RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable); - foreach(l, (List *) jtnode) - { - result = scanJoinListForRefname(lfirst(l), refname); - if (result) - break; - } + if (strcmp(rte->eref->relname, refname) == 0) + result = (Node *) rte; } - else if (IsA(jtnode, RangeTblRef)) + else if (IsA(nsnode, JoinExpr)) { - /* ignore ... */ + JoinExpr *j = (JoinExpr *) nsnode; + + if (j->alias) + { + if (strcmp(j->alias->relname, refname) == 0) + return (Node *) j; /* matched a join alias */ + /* + * Tables within an aliased join are invisible from outside + * the join, according to the scope rules of SQL92 (the join + * is considered a subquery). So, stop here. + */ + return NULL; + } + result = scanNameSpaceForRefname(pstate, j->larg, refname); + if (! result) + result = scanNameSpaceForRefname(pstate, j->rarg, refname); } - else if (IsA(jtnode, JoinExpr)) + else if (IsA(nsnode, List)) { - JoinExpr *j = (JoinExpr *) jtnode; + List *l; - if (j->alias && strcmp(j->alias->relname, refname) == 0) - return j; - result = scanJoinListForRefname(j->larg, refname); - if (! result) - result = scanJoinListForRefname(j->rarg, refname); + foreach(l, (List *) nsnode) + { + result = scanNameSpaceForRefname(pstate, lfirst(l), refname); + if (result) + break; + } } else - elog(ERROR, "scanJoinListForRefname: unexpected node type %d", - nodeTag(jtnode)); + elog(ERROR, "scanNameSpaceForRefname: unexpected node type %d", + nodeTag(nsnode)); return result; } -/* - * given refname, return a pointer to the range table entry. - * - * NOTE that this routine will ONLY find RTEs, not join tables. - */ -RangeTblEntry * -refnameRangeTableEntry(ParseState *pstate, char *refname) +/* Convenience subroutine for checkNameSpaceConflicts */ +static void +scanNameSpaceForConflict(ParseState *pstate, Node *nsnode, + char *refname) { - List *temp; - - while (pstate != NULL) - { - foreach(temp, pstate->p_rtable) - { - RangeTblEntry *rte = lfirst(temp); - - if (strcmp(rte->eref->relname, refname) == 0) - return rte; - } - pstate = pstate->parentParseState; - } - return NULL; + if (scanNameSpaceForRefname(pstate, nsnode, refname) != NULL) + elog(ERROR, "Table name \"%s\" specified more than once", refname); } /* - * given refname, return RT index (starting with 1) of the relation, - * and optionally get its nesting depth (0 = current). If sublevels_up - * is NULL, only consider rels at the current nesting level. - * A zero result means name not found. + * Recursively check for refname conflicts between two namespaces or + * namespace subtrees. Raise an error if any is found. + * + * Works by recursively scanning namespace1 in the same way that + * scanNameSpaceForRefname does, and then looking in namespace2 for + * a match to each refname found in namespace1. * - * NOTE that this routine will ONLY find RTEs, not join tables. + * Note: we assume that each given argument does not contain conflicts + * itself; we just want to know if the two can be merged together. */ -int -refnameRangeTablePosn(ParseState *pstate, char *refname, int *sublevels_up) +void +checkNameSpaceConflicts(ParseState *pstate, Node *namespace1, + Node *namespace2) { - int index; - List *temp; - - if (sublevels_up) - *sublevels_up = 0; + if (namespace1 == NULL) + return; + if (IsA(namespace1, RangeTblRef)) + { + int varno = ((RangeTblRef *) namespace1)->rtindex; + RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable); - while (pstate != NULL) + scanNameSpaceForConflict(pstate, namespace2, rte->eref->relname); + } + else if (IsA(namespace1, JoinExpr)) { - index = 1; - foreach(temp, pstate->p_rtable) + JoinExpr *j = (JoinExpr *) namespace1; + + if (j->alias) { - RangeTblEntry *rte = lfirst(temp); + scanNameSpaceForConflict(pstate, namespace2, j->alias->relname); + /* + * Tables within an aliased join are invisible from outside + * the join, according to the scope rules of SQL92 (the join + * is considered a subquery). So, stop here. + */ + return; + } + checkNameSpaceConflicts(pstate, j->larg, namespace2); + checkNameSpaceConflicts(pstate, j->rarg, namespace2); + } + else if (IsA(namespace1, List)) + { + List *l; - if (strcmp(rte->eref->relname, refname) == 0) - return index; - index++; + foreach(l, (List *) namespace1) + { + checkNameSpaceConflicts(pstate, lfirst(l), namespace2); } - pstate = pstate->parentParseState; - if (sublevels_up) - (*sublevels_up)++; - else - break; } - return 0; + else + elog(ERROR, "checkNameSpaceConflicts: unexpected node type %d", + nodeTag(namespace1)); } /* @@ -257,6 +268,7 @@ RTERangeTablePosn(ParseState *pstate, RangeTblEntry *rte, int *sublevels_up) else break; } + elog(ERROR, "RTERangeTablePosn: RTE not found (internal error)"); return 0; /* keep compiler quiet */ } @@ -369,21 +381,21 @@ colnameToVar(ParseState *pstate, char *colname) while (pstate != NULL) { - List *jt; + List *ns; /* - * We want to look only at top-level jointree items, and even for + * We need to look only at top-level namespace items, and even for * those, ignore RTEs that are marked as not inFromCl and not * the query's target relation. */ - foreach(jt, pstate->p_joinlist) + foreach(ns, pstate->p_namespace) { - Node *jtnode = (Node *) lfirst(jt); + Node *nsnode = (Node *) lfirst(ns); Node *newresult = NULL; - if (IsA(jtnode, RangeTblRef)) + if (IsA(nsnode, RangeTblRef)) { - int varno = ((RangeTblRef *) jtnode)->rtindex; + int varno = ((RangeTblRef *) nsnode)->rtindex; RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable); if (! rte->inFromCl && @@ -393,15 +405,15 @@ colnameToVar(ParseState *pstate, char *colname) /* use orig_pstate here to get the right sublevels_up */ newresult = scanRTEForColumn(orig_pstate, rte, colname); } - else if (IsA(jtnode, JoinExpr)) + else if (IsA(nsnode, JoinExpr)) { - JoinExpr *j = (JoinExpr *) jtnode; + JoinExpr *j = (JoinExpr *) nsnode; newresult = scanJoinForColumn(j, colname, levels_up); } else elog(ERROR, "colnameToVar: unexpected node type %d", - nodeTag(jtnode)); + nodeTag(nsnode)); if (newresult) { @@ -451,7 +463,7 @@ qualifiedNameToVar(ParseState *pstate, char *refname, char *colname, colname); else if (IsA(rteorjoin, JoinExpr)) result = scanJoinForColumn((JoinExpr *) rteorjoin, - colname, sublevels_up); + colname, sublevels_up); else { elog(ERROR, "qualifiedNameToVar: unexpected node type %d", @@ -465,10 +477,11 @@ qualifiedNameToVar(ParseState *pstate, char *refname, char *colname, /* * Add an entry for a relation to the pstate's range table (p_rtable). * - * If the specified refname is already present, raise error. + * If pstate is NULL, we just build an RTE and return it without adding it + * to an rtable list. * - * If pstate is NULL, we just build an RTE and return it without worrying - * about membership in an rtable list. + * Note: formerly this checked for refname conflicts, but that's wrong. + * Caller is responsible for checking for conflicts in the appropriate scope. */ RangeTblEntry * addRangeTableEntry(ParseState *pstate, @@ -477,27 +490,15 @@ addRangeTableEntry(ParseState *pstate, bool inh, bool inFromCl) { + RangeTblEntry *rte = makeNode(RangeTblEntry); char *refname = alias ? alias->relname : relname; LOCKMODE lockmode; Relation rel; - RangeTblEntry *rte; Attr *eref; int maxattrs; int numaliases; int varattno; - /* Check for conflicting RTE or jointable alias (at level 0 only) */ - if (pstate != NULL) - { - Node *rteorjoin = refnameRangeOrJoinEntry(pstate, refname, NULL); - - if (rteorjoin) - elog(ERROR, "Table name \"%s\" specified more than once", - refname); - } - - rte = makeNode(RangeTblEntry); - rte->relname = relname; rte->alias = alias; rte->subquery = NULL; @@ -559,7 +560,8 @@ addRangeTableEntry(ParseState *pstate, rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ /* - * Add completed RTE to range table list. + * Add completed RTE to pstate's range table list, but not to join list + * nor namespace --- caller must do that if appropriate. */ if (pstate != NULL) pstate->p_rtable = lappend(pstate->p_rtable, rte); @@ -579,25 +581,13 @@ addRangeTableEntryForSubquery(ParseState *pstate, Attr *alias, bool inFromCl) { + RangeTblEntry *rte = makeNode(RangeTblEntry); char *refname = alias->relname; - RangeTblEntry *rte; Attr *eref; int numaliases; int varattno; List *tlistitem; - /* Check for conflicting RTE or jointable alias (at level 0 only) */ - if (pstate != NULL) - { - Node *rteorjoin = refnameRangeOrJoinEntry(pstate, refname, NULL); - - if (rteorjoin) - elog(ERROR, "Table name \"%s\" specified more than once", - refname); - } - - rte = makeNode(RangeTblEntry); - rte->relname = NULL; rte->relid = InvalidOid; rte->subquery = subquery; @@ -647,7 +637,8 @@ addRangeTableEntryForSubquery(ParseState *pstate, rte->checkAsUser = InvalidOid; /* - * Add completed RTE to range table list. + * Add completed RTE to pstate's range table list, but not to join list + * nor namespace --- caller must do that if appropriate. */ if (pstate != NULL) pstate->p_rtable = lappend(pstate->p_rtable, rte); @@ -691,37 +682,30 @@ isForUpdate(ParseState *pstate, char *relname) } /* - * Add the given RTE as a top-level entry in the pstate's join list, - * unless there already is an entry for it. + * Add the given RTE as a top-level entry in the pstate's join list + * and/or name space list. (We assume caller has checked for any + * namespace conflict.) */ void -addRTEtoJoinList(ParseState *pstate, RangeTblEntry *rte) +addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte, + bool addToJoinList, bool addToNameSpace) { int rtindex = RTERangeTablePosn(pstate, rte, NULL); - List *jt; - RangeTblRef *rtr; - - foreach(jt, pstate->p_joinlist) - { - Node *n = (Node *) lfirst(jt); - - if (IsA(n, RangeTblRef)) - { - if (rtindex == ((RangeTblRef *) n)->rtindex) - return; /* it's already being joined to */ - } - } + RangeTblRef *rtr = makeNode(RangeTblRef); - /* Not present, so add it */ - rtr = makeNode(RangeTblRef); rtr->rtindex = rtindex; - pstate->p_joinlist = lappend(pstate->p_joinlist, rtr); + + if (addToJoinList) + pstate->p_joinlist = lappend(pstate->p_joinlist, rtr); + if (addToNameSpace) + pstate->p_namespace = lappend(pstate->p_namespace, rtr); } /* * Add a POSTQUEL-style implicit RTE. * - * We assume caller has already checked that there is no such RTE now. + * We assume caller has already checked that there is no RTE or join with + * a conflicting name. */ RangeTblEntry * addImplicitRTE(ParseState *pstate, char *relname) @@ -729,7 +713,7 @@ addImplicitRTE(ParseState *pstate, char *relname) RangeTblEntry *rte; rte = addRangeTableEntry(pstate, relname, NULL, false, false); - addRTEtoJoinList(pstate, rte); + addRTEtoQuery(pstate, rte, true, true); warnAutoRange(pstate, relname); return rte; @@ -922,6 +906,11 @@ expandNamesVars(ParseState *pstate, List *names, List *vars) * This is unlike get_attname() because we use aliases if available. * In particular, it will work on an RTE for a subselect, whereas * get_attname() only works on real relations. + * + * XXX Actually, this is completely bogus, because refnames of RTEs are + * not guaranteed unique, and may not even have scope across the whole + * query. Cleanest fix would be to add refname/attname to Var nodes and + * just print those, rather than indulging in this hack. * ---------- */ char * @@ -1088,4 +1077,3 @@ warnAutoRange(ParseState *pstate, char *refname) pstate->parentParseState != NULL ? " in subquery" : "", refname); } - diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 647b176f059..6b566da7475 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. + */ + arrayBase = NULL; /* signal there is no source array */ + } + else + { + /* + * Build a Var for the array to be updated. */ - aref->refexpr = NULL; /* signal there is no source array */ + 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; } |