Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 399437a

Browse files
committed
Improve error messages for missing-FROM-entry cases, as per recent discussion.
1 parent 8ea91ba commit 399437a

File tree

2 files changed

+98
-18
lines changed

2 files changed

+98
-18
lines changed

src/backend/parser/parse_relation.c

+97-17
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.117 2005/11/22 18:17:16 momjian Exp $
11+
* $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.118 2006/01/10 21:59:59 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -161,7 +161,7 @@ scanNameSpaceForRelid(ParseState *pstate, Oid relid)
161161
{
162162
RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
163163

164-
/* yes, the test for alias==NULL should be there... */
164+
/* yes, the test for alias == NULL should be there... */
165165
if (rte->rtekind == RTE_RELATION &&
166166
rte->relid == relid &&
167167
rte->alias == NULL)
@@ -177,6 +177,48 @@ scanNameSpaceForRelid(ParseState *pstate, Oid relid)
177177
return result;
178178
}
179179

180+
/*
181+
* searchRangeTable
182+
* See if any RangeTblEntry could possibly match the RangeVar.
183+
* If so, return a pointer to the RangeTblEntry; else return NULL.
184+
*
185+
* This is different from refnameRangeTblEntry in that it considers every
186+
* entry in the ParseState's rangetable(s), not only those that are currently
187+
* visible in the p_relnamespace lists. This behavior is invalid per the SQL
188+
* spec, and it may give ambiguous results (there might be multiple equally
189+
* valid matches, but only one will be returned). This must be used ONLY
190+
* as a heuristic in giving suitable error messages. See warnAutoRange.
191+
*
192+
* Notice that we consider both matches on actual relation name and matches
193+
* on alias.
194+
*/
195+
static RangeTblEntry *
196+
searchRangeTable(ParseState *pstate, RangeVar *relation)
197+
{
198+
Oid relId = RangeVarGetRelid(relation, true);
199+
char *refname = relation->relname;
200+
201+
while (pstate != NULL)
202+
{
203+
ListCell *l;
204+
205+
foreach(l, pstate->p_rtable)
206+
{
207+
RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
208+
209+
if (OidIsValid(relId) &&
210+
rte->rtekind == RTE_RELATION &&
211+
rte->relid == relId)
212+
return rte;
213+
if (strcmp(rte->eref->aliasname, refname) == 0)
214+
return rte;
215+
}
216+
217+
pstate = pstate->parentParseState;
218+
}
219+
return NULL;
220+
}
221+
180222
/*
181223
* Check for relation-name conflicts between two relnamespace lists.
182224
* Raise an error if any is found.
@@ -1005,6 +1047,8 @@ addImplicitRTE(ParseState *pstate, RangeVar *relation)
10051047
{
10061048
RangeTblEntry *rte;
10071049

1050+
/* issue warning or error as needed */
1051+
warnAutoRange(pstate, relation);
10081052
/*
10091053
* Note that we set inFromCl true, so that the RTE will be listed
10101054
* explicitly if the parsetree is ever decompiled by ruleutils.c. This
@@ -1014,7 +1058,6 @@ addImplicitRTE(ParseState *pstate, RangeVar *relation)
10141058
rte = addRangeTableEntry(pstate, relation, NULL, false, true);
10151059
/* Add to joinlist and relnamespace, but not varnamespace */
10161060
addRTEtoQuery(pstate, rte, true, true, false);
1017-
warnAutoRange(pstate, relation);
10181061

