*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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 */
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
return result;
}
+/*
+ * 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.
* 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;
{
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;
}
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);
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) :
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
/* 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) :