8
8
*
9
9
*
10
10
* 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 $
12
12
*
13
13
*-------------------------------------------------------------------------
14
14
*/
@@ -182,6 +182,38 @@ scanNameSpaceForRelid(ParseState *pstate, Oid relid, int location)
182
182
return result ;
183
183
}
184
184
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
+
185
217
/*
186
218
* searchRangeTable
187
219
* See if any RangeTblEntry could possibly match the RangeVar.
@@ -194,32 +226,51 @@ scanNameSpaceForRelid(ParseState *pstate, Oid relid, int location)
194
226
* valid matches, but only one will be returned). This must be used ONLY
195
227
* as a heuristic in giving suitable error messages. See warnAutoRange.
196
228
*
197
- * Notice that we consider both matches on actual relation name and matches
198
- * on alias.
229
+ * Notice that we consider both matches on actual relation (or CTE) name
230
+ * and matches on alias.
199
231
*/
200
232
static RangeTblEntry *
201
233
searchRangeTable (ParseState * pstate , RangeVar * relation )
202
234
{
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 ;
205
240
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 ++ )
207
255
{
208
256
ListCell * l ;
209
257
210
258
foreach (l , pstate -> p_rtable )
211
259
{
212
260
RangeTblEntry * rte = (RangeTblEntry * ) lfirst (l );
213
261
214
- if (OidIsValid ( relId ) &&
215
- rte -> rtekind == RTE_RELATION &&
262
+ if (rte -> rtekind == RTE_RELATION &&
263
+ OidIsValid ( relId ) &&
216
264
rte -> relid == relId )
217
265
return rte ;
266
+ if (rte -> rtekind == RTE_CTE &&
267
+ cte != NULL &&
268
+ rte -> ctelevelsup + levelsup == ctelevelsup &&
269
+ strcmp (rte -> ctename , refname ) == 0 )
270
+ return rte ;
218
271
if (strcmp (rte -> eref -> aliasname , refname ) == 0 )
219
272
return rte ;
220
273
}
221
-
222
- pstate = pstate -> parentParseState ;
223
274
}
224
275
return NULL ;
225
276
}
@@ -1293,18 +1344,27 @@ addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte,
1293
1344
RangeTblEntry *
1294
1345
addImplicitRTE (ParseState * pstate , RangeVar * relation )
1295
1346
{
1347
+ CommonTableExpr * cte = NULL ;
1348
+ Index levelsup = 0 ;
1296
1349
RangeTblEntry * rte ;
1297
1350
1298
1351
/* issue warning or error as needed */
1299
1352
warnAutoRange (pstate , relation );
1300
1353
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
+
1301
1358
/*
1302
1359
* Note that we set inFromCl true, so that the RTE will be listed
1303
1360
* explicitly if the parsetree is ever decompiled by ruleutils.c. This
1304
1361
* provides a migration path for views/rules that were originally written
1305
1362
* with implicit-RTE syntax.
1306
1363
*/
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);
1308
1368
/* Add to joinlist and relnamespace, but not varnamespace */
1309
1369
addRTEtoQuery (pstate , rte , true, true, false);
1310
1370
@@ -2194,8 +2254,8 @@ warnAutoRange(ParseState *pstate, RangeVar *relation)
2194
2254
if (rte )
2195
2255
ereport (ERROR ,
2196
2256
(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 ),
2199
2259
(badAlias ?
2200
2260
errhint ("Perhaps you meant to reference the table alias \"%s\"." ,
2201
2261
badAlias ) :
@@ -2205,23 +2265,17 @@ warnAutoRange(ParseState *pstate, RangeVar *relation)
2205
2265
else
2206
2266
ereport (ERROR ,
2207
2267
(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 ),
2213
2270
parser_errposition (pstate , relation -> location )));
2214
2271
}
2215
2272
else
2216
2273
{
2217
2274
/* just issue a warning */
2218
2275
ereport (NOTICE ,
2219
2276
(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 ),
2225
2279
(badAlias ?
2226
2280
errhint ("Perhaps you meant to reference the table alias \"%s\"." ,
2227
2281
badAlias ) :
0 commit comments