mirror of
https://github.com/postgres/postgres.git
synced 2025-07-09 22:41:56 +03:00
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
This commit is contained in:
@ -383,19 +383,32 @@ rewriteSearchAndCycle(CommonTableExpr *cte)
|
|||||||
newrte->eref = newrte->alias;
|
newrte->eref = newrte->alias;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Find the reference to our CTE in the range table
|
* Find the reference to the recursive CTE in the right UNION subquery's
|
||||||
|
* range table. We expect it to be two levels up from the UNION subquery
|
||||||
|
* (and must check that to avoid being fooled by sub-WITHs with the same
|
||||||
|
* CTE name). There will not be more than one such reference, because the
|
||||||
|
* parser would have rejected that (see checkWellFormedRecursion() in
|
||||||
|
* parse_cte.c). However, the parser doesn't insist that the reference
|
||||||
|
* appear in the UNION subquery's topmost range table, so we might fail to
|
||||||
|
* find it at all. That's an unimplemented case for the moment.
|
||||||
*/
|
*/
|
||||||
for (int rti = 1; rti <= list_length(rte2->subquery->rtable); rti++)
|
for (int rti = 1; rti <= list_length(rte2->subquery->rtable); rti++)
|
||||||
{
|
{
|
||||||
RangeTblEntry *e = rt_fetch(rti, rte2->subquery->rtable);
|
RangeTblEntry *e = rt_fetch(rti, rte2->subquery->rtable);
|
||||||
|
|
||||||
if (e->rtekind == RTE_CTE && strcmp(cte->ctename, e->ctename) == 0)
|
if (e->rtekind == RTE_CTE &&
|
||||||
|
strcmp(cte->ctename, e->ctename) == 0 &&
|
||||||
|
e->ctelevelsup == 2)
|
||||||
{
|
{
|
||||||
cte_rtindex = rti;
|
cte_rtindex = rti;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Assert(cte_rtindex > 0);
|
if (cte_rtindex <= 0)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
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",
|
||||||
|
cte->ctename)));
|
||||||
|
|
||||||
newsubquery = copyObject(rte2->subquery);
|
newsubquery = copyObject(rte2->subquery);
|
||||||
IncrementVarSublevelsUp((Node *) newsubquery, 1, 1);
|
IncrementVarSublevelsUp((Node *) newsubquery, 1, 1);
|
||||||
|
@ -846,6 +846,16 @@ with recursive search_graph(f, t, label) as (
|
|||||||
) search depth first by f, t set seq
|
) search depth first by f, t set seq
|
||||||
select * from search_graph order by seq;
|
select * from search_graph order by seq;
|
||||||
ERROR: with a SEARCH or CYCLE clause, the right side of the UNION must be a SELECT
|
ERROR: with a SEARCH or CYCLE clause, the right side of the UNION must be a SELECT
|
||||||
|
-- check that we distinguish same CTE name used at different levels
|
||||||
|
-- (this case could be supported, perhaps, but it isn't today)
|
||||||
|
with recursive x(col) as (
|
||||||
|
select 1
|
||||||
|
union
|
||||||
|
(with x as (select * from x)
|
||||||
|
select * from x)
|
||||||
|
) search depth first by col set seq
|
||||||
|
select * from x;
|
||||||
|
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
|
||||||
-- test ruleutils and view expansion
|
-- test ruleutils and view expansion
|
||||||
create temp view v_search as
|
create temp view v_search as
|
||||||
with recursive search_graph(f, t, label) as (
|
with recursive search_graph(f, t, label) as (
|
||||||
|
@ -464,6 +464,16 @@ with recursive search_graph(f, t, label) as (
|
|||||||
) search depth first by f, t set seq
|
) search depth first by f, t set seq
|
||||||
select * from search_graph order by seq;
|
select * from search_graph order by seq;
|
||||||
|
|
||||||
|
-- check that we distinguish same CTE name used at different levels
|
||||||
|
-- (this case could be supported, perhaps, but it isn't today)
|
||||||
|
with recursive x(col) as (
|
||||||
|
select 1
|
||||||
|
union
|
||||||
|
(with x as (select * from x)
|
||||||
|
select * from x)
|
||||||
|
) search depth first by col set seq
|
||||||
|
select * from x;
|
||||||
|
|
||||||
-- test ruleutils and view expansion
|
-- test ruleutils and view expansion
|
||||||
create temp view v_search as
|
create temp view v_search as
|
||||||
with recursive search_graph(f, t, label) as (
|
with recursive search_graph(f, t, label) as (
|
||||||
|
Reference in New Issue
Block a user