8
8
*
9
9
*
10
10
* 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 $
12
12
*
13
13
*-------------------------------------------------------------------------
14
14
*/
@@ -161,7 +161,7 @@ scanNameSpaceForRelid(ParseState *pstate, Oid relid)
161
161
{
162
162
RangeTblEntry * rte = (RangeTblEntry * ) lfirst (l );
163
163
164
- /* yes, the test for alias== NULL should be there... */
164
+ /* yes, the test for alias == NULL should be there... */
165
165
if (rte -> rtekind == RTE_RELATION &&
166
166
rte -> relid == relid &&
167
167
rte -> alias == NULL )
@@ -177,6 +177,48 @@ scanNameSpaceForRelid(ParseState *pstate, Oid relid)
177
177
return result ;
178
178
}
179
179
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
+
180
222
/*
181
223
* Check for relation-name conflicts between two relnamespace lists.
182
224
* Raise an error if any is found.
@@ -1005,6 +1047,8 @@ addImplicitRTE(ParseState *pstate, RangeVar *relation)
1005
1047
{
1006
1048
RangeTblEntry * rte ;
1007
1049
1050
+ /* issue warning or error as needed */
1051
+ warnAutoRange (pstate , relation );
1008
1052
/*
1009
1053
* Note that we set inFromCl true, so that the RTE will be listed
1010
1054
* explicitly if the parsetree is ever decompiled by ruleutils.c. This
@@ -1014,7 +1058,6 @@ addImplicitRTE(ParseState *pstate, RangeVar *relation)
1014
1058
rte = addRangeTableEntry (pstate , relation , NULL , false, true);
1015
1059
/* Add to joinlist and relnamespace, but not varnamespace */
1016
1060
addRTEtoQuery (pstate , rte , true, true, false);
1017
- warnAutoRange (pstate , relation );
1018
1061
1019
1062
return rte ;
1020
1063
}
@@ -1761,31 +1804,68 @@ attnumTypeId(Relation rd, int attid)
1761
1804
static void
1762
1805
warnAutoRange (ParseState * pstate , RangeVar * relation )
1763
1806
{
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
+
1764
1833
if (!add_missing_from )
1765
1834
{
1766
- if (pstate -> parentParseState != NULL )
1835
+ if (rte )
1767
1836
ereport (ERROR ,
1768
1837
(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 ))));
1771
1845
else
1772
1846
ereport (ERROR ,
1773
1847
(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 ))));
1776
1853
}
1777
1854
else
1778
1855
{
1779
1856
/* 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 ) :
1788
1862
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 ))));
1790
1870
}
1791
1871
}
0 commit comments