Re: BUG #17320: A SEGV in optimizer

Поиск
Список
Период
Сортировка
От Tom Lane
Тема Re: BUG #17320: A SEGV in optimizer
Дата
Msg-id 3774893.1650663823@sss.pgh.pa.us
обсуждение исходный текст
Ответ на Re: BUG #17320: A SEGV in optimizer  (Kyotaro Horiguchi <horikyota.ntt@gmail.com>)
Список pgsql-bugs
[ this'd been on my to-do list for a long time, I finally got to it ]

Kyotaro Horiguchi <horikyota.ntt@gmail.com> writes:
> If I tried to execute the following query, I got the follwoing
> error. This looks like rooted from the same mistake.

> WITH RECURSIVE x ( x ) AS
>  (SELECT 1
>   UNION
>   (WITH x AS (SELECT * FROM x)
>    SELECT * FROM x)
>  ) SEARCH BREADTH FIRST BY x SET NCHAR
> SELECT * FROM x;

> ERROR:  could not find attribute 2 in subquery targetlist

> By the way, if I executed the following query, I get the following
> another assertion failure.

> WITH RECURSIVE x ( x ) AS
>  (SELECT 1
>   UNION
>   (WITH y AS (SELECT * FROM x)
>    SELECT * FROM y)
>  ) SEARCH BREADTH FIRST BY x SET NCHAR
> SELECT * FROM x;

> TRAP: FailedAssertion("cte_rtindex > 0", File: "rewriteSearchCycle.c", Line: 398, PID: 31288)

This is pretty remarkable, because those two queries *should* be
semantically identical.  I guessed that something is getting confused
by the duplicated CTE name, and it didn't take long to see what:
rewriteSearchAndCycle() is failing to check ctelevelsup while looking
for a match to the recursive CTE's name.  Thus, it thinks that the
reference to the lower WITH item (either "x" or "y" in these two
examples) is the recursive reference it seeks, which it ain't.

Eventually perhaps we should support this, since a recursive query
of this form is allowed in other contexts.  But I'm content to
throw error for now, as per the attached patch.  This catches all
the cases shown in this thread, as well as the seemingly unrelated
error shown in bug #17318.

(I looked for other places that might be carelessly matching on
ctename without checking levelsup, and found none.)

            regards, tom lane

diff --git a/src/backend/rewrite/rewriteSearchCycle.c b/src/backend/rewrite/rewriteSearchCycle.c
index dc408404eb..58f684cd52 100644
--- a/src/backend/rewrite/rewriteSearchCycle.c
+++ b/src/backend/rewrite/rewriteSearchCycle.c
@@ -383,19 +383,32 @@ rewriteSearchAndCycle(CommonTableExpr *cte)
     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++)
     {
         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;
             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
toplevel of its right-hand SELECT", 
+                        cte->ctename)));

     newsubquery = copyObject(rte2->subquery);
     IncrementVarSublevelsUp((Node *) newsubquery, 1, 1);
diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out
index 32f90d7375..d731604374 100644
--- a/src/test/regress/expected/with.out
+++ b/src/test/regress/expected/with.out
@@ -846,6 +846,16 @@ with recursive search_graph(f, t, label) as (
 ) search depth first by f, t set 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
+-- 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-handSELECT 
 -- test ruleutils and view expansion
 create temp view v_search as
 with recursive search_graph(f, t, label) as (
diff --git a/src/test/regress/sql/with.sql b/src/test/regress/sql/with.sql
index 7e430e859e..3251c29584 100644
--- a/src/test/regress/sql/with.sql
+++ b/src/test/regress/sql/with.sql
@@ -464,6 +464,16 @@ with recursive search_graph(f, t, label) as (
 ) search depth first by f, t set 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
 create temp view v_search as
 with recursive search_graph(f, t, label) as (

В списке pgsql-bugs по дате отправления:

Предыдущее
От: John Honarvar
Дата:
Сообщение: Is this a known Bug?
Следующее
От: Andrey Borodin
Дата:
Сообщение: Re: BUG #17401: REINDEX TABLE CONCURRENTLY creates a race condition on a streaming replica