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

Commit f819020

Browse files
committed
Fix incautious CTE matching in rewriteSearchAndCycle().
This function looks for a reference to the recursive WITH CTE, but it checked only the CTE name not ctelevelsup, so that it could seize on a lower CTE that happened to have the same name. This would result in planner failures later, either weird errors such as "could not find attribute 2 in subquery targetlist", or crashes or assertion failures. The code also merely Assert'ed that it found a matching entry, which is not guaranteed at all by the parser. Per bugs #17320 and #17318 from Zhiyong Wu. Thanks to Kyotaro Horiguchi for investigation. Discussion: https://postgr.es/m/17320-70e37868182512ab@postgresql.org Discussion: https://postgr.es/m/17318-2eb65a3a611d2368@postgresql.org
1 parent c1da0ac commit f819020

File tree

3 files changed

+36
-3
lines changed

3 files changed

+36
-3
lines changed

src/backend/rewrite/rewriteSearchCycle.c

+16-3
Original file line numberDiff line numberDiff line change
@@ -383,19 +383,32 @@ rewriteSearchAndCycle(CommonTableExpr *cte)
383383
newrte->eref = newrte->alias;
384384

385385
/*
386-
* Find the reference to our CTE in the range table
386+
* Find the reference to the recursive CTE in the right UNION subquery's
387+
* range table. We expect it to be two levels up from the UNION subquery
388+
* (and must check that to avoid being fooled by sub-WITHs with the same
389+
* CTE name). There will not be more than one such reference, because the
390+
* parser would have rejected that (see checkWellFormedRecursion() in
391+
* parse_cte.c). However, the parser doesn't insist that the reference
392+
* appear in the UNION subquery's topmost range table, so we might fail to
393+
* find it at all. That's an unimplemented case for the moment.
387394
*/
388395
for (int rti = 1; rti <= list_length(rte2->subquery->rtable); rti++)
389396
{
390397
RangeTblEntry *e = rt_fetch(rti, rte2->subquery->rtable);
391398

392-
if (e->rtekind == RTE_CTE && strcmp(cte->ctename, e->ctename) == 0)
399+
if (e->rtekind == RTE_CTE &&
400+
strcmp(cte->ctename, e->ctename) == 0 &&
401+
e->ctelevelsup == 2)
393402
{
394403
cte_rtindex = rti;
395404
break;
396405
}
397406
}
398-
Assert(cte_rtindex > 0);
407+
if (cte_rtindex <= 0)
408+
ereport(ERROR,
409+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
410+
errmsg("with a SEARCH or CYCLE clause, the recursive reference to WITH query \"%s\" must be at the top level of its right-hand SELECT",
411+
cte->ctename)));
399412

400413
newsubquery = copyObject(rte2->subquery);
401414
IncrementVarSublevelsUp((Node *) newsubquery, 1, 1);

src/test/regress/expected/with.out

+10
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,16 @@ with recursive search_graph(f, t, label) as (
846846
) search depth first by f, t set seq
847847
select * from search_graph order by seq;
848848
ERROR: with a SEARCH or CYCLE clause, the right side of the UNION must be a SELECT
849+
-- check that we distinguish same CTE name used at different levels
850+
-- (this case could be supported, perhaps, but it isn't today)
851+
with recursive x(col) as (
852+
select 1
853+
union
854+
(with x as (select * from x)
855+
select * from x)
856+
) search depth first by col set seq
857+
select * from x;
858+
ERROR: with a SEARCH or CYCLE clause, the recursive reference to WITH query "x" must be at the top level of its right-hand SELECT
849859
-- test ruleutils and view expansion
850860
create temp view v_search as
851861
with recursive search_graph(f, t, label) as (

src/test/regress/sql/with.sql

+10
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,16 @@ with recursive search_graph(f, t, label) as (
464464
) search depth first by f, t set seq
465465
select * from search_graph order by seq;
466466

467+
-- check that we distinguish same CTE name used at different levels
468+
-- (this case could be supported, perhaps, but it isn't today)
469+
with recursive x(col) as (
470+
select 1
471+
union
472+
(with x as (select * from x)
473+
select * from x)
474+
) search depth first by col set seq
475+
select * from x;
476+
467477
-- test ruleutils and view expansion
468478
create temp view v_search as
469479
with recursive search_graph(f, t, label) as (

0 commit comments

Comments
 (0)