88 *
99 *
1010 * IDENTIFICATION
11- * $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.136 2008/10/04 21:56:54 tgl Exp $
11+ * $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.137 2008/10/06 02:12:56 tgl Exp $
1212 *
1313 *-------------------------------------------------------------------------
1414 */
@@ -182,6 +182,38 @@ scanNameSpaceForRelid(ParseState *pstate, Oid relid, int location)
182182return result ;
183183}
184184
185+ /*
186+ * Search the query's CTE namespace for a CTE matching the given unqualified
187+ * refname. Return the CTE (and its levelsup count) if a match, or NULL
188+ * if no match. We need not worry about multiple matches, since parse_cte.c
189+ * rejects WITH lists containing duplicate CTE names.
190+ */
191+ CommonTableExpr *
192+ scanNameSpaceForCTE (ParseState * pstate ,const char * refname ,
193+ Index * ctelevelsup )
194+ {
195+ Index levelsup ;
196+
197+ for (levelsup = 0 ;
198+ pstate != NULL ;
199+ pstate = pstate -> parentParseState ,levelsup ++ )
200+ {
201+ ListCell * lc ;
202+
203+ foreach (lc ,pstate -> p_ctenamespace )
204+ {
205+ CommonTableExpr * cte = (CommonTableExpr * )lfirst (lc );
206+
207+ if (strcmp (cte -> ctename ,refname )== 0 )
208+ {
209+ * ctelevelsup = levelsup ;
210+ return cte ;
211+ }
212+ }
213+ }
214+ return NULL ;
215+ }
216+
185217/*
186218 * searchRangeTable
187219 * See if any RangeTblEntry could possibly match the RangeVar.
@@ -194,32 +226,51 @@ scanNameSpaceForRelid(ParseState *pstate, Oid relid, int location)
194226 * valid matches, but only one will be returned). This must be used ONLY
195227 * as a heuristic in giving suitable error messages. See warnAutoRange.
196228 *
197- * Notice that we consider both matches on actual relationname and matches
198- * on alias.
229+ * Notice that we consider both matches on actual relation(or CTE) name
230+ *and matches on alias.
199231 */
200232static RangeTblEntry *
201233searchRangeTable (ParseState * pstate ,RangeVar * relation )
202234{
203- Oid relId = RangeVarGetRelid (relation , true);
204- char * refname = relation -> relname ;
235+ const char * refname = relation -> relname ;
236+ Oid relId = InvalidOid ;
237+ CommonTableExpr * cte = NULL ;
238+ Index ctelevelsup = 0 ;
239+ Index levelsup ;
205240
206- while (pstate != NULL )
241+ /*
242+ * If it's an unqualified name, check for possible CTE matches.
243+ * A CTE hides any real relation matches. If no CTE, look for
244+ * a matching relation.
245+ */
246+ if (!relation -> schemaname )
247+ cte = scanNameSpaceForCTE (pstate ,refname ,& ctelevelsup );
248+ if (!cte )
249+ relId = RangeVarGetRelid (relation , true);
250+
251+ /* Now look for RTEs matching either the relation/CTE or the alias */
252+ for (levelsup = 0 ;
253+ pstate != NULL ;
254+ pstate = pstate -> parentParseState ,levelsup ++ )
207255{
208256ListCell * l ;
209257
210258foreach (l ,pstate -> p_rtable )
211259{
212260RangeTblEntry * rte = (RangeTblEntry * )lfirst (l );
213261
214- if (OidIsValid ( relId ) &&
215- rte -> rtekind == RTE_RELATION &&
262+ if (rte -> rtekind == RTE_RELATION &&
263+ OidIsValid ( relId ) &&
216264rte -> relid == relId )
217265return rte ;
266+ if (rte -> rtekind == RTE_CTE &&
267+ cte != NULL &&
268+ rte -> ctelevelsup + levelsup == ctelevelsup &&
269+ strcmp (rte -> ctename ,refname )== 0 )
270+ return rte ;
218271if (strcmp (rte -> eref -> aliasname ,refname )== 0 )
219272return rte ;
220273}
221-
222- pstate = pstate -> parentParseState ;
223274}
224275return NULL ;
225276}
@@ -1293,18 +1344,27 @@ addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte,
12931344RangeTblEntry *
12941345addImplicitRTE (ParseState * pstate ,RangeVar * relation )
12951346{
1347+ CommonTableExpr * cte = NULL ;
1348+ Index levelsup = 0 ;
12961349RangeTblEntry * rte ;
12971350
12981351/* issue warning or error as needed */
12991352warnAutoRange (pstate ,relation );
13001353
1354+ /* if it is an unqualified name, it might be a CTE reference */
1355+ if (!relation -> schemaname )
1356+ cte = scanNameSpaceForCTE (pstate ,relation -> relname ,& levelsup );
1357+
13011358/*
13021359 * Note that we set inFromCl true, so that the RTE will be listed
13031360 * explicitly if the parsetree is ever decompiled by ruleutils.c. This
13041361 * provides a migration path for views/rules that were originally written
13051362 * with implicit-RTE syntax.
13061363 */
1307- rte = addRangeTableEntry (pstate ,relation ,NULL , false, true);
1364+ if (cte )
1365+ rte = addRangeTableEntryForCTE (pstate ,cte ,levelsup ,NULL , true);
1366+ else
1367+ rte = addRangeTableEntry (pstate ,relation ,NULL , false, true);
13081368/* Add to joinlist and relnamespace, but not varnamespace */
13091369addRTEtoQuery (pstate ,rte , true, true, false);
13101370
@@ -2194,8 +2254,8 @@ warnAutoRange(ParseState *pstate, RangeVar *relation)
21942254if (rte )
21952255ereport (ERROR ,
21962256(errcode (ERRCODE_UNDEFINED_TABLE ),
2197- errmsg ("invalid reference to FROM-clause entry for table \"%s\"" ,
2198- relation -> relname ),
2257+ errmsg ("invalid reference to FROM-clause entry for table \"%s\"" ,
2258+ relation -> relname ),
21992259 (badAlias ?
22002260errhint ("Perhaps you meant to reference the table alias \"%s\"." ,
22012261badAlias ) :
@@ -2205,23 +2265,17 @@ warnAutoRange(ParseState *pstate, RangeVar *relation)
22052265else
22062266ereport (ERROR ,
22072267(errcode (ERRCODE_UNDEFINED_TABLE ),
2208- (pstate -> parentParseState ?
2209- errmsg ("missing FROM-clause entry in subquery for table \"%s\"" ,
2210- relation -> relname ) :
2211- errmsg ("missing FROM-clause entry for table \"%s\"" ,
2212- relation -> relname )),
2268+ errmsg ("missing FROM-clause entry for table \"%s\"" ,
2269+ relation -> relname ),
22132270parser_errposition (pstate ,relation -> location )));
22142271}
22152272else
22162273{
22172274/* just issue a warning */
22182275ereport (NOTICE ,
22192276(errcode (ERRCODE_UNDEFINED_TABLE ),
2220- (pstate -> parentParseState ?
2221- errmsg ("adding missing FROM-clause entry in subquery for table \"%s\"" ,
2222- relation -> relname ) :
2223- errmsg ("adding missing FROM-clause entry for table \"%s\"" ,
2224- relation -> relname )),
2277+ errmsg ("adding missing FROM-clause entry for table \"%s\"" ,
2278+ relation -> relname ),
22252279 (badAlias ?
22262280errhint ("Perhaps you meant to reference the table alias \"%s\"." ,
22272281badAlias ) :