aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/parser/parse_clause.c31
-rw-r--r--src/backend/parser/parse_relation.c100
-rw-r--r--src/include/parser/parse_relation.h5
3 files changed, 87 insertions, 49 deletions
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index dbf17759617..0e5fbfd28ac 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.180 2008/10/04 21:56:54 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.181 2008/10/06 02:12:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -631,34 +631,15 @@ transformFromClauseItem(ParseState *pstate, Node *n,
RangeTblEntry *rte = NULL;
int rtindex;
- /*
- * If it is an unqualified name, it might be a reference to some
- * CTE visible in this or a parent query.
- */
+ /* if it is an unqualified name, it might be a CTE reference */
if (!rv->schemaname)
{
- ParseState *ps;
+ CommonTableExpr *cte;
Index levelsup;
- for (ps = pstate, levelsup = 0;
- ps != NULL;
- ps = ps->parentParseState, levelsup++)
- {
- ListCell *lc;
-
- foreach(lc, ps->p_ctenamespace)
- {
- CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
-
- if (strcmp(rv->relname, cte->ctename) == 0)
- {
- rte = transformCTEReference(pstate, rv, cte, levelsup);
- break;
- }
- }
- if (rte)
- break;
- }
+ cte = scanNameSpaceForCTE(pstate, rv->relname, &levelsup);
+ if (cte)
+ rte = transformCTEReference(pstate, rv, cte, levelsup);
}
/* if not found as a CTE, must be a table reference */
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index fa2da35c6b7..870911c062a 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.136 2008/10/04 21:56:54 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.137 2008/10/06 02:12:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -183,6 +183,38 @@ scanNameSpaceForRelid(ParseState *pstate, Oid relid, int location)
}
/*
+ * Search the query's CTE namespace for a CTE matching the given unqualified
+ * refname. Return the CTE (and its levelsup count) if a match, or NULL
+ * if no match. We need not worry about multiple matches, since parse_cte.c
+ * rejects WITH lists containing duplicate CTE names.
+ */
+CommonTableExpr *
+scanNameSpaceForCTE(ParseState *pstate, const char *refname,
+ Index *ctelevelsup)
+{
+ Index levelsup;
+
+ for (levelsup = 0;
+ pstate != NULL;
+ pstate = pstate->parentParseState, levelsup++)
+ {
+ ListCell *lc;
+
+ foreach(lc, pstate->p_ctenamespace)
+ {
+ CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
+
+ if (strcmp(cte->ctename, refname) == 0)
+ {
+ *ctelevelsup = levelsup;
+ return cte;
+ }
+ }
+ }
+ return NULL;
+}
+
+/*
* searchRangeTable
* See if any RangeTblEntry could possibly match the RangeVar.
* If so, return a pointer to the RangeTblEntry; else return NULL.
@@ -194,16 +226,32 @@ scanNameSpaceForRelid(ParseState *pstate, Oid relid, int location)
* valid matches, but only one will be returned). This must be used ONLY
* as a heuristic in giving suitable error messages. See warnAutoRange.
*
- * Notice that we consider both matches on actual relation name and matches
- * on alias.
+ * Notice that we consider both matches on actual relation (or CTE) name
+ * and matches on alias.
*/
static RangeTblEntry *
searchRangeTable(ParseState *pstate, RangeVar *relation)
{
- Oid relId = RangeVarGetRelid(relation, true);
- char *refname = relation->relname;
+ const char *refname = relation->relname;
+ Oid relId = InvalidOid;
+ CommonTableExpr *cte = NULL;
+ Index ctelevelsup = 0;
+ Index levelsup;
- while (pstate != NULL)
+ /*
+ * If it's an unqualified name, check for possible CTE matches.
+ * A CTE hides any real relation matches. If no CTE, look for
+ * a matching relation.
+ */
+ if (!relation->schemaname)
+ cte = scanNameSpaceForCTE(pstate, refname, &ctelevelsup);
+ if (!cte)
+ relId = RangeVarGetRelid(relation, true);
+
+ /* Now look for RTEs matching either the relation/CTE or the alias */
+ for (levelsup = 0;
+ pstate != NULL;
+ pstate = pstate->parentParseState, levelsup++)
{
ListCell *l;
@@ -211,15 +259,18 @@ searchRangeTable(ParseState *pstate, RangeVar *relation)
{
RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
- if (OidIsValid(relId) &&
- rte->rtekind == RTE_RELATION &&
+ if (rte->rtekind == RTE_RELATION &&
+ OidIsValid(relId) &&
rte->relid == relId)
return rte;
+ if (rte->rtekind == RTE_CTE &&
+ cte != NULL &&
+ rte->ctelevelsup + levelsup == ctelevelsup &&
+ strcmp(rte->ctename, refname) == 0)
+ return rte;
if (strcmp(rte->eref->aliasname, refname) == 0)
return rte;
}
-
- pstate = pstate->parentParseState;
}
return NULL;
}
@@ -1293,18 +1344,27 @@ addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte,
RangeTblEntry *
addImplicitRTE(ParseState *pstate, RangeVar *relation)
{
+ CommonTableExpr *cte = NULL;
+ Index levelsup = 0;
RangeTblEntry *rte;
/* issue warning or error as needed */
warnAutoRange(pstate, relation);
+ /* if it is an unqualified name, it might be a CTE reference */
+ if (!relation->schemaname)
+ cte = scanNameSpaceForCTE(pstate, relation->relname, &levelsup);
+
/*
* Note that we set inFromCl true, so that the RTE will be listed
* explicitly if the parsetree is ever decompiled by ruleutils.c. This
* provides a migration path for views/rules that were originally written
* with implicit-RTE syntax.
*/
- rte = addRangeTableEntry(pstate, relation, NULL, false, true);
+ if (cte)
+ rte = addRangeTableEntryForCTE(pstate, cte, levelsup, NULL, true);
+ else
+ rte = addRangeTableEntry(pstate, relation, NULL, false, true);
/* Add to joinlist and relnamespace, but not varnamespace */
addRTEtoQuery(pstate, rte, true, true, false);
@@ -2194,8 +2254,8 @@ warnAutoRange(ParseState *pstate, RangeVar *relation)
if (rte)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_TABLE),
- errmsg("invalid reference to FROM-clause entry for table \"%s\"",
- relation->relname),
+ errmsg("invalid reference to FROM-clause entry for table \"%s\"",
+ relation->relname),
(badAlias ?
errhint("Perhaps you meant to reference the table alias \"%s\".",
badAlias) :
@@ -2205,11 +2265,8 @@ warnAutoRange(ParseState *pstate, RangeVar *relation)
else
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_TABLE),
- (pstate->parentParseState ?
- errmsg("missing FROM-clause entry in subquery for table \"%s\"",
- relation->relname) :
- errmsg("missing FROM-clause entry for table \"%s\"",
- relation->relname)),
+ errmsg("missing FROM-clause entry for table \"%s\"",
+ relation->relname),
parser_errposition(pstate, relation->location)));
}
else
@@ -2217,11 +2274,8 @@ warnAutoRange(ParseState *pstate, RangeVar *relation)
/* just issue a warning */
ereport(NOTICE,
(errcode(ERRCODE_UNDEFINED_TABLE),
- (pstate->parentParseState ?
- errmsg("adding missing FROM-clause entry in subquery for table \"%s\"",
- relation->relname) :
- errmsg("adding missing FROM-clause entry for table \"%s\"",
- relation->relname)),
+ errmsg("adding missing FROM-clause entry for table \"%s\"",
+ relation->relname),
(badAlias ?
errhint("Perhaps you meant to reference the table alias \"%s\".",
badAlias) :
diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h
index 9c81d4b50b5..f5212c421b7 100644
--- a/src/include/parser/parse_relation.h
+++ b/src/include/parser/parse_relation.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/parser/parse_relation.h,v 1.59 2008/10/04 21:56:55 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/parser/parse_relation.h,v 1.60 2008/10/06 02:12:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -23,6 +23,9 @@ extern RangeTblEntry *refnameRangeTblEntry(ParseState *pstate,
const char *refname,
int location,
int *sublevels_up);
+extern CommonTableExpr *scanNameSpaceForCTE(ParseState *pstate,
+ const char *refname,
+ Index *ctelevelsup);
extern void checkNameSpaceConflicts(ParseState *pstate, List *namespace1,
List *namespace2);
extern int RTERangeTablePosn(ParseState *pstate,