10191062
return rte;
10201063
}
@@ -1761,31 +1804,68 @@ attnumTypeId(Relation rd, int attid)
17611804
static void
17621805
warnAutoRange(ParseState *pstate, RangeVar *relation)
17631806
{
1807+
RangeTblEntry *rte;
1808+
int sublevels_up;
1809+
const char *badAlias = NULL;
1810+
1811+
/*
1812+
* Check to see if there are any potential matches in the query's
1813+
* rangetable. This affects the message we provide.
1814+
*/
1815+
rte = searchRangeTable(pstate, relation);
1816+
1817+
/*
1818+
* If we found a match that has an alias and the alias is visible in
1819+
* the namespace, then the problem is probably use of the relation's
1820+
* real name instead of its alias, ie "SELECT foo.* FROM foo f".
1821+
* This mistake is common enough to justify a specific hint.
1822+
*
1823+
* If we found a match that doesn't meet those criteria, assume the
1824+
* problem is illegal use of a relation outside its scope, as in the
1825+
* MySQL-ism "SELECT ... FROM a, b LEFT JOIN c ON (a.x = c.y)".
1826+
*/
1827+
if (rte && rte->alias &&
1828+
strcmp(rte->eref->aliasname, relation->relname) != 0 &&
1829+
refnameRangeTblEntry(pstate, NULL, rte->eref->aliasname,
1830+
&sublevels_up) == rte)
1831+
badAlias = rte->eref->aliasname;
1832+
17641833
if (!add_missing_from)
17651834
{
1766-
if (pstate->parentParseState != NULL)
1835+
if (rte)
17671836
ereport(ERROR,
17681837
(errcode(ERRCODE_UNDEFINED_TABLE),
1769-
errmsg("missing FROM-clause entry in subquery for table \"%s\"",
1770-
relation->relname)));
1838+
errmsg("invalid reference to FROM-clause entry for table \"%s\"",
1839+
relation->relname),
1840+
(badAlias ?
1841+
errhint("Perhaps you meant to reference the table alias \"%s\".",
1842+
badAlias) :
1843+
errhint("There is an entry for table \"%s\", but it cannot be referenced from this part of the query.",
1844+
rte->eref->aliasname))));
17711845
else
17721846
ereport(ERROR,
17731847
(errcode(ERRCODE_UNDEFINED_TABLE),
1774-
errmsg("missing FROM-clause entry for table \"%s\"",
1775-
relation->relname)));
1848+
(pstate->parentParseState ?
1849+
errmsg("missing FROM-clause entry in subquery for table \"%s\"",
1850+
relation->relname) :
1851+
errmsg("missing FROM-clause entry for table \"%s\"",
1852+
relation->relname))));
17761853
}
17771854
else
17781855
{
17791856
/* just issue a warning */
1780-
if (pstate->parentParseState != NULL)
1781-
ereport(NOTICE,
1782-
(errcode(ERRCODE_UNDEFINED_TABLE),
1783-
errmsg("adding missing FROM-clause entry in subquery for table \"%s\"",
1784-
relation->relname)));
1785-
else
1786-
ereport(NOTICE,
1787-
(errcode(ERRCODE_UNDEFINED_TABLE),
1857+
ereport(NOTICE,
1858+
(errcode(ERRCODE_UNDEFINED_TABLE),
1859+
(pstate->parentParseState ?
1860+
errmsg("adding missing FROM-clause entry in subquery for table \"%s\"",
1861+
relation->relname) :
17881862
errmsg("adding missing FROM-clause entry for table \"%s\"",
1789-
relation->relname)));
1863+
relation->relname)),
1864+
(badAlias ?
1865+
errhint("Perhaps you meant to reference the table alias \"%s\".",
1866+
badAlias) :
1867+
(rte ?
1868+
errhint("There is an entry for table \"%s\", but it cannot be referenced from this part of the query.",
1869+
rte->eref->aliasname) : 0))));
17901870
}
17911871
}

src/test/regress/expected/rowtypes.out

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ select * from quadtable;
6060
(2 rows)
6161

6262
select f1, q.c1 from quadtable; -- fails, q is a table reference
63-
ERROR: relation "q" does not exist
63+
ERROR: missing FROM-clause entry for table "q"
6464
select f1, (q).c1, (qq.q).c1.i from quadtable qq;
6565
f1 | c1 | i
6666
----+-----------+-----

0 commit comments

Comments
 (0)