mirror of
https://github.com/postgres/postgres.git
synced 2025-06-16 06:01:02 +03:00
Modify the parser's error reporting to include a specific hint for the case
of referencing a WITH item that's not yet in scope according to the SQL spec's semantics. This seems to be an easy error to make, and the bare "relation doesn't exist" message doesn't lead one's mind in the correct direction to fix it.
This commit is contained in:
@ -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 $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -214,6 +214,29 @@ scanNameSpaceForCTE(ParseState *pstate, const char *refname,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user