diff options
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; } |