Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions mysql-test/main/cte_recursive.result
Original file line number Diff line number Diff line change
Expand Up @@ -5979,7 +5979,7 @@ ERROR 42S22: Unknown column 'x' in 'SELECT'
# MDEV-32299 Segfault when preparing unreferenced select in recursive CTE
#
SELECT ( WITH RECURSIVE x AS ( SELECT * FROM ( SELECT UTC_TIMESTAMP FROM ( SELECT ( WITH x AS ( WITH x AS ( SELECT * FROM x ) SELECT 1 ) SELECT 1 ) ) x ) x UNION SELECT NULL ) ( SELECT x FROM x ) ) ;
ERROR 42S22: Unknown column 'x' in 'SELECT'
ERROR HY000: Restrictions imposed on recursive definitions are violated for table 'x'
select (
with recursive x as (
select * from (
Expand All @@ -6001,7 +6001,7 @@ select null
)
(select x from x)
);
ERROR 42S22: Unknown column 'x' in 'SELECT'
ERROR HY000: Restrictions imposed on recursive definitions are violated for table 'x'
select (
with recursive r as (
select * from (
Expand Down Expand Up @@ -6153,4 +6153,13 @@ y
2
2
4
#
# MDEV-32326: Recursive reference in scalar subquery should be rejected
#
WITH RECURSIVE x AS (
SELECT 1
UNION
SELECT 1 > ( WITH cte AS ( SELECT 1 FROM x ) SELECT 1 FROM cte ) )
SELECT 1 FROM x;
ERROR HY000: Restrictions imposed on recursive definitions are violated for table 'x'
# End of 10.11 tests
15 changes: 13 additions & 2 deletions mysql-test/main/cte_recursive.test
Original file line number Diff line number Diff line change
Expand Up @@ -4071,12 +4071,12 @@ SELECT ( WITH RECURSIVE x AS ( WITH x AS ( SELECT 1 FROM t14 ) SELECT x ) , t14
--echo #

# As in bug report
--error ER_BAD_FIELD_ERROR
--error ER_NOT_STANDARD_COMPLIANT_RECURSIVE
SELECT ( WITH RECURSIVE x AS ( SELECT * FROM ( SELECT UTC_TIMESTAMP FROM ( SELECT ( WITH x AS ( WITH x AS ( SELECT * FROM x ) SELECT 1 ) SELECT 1 ) ) x ) x UNION SELECT NULL ) ( SELECT x FROM x ) ) ;

#0 0x00005555569dff7b in TABLE_LIST::reset_const_table (this=0x7fffe0019938) at ../src/sql/table.cc:9615
#1 0x00005555569dffd4 in TABLE_LIST::reset_const_table (this=0x7fffe001a8c0) at ../src/sql/table.cc:9622
--error ER_BAD_FIELD_ERROR
--error ER_NOT_STANDARD_COMPLIANT_RECURSIVE
select (
with recursive x as (
select * from (
Expand Down Expand Up @@ -4240,4 +4240,15 @@ with recursive r (y) as (
select z * 2 from r where z < 10)
select y from r;

--echo #
--echo # MDEV-32326: Recursive reference in scalar subquery should be rejected
--echo #

--ERROR ER_NOT_STANDARD_COMPLIANT_RECURSIVE
WITH RECURSIVE x AS (
SELECT 1
UNION
SELECT 1 > ( WITH cte AS ( SELECT 1 FROM x ) SELECT 1 FROM cte ) )
SELECT 1 FROM x;

--echo # End of 10.11 tests
2 changes: 1 addition & 1 deletion sql/sql_cte.cc
Original file line number Diff line number Diff line change
Expand Up @@ -653,13 +653,13 @@ void With_element::check_dependencies_in_unit(st_select_lex_unit *unit,
table_map *dep_map)
{
st_unit_ctxt_elem unit_ctxt_elem= {ctxt, unit};
in_subq |= unit->item != NULL;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Because in_subq is a member variable of With_element, modifying it with in_subq |= unit->item != NULL; during the recursive traversal of nested units (subqueries) permanently sets it to true for the remainder of the dependency check of this With_element. This means that any subsequent sibling subqueries or top-level query blocks (such as the recursive step of a UNION) will incorrectly inherit in_subq == true, causing valid, standard-compliant recursive CTEs to be rejected with ER_NOT_STANDARD_COMPLIANT_RECURSIVE.

To fix this, we should save and restore the state of in_subq when entering and leaving check_dependencies_in_unit. Since the end of the function is outside the current diff hunk, we can use a simple local RAII struct to automatically restore the value upon function exit.

Suggested change
in_subq |= unit->item != NULL;
struct Save_in_subq {
bool &ref;
bool val;
Save_in_subq(bool &r) : ref(r), val(r) {}
~Save_in_subq() { ref = val; }
} save_in_subq(in_subq);
in_subq |= unit->item != NULL;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are wrong, in_subq is not a member of With_element but a by-value parameter of the function, so it's scoped to only this frame and doesn't mutate the caller's in_subq value upstream.

if (unit->with_clause)
{
(void) unit->with_clause->check_dependencies();
check_dependencies_in_with_clause(unit->with_clause, &unit_ctxt_elem,
in_subq, dep_map);
}
in_subq |= unit->item != NULL;
st_select_lex *sl= unit->first_select();
for (; sl; sl= sl->next_select())
{
Expand Down