summaryrefslogtreecommitdiff
path: root/src/backend/parser/parse_relation.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser/parse_relation.c')
-rw-r--r--src/backend/parser/parse_relation.c62
1 files changed, 58 insertions, 4 deletions
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 14ddd34b76a..fb4ad74edba 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.138 2008/10/06 15:15:22 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.139 2008/10/08 01:14:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -215,6 +215,29 @@ scanNameSpaceForCTE(ParseState *pstate, const char *refname,
}
/*
+ * Search for a possible "future CTE", that is one that is not yet in scope
+ * according to the WITH scoping rules. This has nothing to do with valid
+ * SQL semantics, but it's important for error reporting purposes.
+ */
+static bool
+isFutureCTE(ParseState *pstate, const char *refname)
+{
+ for (; pstate != NULL; pstate = pstate->parentParseState)
+ {
+ ListCell *lc;
+
+ foreach(lc, pstate->p_future_ctes)
+ {
+ CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
+
+ if (strcmp(cte->ctename, refname) == 0)
+ return true;
+ }
+ }
+ return false;
+}
+
+/*
* searchRangeTable
* See if any RangeTblEntry could possibly match the RangeVar.
* If so, return a pointer to the RangeTblEntry; else return NULL.
@@ -702,8 +725,9 @@ buildScalarFunctionAlias(Node *funcexpr, char *funcname,
/*
* Open a table during parse analysis
*
- * This is essentially just the same as heap_openrv(), except that it
- * arranges to include the RangeVar's parse location in any resulting error.
+ * This is essentially just the same as heap_openrv(), except that it caters
+ * to some parser-specific error reporting needs, notably that it arranges
+ * to include the RangeVar's parse location in any resulting error.
*
* Note: properly, lockmode should be declared LOCKMODE not int, but that
* would require importing storage/lock.h into parse_relation.h. Since
@@ -716,7 +740,37 @@ parserOpenTable(ParseState *pstate, const RangeVar *relation, int lockmode)
ParseCallbackState pcbstate;
setup_parser_errposition_callback(&pcbstate, pstate, relation->location);
- rel = heap_openrv(relation, lockmode);
+ rel = try_heap_openrv(relation, lockmode);
+ if (rel == NULL)
+ {
+ if (relation->schemaname)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_TABLE),
+ errmsg("relation \"%s.%s\" does not exist",
+ relation->schemaname, relation->relname)));
+ else
+ {
+ /*
+ * An unqualified name might have been meant as a reference to
+ * some not-yet-in-scope CTE. The bare "does not exist" message
+ * has proven remarkably unhelpful for figuring out such problems,
+ * so we take pains to offer a specific hint.
+ */
+ if (isFutureCTE(pstate, relation->relname))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_TABLE),
+ errmsg("relation \"%s\" does not exist",
+ relation->relname),
+ errdetail("There is a WITH item named \"%s\", but it cannot be referenced from this part of the query.",
+ relation->relname),
+ errhint("Use WITH RECURSIVE, or re-order the WITH items to remove forward references.")));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_TABLE),
+ errmsg("relation \"%s\" does not exist",
+ relation->relname)));
+ }
+ }
cancel_parser_errposition_callback(&pcbstate);
return rel;
}