[MDEV-26108] Crash with query referencing twice CTE that uses embedded recursive CTE Created: 2021-07-07  Updated: 2021-08-24  Resolved: 2021-07-10

Status: Closed
Project: MariaDB Server
Component/s: Optimizer - CTE
Affects Version/s: 10.4, 10.5, 10.6
Fix Version/s: 10.4.21, 10.5.12, 10.6.4

Type: Bug Priority: Major
Reporter: Igor Babaev Assignee: Igor Babaev
Resolution: Fixed Votes: 0
Labels: None

Issue Links:
Problem/Incident
Relates
relates to MDEV-25103 Crash at JOIN::reinit - calling store... Closed
relates to MDEV-26025 Server crashes while executing query ... Closed

 Description   

Processing of a query containing at least two references to the same CTE that uses an embedded recursive CTE causes a crash of the server in 10.4+.
Here's a simple test case that demonstrates this problem.

create table t1 (a int); 
insert into t1 values (5), (7);
with cte_e as (
  with recursive cte_r as (
    select a from t1 union select a+1 as a from cte_r r where a < 10
  ) select * from cte_r
) select * from cte_e s1, cte_e s2 where s1.a=s2.a;



 Comments   
Comment by Igor Babaev [ 2021-07-08 ]

In the test case query above we actually have 2 instances of the recursive CTE cte_r, because cte_e is used twice in the main query. Accordingly 2 TABLE_LIST structures are created for the recursive reference r used in the specification of these two instances of cte_r. In debugger we see that starting from 10.4 TABLE_LIST::init_derived() is called for the recursive reference used in the specification of the first instance of cte_r, but is not called for the recursive reference used in the specification of the second instance of cte_r. Yet this function must be called for the table reference of any derived table. TABLE_LIST::init_derived() is not called for the recursive reference of the second instance of cte_r because the SELECT specifying this instance was not included in the global list of all selects. It has happened because the following code

   old_lex->all_selects_list=
     (st_select_lex*) (lex->all_selects_list->
                      insert_chain_before(
                        (st_select_lex_node **) &(old_lex->all_selects_list),
                         with_select));

from With_element::clone_parsed_spec() assumes that with_select that points to the SELECT_LEX created for the specification of the second instance of cte_r is the last select the in the chain of the selects created when the specification of cte_e is parsed for the second reference to this CTE. It is realy so for 10.2 and 10.3, but the new grammar of 10.4 constructs SELECT_LEX objects in a different order and they are included into all_select_list in a different order.

Comment by Igor Babaev [ 2021-07-08 ]

This problem of missing SELECTs in the all_select_list does not lead to similar failures for the queries that use only non-recursive CTEs, such as the following one:

with cte_e as (
  with cte_o as (
    with cte_i as (select a from t1 group by a)
  select a from cte_i
 ) select a from cte_o group by a
) select * from cte_e s1, cte_e s2 where s1.a=s2.a;

.
Here TABLE_LIST::init_derived() is invoked for the second reference to cte_i occurred the the select created for the second instance of CTE cte_e. It is invoked by the code:

  if (!derived->is_with_table_recursive_reference() &&
       derived->init_derived(thd, FALSE))
     goto exit;

from mysql_derived_prepare().

Comment by Oleksandr Byelkin [ 2021-07-09 ]

OK to push

Comment by Igor Babaev [ 2021-07-10 ]

A fix for this bug was pushed into 10.4. It should be merged upstream as it is.

Generated at Thu Feb 08 09:42:48 UTC 2024 using Jira 8.20.16#820016-sha1:9d11dbea5f4be3d4cc21f03a88dd11d8c8687422.