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{
162162RangeTblEntry * 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... */
165165if (rte -> rtekind == RTE_RELATION &&
166166rte -> relid == relid &&
167167rte -> alias == NULL )
@@ -177,6 +177,48 @@ scanNameSpaceForRelid(ParseState *pstate, Oid relid)
177177return 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{
10061048RangeTblEntry * 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)
10141058rte = addRangeTableEntry (pstate ,relation ,NULL , false, true);
10151059/* Add to joinlist and relnamespace, but not varnamespace */
10161060addRTEtoQuery (pstate ,rte , true, true, false);
1017- warnAutoRange (pstate ,relation );
10181061
10191062return rte ;
10201063}
@@ -1761,31 +1804,68 @@ attnumTypeId(Relation rd, int attid)
17611804static void
17621805warnAutoRange (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+
17641833if (!add_missing_from )
17651834{
1766- if (pstate -> parentParseState != NULL )
1835+ if (rte )
17671836ereport (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 ))));
17711845else
17721846ereport (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}
17771854else
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 ) :
17881862errmsg ("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}