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

Commit 0ff384f

Browse files
committed
Fix the implicit-RTE code to be able to handle implicit RTEs for CTEs, as
well as regular tables. Per discussion, this seems necessary to meet the principle of least astonishment. In passing, simplify the error messages in warnAutoRange(). Now that we have parser error position info for these errors, it doesn't seem very useful to word the error message differently depending on whether we are inside a sub-select or not.
1 parent af88c9b commit 0ff384f

File tree

3 files changed

+87
-49
lines changed

3 files changed

+87
-49
lines changed

src/backend/parser/parse_clause.c

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.180 2008/10/04 21:56:54 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.181 2008/10/06 02:12:56 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -631,34 +631,15 @@ transformFromClauseItem(ParseState *pstate, Node *n,
631631
RangeTblEntry *rte = NULL;
632632
int rtindex;
633633

634-
/*
635-
* If it is an unqualified name, it might be a reference to some
636-
* CTE visible in this or a parent query.
637-
*/
634+
/* if it is an unqualified name, it might be a CTE reference */
638635
if (!rv->schemaname)
639636
{
640-
ParseState *ps;
637+
CommonTableExpr *cte;
641638
Index levelsup;
642639

643-
for (ps = pstate, levelsup = 0;
644-
ps != NULL;
645-
ps = ps->parentParseState, levelsup++)
646-
{
647-
ListCell *lc;
648-
649-
foreach(lc, ps->p_ctenamespace)
650-
{
651-
CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
652-
653-
if (strcmp(rv->relname, cte->ctename) == 0)
654-
{
655-
rte = transformCTEReference(pstate, rv, cte, levelsup);
656-
break;
657-
}
658-
}
659-
if (rte)
660-
break;
661-
}
640+
cte = scanNameSpaceForCTE(pstate, rv->relname, &levelsup);
641+
if (cte)
642+
rte = transformCTEReference(pstate, rv, cte, levelsup);
662643
}
663644

664645
/* if not found as a CTE, must be a table reference */

src/backend/parser/parse_relation.c

Lines changed: 77 additions & 23 deletions
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.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)
182182
return 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 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.
199231
*/
200232
static RangeTblEntry *
201233
searchRangeTable(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
{
208256
ListCell *l;
209257

210258
foreach(l, pstate->p_rtable)
211259
{
212260
RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
213261

214-
if (OidIsValid(relId) &&
215-
rte->rtekind == RTE_RELATION &&
262+
if (rte->rtekind == RTE_RELATION &&
263+
OidIsValid(relId) &&
216264
rte->relid == relId)
217265
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;
218271
if (strcmp(rte->eref->aliasname, refname) == 0)
219272
return rte;
220273
}
221-
222-
pstate = pstate->parentParseState;
223274
}
224275
return NULL;
225276
}
@@ -1293,18 +1344,27 @@ addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte,
12931344
RangeTblEntry *
12941345
addImplicitRTE(ParseState *pstate, RangeVar *relation)
12951346
{
1347+
CommonTableExpr *cte = NULL;
1348+
Index levelsup = 0;
12961349
RangeTblEntry *rte;
12971350

12981351
/* issue warning or error as needed */
12991352
warnAutoRange(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 */
13091369
addRTEtoQuery(pstate, rte, true, true, false);
13101370

@@ -2194,8 +2254,8 @@ warnAutoRange(ParseState *pstate, RangeVar *relation)
21942254
if (rte)
21952255
ereport(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 ?
22002260
errhint("Perhaps you meant to reference the table alias \"%s\".",
22012261
badAlias) :
@@ -2205,23 +2265,17 @@ warnAutoRange(ParseState *pstate, RangeVar *relation)
22052265
else
22062266
ereport(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),
22132270
parser_errposition(pstate, relation->location)));
22142271
}
22152272
else
22162273
{
22172274
/* just issue a warning */
22182275
ereport(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 ?
22262280
errhint("Perhaps you meant to reference the table alias \"%s\".",
22272281
badAlias) :

src/include/parser/parse_relation.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $PostgreSQL: pgsql/src/include/parser/parse_relation.h,v 1.59 2008/10/04 21:56:55 tgl Exp $
10+
* $PostgreSQL: pgsql/src/include/parser/parse_relation.h,v 1.60 2008/10/06 02:12:56 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -23,6 +23,9 @@ extern RangeTblEntry *refnameRangeTblEntry(ParseState *pstate,
2323
const char *refname,
2424
int location,
2525
int *sublevels_up);
26+
extern CommonTableExpr *scanNameSpaceForCTE(ParseState *pstate,
27+
const char *refname,
28+
Index *ctelevelsup);
2629
extern void checkNameSpaceConflicts(ParseState *pstate, List *namespace1,
2730
List *namespace2);
2831
extern int RTERangeTablePosn(ParseState *pstate,

0 commit comments

Comments
 (0)