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

Lines changed: 16 additions & 3 deletions
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

Lines changed: 10 additions & 0 deletions
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

Lines changed: 10 additions & 0 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy