[MDEV-30248] Infinite sequence of recursive calls when processing embedded CTE Created: 2022-12-17 Updated: 2023-02-13 Resolved: 2023-01-25 |
|
| Status: | Closed |
| Project: | MariaDB Server |
| Component/s: | Optimizer - CTE |
| Affects Version/s: | 10.3, 10.4, 10.5, 10.6, 10.7, 10.8, 10.9, 10.10, 10.11 |
| Fix Version/s: | 10.11.2, 10.3.38, 10.4.28, 10.5.19, 10.6.12, 10.7.8, 10.8.7, 10.9.5, 10.10.3 |
| Type: | Bug | Priority: | Critical |
| Reporter: | Roel Van de Paar | Assignee: | Igor Babaev |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | hang, regression, stack-looping | ||
| Issue Links: |
|
||||||||
| Description |
|
New regression caused by
Leads to:
Bug confirmed present in: |
| Comments |
| Comment by Roel Van de Paar [ 2022-12-17 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
A hang can also be seen when using the following testcase:
Leads to:
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Roel Van de Paar [ 2022-12-17 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
I am adding two unsimplified testcases (one which was reduced by computer, then by hand to the above minimum), to test any patches with:
And
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Igor Babaev [ 2022-12-22 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Processing of the following query also falls into an infinite loop:
If there is no base table with name 'x' processing of the query should end up with the error message
Otherwise processing of the query should return:
Similar results are expected when processing the query:
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Igor Babaev [ 2022-12-22 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
After the following patch has been applied to the current 10.3 code the above queries do not cause any problems and return expected results:
All tests from the 'main' test suite also finish as expected. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Igor Babaev [ 2023-01-20 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Let's figure out why we come to an infinite sequence recursive calls when resolving references to CTE for the query
Looking at the query we can easily see that the reference table x aliased with t cannot be resolved against any CTE comtained in the query because it is used within the defition of each CTE with name x and none of them is defined in a RECURSIVE with clause.
and we are going to resolve the reference to x aliased as t. to it. The function fails to resolve x as t. We return to the function With_clause::check_dependencies called for the with clause WITH x AS (SELECT a FROM x AS t). Note that the reference x as t has not been resolved so far.
The only with element of this with clause is 'x AS (WITH x AS (SELECT a FROM x AS t) SELECT 1 AS b)'. 'WITH x AS (SELECT a FROM x AS t) SELECT 1 AS b'. The select SELECT 1 AS b does not contain any table references. So With_element::check_dependencies_in_unit() is called for the with element and the first inner unit SELECT a FROM x AS t is passed to it as parameter together {ctxt, unit}, where ctxt is pointer to {NULL, spec} . The unit does not contain with clause attached to it, it contains only one select and With_element::check_dependencies_in_select() is called for it. Here find_table_def_in_with_clauses() is called for x as t again but now with {ctxt, unit}as parameter. 'unit' does not contain with clause attached to it. As it is a with element we consider the unit from {NULL,spec}. It is 'select 1 AS b' there is a with clause attached to it. We call With_clause::find_table_def() passing x as t and with element with the specSELECT a AS a FROM x AS t as the barrier. This is the only element in the with clause. So we return NULL. we return to LEX::check_dependencies_in_with_clauses() with x as t still unresolved. Now we move to the with clause
.
as parameters. With_element::check_dependencies_in_unit() called for the first inner unit of the above select and this is SELECT b FROM x AS r. A with clause is attached to it. With_element::check_dependencies_in_with_clause() is called for this with clause and then With_element::check_dependencies_in_unit() is called for the spec of the only with element of this with clause select 1 AS b. The with clause WITH x AS (SELECT a FROM x AS t) is attached to this unit. With_element::check_dependencies_in_unit() is called for the unit SELECT a FROM x AS t and then check_dependencies_in_select() is called for it. find_table_def_in_with_clauses() is called for x as t again.
With_clause::find_table_def() is called with no barrier and x as t is resolved against CTE:
Resolution of x as t against the embedding CTE can lead to serious problems. This is not good. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Igor Babaev [ 2023-01-21 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
As a result of the resolution of table reference x as t against the CTE
we have
to be the inner unit for the unit
and vice versa.
it brings us to an infinite sequence of recursive calls in st_select_lex_unit::set_unique_exclude().
where t1 is defined as
it brings us to an infinite loop in the function st_select_lex::find_table_def_in_with_clauses().
we have an infinite sequence of recursive call. Note that there is no hanging CTE in this query.
as well as for the query
and for the query
If we
then the query
returns the expected result
.
we still have an infinite loop in st_select_lex::find_table_def_in_with_clauses(). | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Oleksandr Byelkin [ 2023-01-23 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
OK to push | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Igor Babaev [ 2023-01-25 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
The patch above in one of the comments that uses the method With_clause::find_with_element_by_spec() is incorrect because there is no 1 to 1 relation between WITH elements and units created for the references to these elements. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Igor Babaev [ 2023-01-25 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
All not simplified test cases were checked with the fix. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Igor Babaev [ 2023-01-25 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
A fix for this bug was pushed into 10.3. It should be merged upstream as it is. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Roel Van de Paar [ 2023-01-28 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Impressive work igor, thank you. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Roel Van de Paar [ 2023-02-13 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
An additional testcase was observed to generate a slightly different stack. The issue looks to be fixed with the patch from this bug. Adding it for completeness. It looks very similar to a testcase by Igor provided earlier.
|