Skip to content

Commit 3b4de4c

Browse files
committed
MDEV-32084: Assertion in best_extension_by_limited_search() ...
When subquery with LEFT JOIN is converted into semi-join, it is possible to construct cases where the LEFT JOIN's ON expression refers to a table in the current select but not in the current join nest. For example: t1 SEMI JOIN ( t2 LEFT JOIN (t3 LEFT JOIN t4 ON t4.col=t1.col) ON expr ) here, ON t4.col=t1.col" has this property. Let's denote it as ON-EXPR-HAS-REF-OUTSIDE-NEST. The optimizer handles LEFT JOINs like so: - Outer join runtime requires that "inner tables follow outer" in any join order. - Join optimizer enforces this by constructing join orders that follow table dependencies as they are specified in TABLE_LIST::dep_tables. - The dep_tables are set in simplify_joins() according to the contents of ON expressions and LEFT JOIN structure. However, the logic in simplify_joins() failed to account for possible ON-EXPR-HAS-REF-OUTSIDE-NEST. It assumed that references outside of the current join nest could only be OUTER_REF_TABLE_BIT or RAND_TABLE_BIT. The fix was to add the missing logic.
1 parent d1a6792 commit 3b4de4c

File tree

4 files changed

+91
-4
lines changed

4 files changed

+91
-4
lines changed

mysql-test/main/join_nested.result

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2051,3 +2051,15 @@ a b c a a b
20512051
DROP TABLE t1, t2, t3;
20522052
set join_cache_level= @save_join_cache_level;
20532053
# end of 10.3 tests
2054+
#
2055+
# MDEV-32084: Assertion in best_extension_by_limited_search(), or crash elsewhere in release
2056+
#
2057+
CREATE TABLE t1 (i int);
2058+
INSERT INTO t1 values (1),(2);
2059+
SELECT 1 FROM t1 WHERE i IN
2060+
(SELECT 1 FROM t1 c
2061+
LEFT JOIN (t1 a LEFT JOIN t1 b ON t1.i = b.i) ON c.i = t1.i);
2062+
1
2063+
1
2064+
DROP TABLE t1;
2065+
# end of 10.11 tests

mysql-test/main/join_nested.test

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1458,3 +1458,16 @@ DROP TABLE t1, t2, t3;
14581458
set join_cache_level= @save_join_cache_level;
14591459

14601460
--echo # end of 10.3 tests
1461+
1462+
--echo #
1463+
--echo # MDEV-32084: Assertion in best_extension_by_limited_search(), or crash elsewhere in release
1464+
--echo #
1465+
CREATE TABLE t1 (i int);
1466+
INSERT INTO t1 values (1),(2);
1467+
1468+
SELECT 1 FROM t1 WHERE i IN
1469+
(SELECT 1 FROM t1 c
1470+
LEFT JOIN (t1 a LEFT JOIN t1 b ON t1.i = b.i) ON c.i = t1.i);
1471+
1472+
DROP TABLE t1;
1473+
--echo # end of 10.11 tests

mysql-test/main/join_nested_jcl6.result

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2060,6 +2060,18 @@ a b c a a b
20602060
DROP TABLE t1, t2, t3;
20612061
set join_cache_level= @save_join_cache_level;
20622062
# end of 10.3 tests
2063+
#
2064+
# MDEV-32084: Assertion in best_extension_by_limited_search(), or crash elsewhere in release
2065+
#
2066+
CREATE TABLE t1 (i int);
2067+
INSERT INTO t1 values (1),(2);
2068+
SELECT 1 FROM t1 WHERE i IN
2069+
(SELECT 1 FROM t1 c
2070+
LEFT JOIN (t1 a LEFT JOIN t1 b ON t1.i = b.i) ON c.i = t1.i);
2071+
1
2072+
1
2073+
DROP TABLE t1;
2074+
# end of 10.11 tests
20632075
CREATE TABLE t5 (a int, b int, c int, PRIMARY KEY(a), KEY b_i (b));
20642076
CREATE TABLE t6 (a int, b int, c int, PRIMARY KEY(a), KEY b_i (b));
20652077
CREATE TABLE t7 (a int, b int, c int, PRIMARY KEY(a), KEY b_i (b));

sql/sql_select.cc

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18628,6 +18628,8 @@ simplify_joins(JOIN *join, List<TABLE_LIST> *join_list, COND *conds, bool top,
1862818628
prev_table->dep_tables|= used_tables;
1862918629
if (prev_table->on_expr)
1863018630
{
18631+
/* If the ON expression is still there, it's an outer join */
18632+
DBUG_ASSERT(prev_table->outer_join);
1863118633
prev_table->dep_tables|= table->on_expr_dep_tables;
1863218634
table_map prev_used_tables= prev_table->nested_join ?
1863318635
prev_table->nested_join->used_tables :
@@ -18642,11 +18644,59 @@ simplify_joins(JOIN *join, List<TABLE_LIST> *join_list, COND *conds, bool top,
1864218644
prevents update of inner table dependences.
1864318645
For example it might happen if RAND() function
1864418646
is used in JOIN ON clause.
18645-
*/
18646-
if (!((prev_table->on_expr->used_tables() &
18647-
~(OUTER_REF_TABLE_BIT | RAND_TABLE_BIT)) &
18648-
~prev_used_tables))
18647+
*/
18648+
table_map prev_on_expr_deps= prev_table->on_expr->used_tables() &
18649+
~(OUTER_REF_TABLE_BIT | RAND_TABLE_BIT);
18650+
prev_on_expr_deps&= ~prev_used_tables;
18651+
18652+
if (!prev_on_expr_deps)
1864918653
prev_table->dep_tables|= used_tables;
18654+
else
18655+
{
18656+
/*
18657+
Another possible case is when prev_on_expr_deps!=0 but it depends
18658+
on a table outside this join nest. SQL name resolution don't allow
18659+
this but it is possible when LEFT JOIN is inside a subquery which
18660+
is converted into a semi-join nest, Example:
18661+
18662+
t1 SEMI JOIN (
18663+
t2
18664+
LEFT JOIN (t3 LEFT JOIN t4 ON t4.col=t1.col) ON expr
18665+
) ON ...
18666+
18667+
here, we would have prev_table=t4, table=t3. The condition
18668+
"ON t4.col=t1.col" depends on tables {t1, t4}. To make sure the
18669+
optimizer puts t3 before t4 we need to make sure t4.dep_tables
18670+
includes t3.
18671+
*/
18672+
18673+
DBUG_ASSERT(table->embedding == prev_table->embedding);
18674+
if (table->embedding)
18675+
{
18676+
/*
18677+
Find what are the "peers" of "table" in the join nest. Normally,
18678+
it is table->embedding->nested_join->used_tables, but here we are
18679+
in the process of recomputing that value.
18680+
So, we walk the join list and collect the bitmap of peers:
18681+
*/
18682+
table_map peers= 0;
18683+
List_iterator_fast<TABLE_LIST> li(*join_list);
18684+
TABLE_LIST *peer;
18685+
while ((peer= li++))
18686+
{
18687+
table_map curmap= peer->nested_join
18688+
? peer->nested_join->used_tables
18689+
: peer->get_map();
18690+
peers|= curmap;
18691+
}
18692+
/*
18693+
If prev_table doesn't depend on any of its peers, add a
18694+
dependency on nearest peer, that is, on 'table'.
18695+
*/
18696+
if (!(prev_on_expr_deps & peers))
18697+
prev_table->dep_tables|= used_tables;
18698+
}
18699+
}
1865018700
}
1865118701
}
1865218702
prev_table= table;

0 commit comments

Comments
 (0)