From 4bc1860eb4baf168fdde69734b38ceecbd0cb9ec Mon Sep 17 00:00:00 2001 From: Galina Shalygina Date: Tue, 30 Apr 2024 21:31:47 +0200 Subject: [PATCH] MDEV-23878 Wrong result with semi-join and splittable derived table Due to this bug a wrong result might be expected from queries with an IN subquery predicate in the WHERE clause and a derived table in the FROM clause to which split optimization could be applied. The function JOIN::fix_all_splittings_in_plan() used the value of the bitmap JOIN::sjm_lookup_tables() such as it had been left after the search for the best plan for the select containing the splittable derived table. That value could not be guaranteed to be correct. So the recalculation of this bitmap is needed to exclude the plans with key accesses from SJM lookup tables. Approved by Igor Babaev --- mysql-test/main/derived_split_innodb.result | 57 +++++++++++++++++++++ mysql-test/main/derived_split_innodb.test | 42 +++++++++++++++ sql/opt_split.cc | 5 +- 3 files changed, 103 insertions(+), 1 deletion(-) diff --git a/mysql-test/main/derived_split_innodb.result b/mysql-test/main/derived_split_innodb.result index cf5076724d1..894384d5bfc 100644 --- a/mysql-test/main/derived_split_innodb.result +++ b/mysql-test/main/derived_split_innodb.result @@ -848,4 +848,61 @@ SELECT * FROM t1 WHERE t1.a IN (SELECT b FROM (SELECT t2.b FROM t2 WHERE NOT EXISTS (SELECT 1 FROM t3) GROUP BY b) sq); a DROP TABLE t1, t2, t3; +# +# MDEV-23878: Wrong result with semi-join and splittable derived table +# +CREATE TABLE t1 ( +groupId int, +id int unsigned, +PRIMARY KEY (groupId, id) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; +INSERT INTO t1 VALUES +(8,1),(8,2),(8,3),(8,4),(8,5),(8,6),(8,7),(8,8),(8,9),(8,10), +(8,11),(8,12),(8,13),(8,14),(8,15),(8,16),(8,17),(8,18),(8,19); +set statement in_predicate_conversion_threshold=2 for SELECT COUNT(*) AS cnt FROM t1 +JOIN +( +SELECT groupId, id +FROM t1 +WHERE id IN (1,2,3,4,5,6) +GROUP BY groupId, id +) AS t2 +USING (groupId, id) +WHERE id IN (1,2,3,4,5,6,7,8); +cnt +6 +set statement in_predicate_conversion_threshold=2 for EXPLAIN SELECT COUNT(*) AS cnt FROM t1 +JOIN +( +SELECT groupId, id +FROM t1 +WHERE id IN (1,2,3,4,5,6) +GROUP BY groupId, id +) AS t2 +USING (groupId, id) +WHERE id IN (1,2,3,4,5,6,7,8); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 index PRIMARY PRIMARY 8 NULL 19 Using index +1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 Using where +1 PRIMARY ref key0 key0 8 test.t1.groupId,test.t1.id 2 +4 MATERIALIZED ALL NULL NULL NULL NULL 8 +5 DERIVED NULL NULL NULL NULL NULL NULL NULL No tables used +3 LATERAL DERIVED t1 eq_ref PRIMARY PRIMARY 8 test.t1.groupId,test.t1.id 1 Using index +3 LATERAL DERIVED ref key0 key0 4 test.t1.id 2 Using where; FirstMatch(t1) +7 DERIVED NULL NULL NULL NULL NULL NULL NULL No tables used +set statement optimizer_switch='split_materialized=off, loosescan=off' for +set statement in_predicate_conversion_threshold=2 for +SELECT COUNT(*) AS cnt FROM t1 +JOIN +( +SELECT groupId, id +FROM t1 +WHERE id IN (1,2,3,4,5,6) +GROUP BY groupId, id +) AS t2 +USING (groupId, id) +WHERE id IN (1,2,3,4,5,6,7,8); +cnt +6 +DROP TABLE t1; # End of 10.4 tests diff --git a/mysql-test/main/derived_split_innodb.test b/mysql-test/main/derived_split_innodb.test index 0909e3de9a6..d7756cb8fc5 100644 --- a/mysql-test/main/derived_split_innodb.test +++ b/mysql-test/main/derived_split_innodb.test @@ -503,4 +503,46 @@ SELECT * FROM t1 WHERE t1.a IN (SELECT b FROM DROP TABLE t1, t2, t3; +--echo # +--echo # MDEV-23878: Wrong result with semi-join and splittable derived table +--echo # + +CREATE TABLE t1 ( + groupId int, + id int unsigned, + PRIMARY KEY (groupId, id) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +INSERT INTO t1 VALUES + (8,1),(8,2),(8,3),(8,4),(8,5),(8,6),(8,7),(8,8),(8,9),(8,10), + (8,11),(8,12),(8,13),(8,14),(8,15),(8,16),(8,17),(8,18),(8,19); + +let $query= +SELECT COUNT(*) AS cnt FROM t1 +JOIN +( + SELECT groupId, id + FROM t1 + WHERE id IN (1,2,3,4,5,6) + GROUP BY groupId, id +) AS t2 +USING (groupId, id) +WHERE id IN (1,2,3,4,5,6,7,8); + +let $tvc_conversion_threshold = + set statement in_predicate_conversion_threshold=2 for; + +eval $tvc_conversion_threshold $query; +eval $tvc_conversion_threshold EXPLAIN $query; + +let $no_split_materialized_loosescan= + set statement optimizer_switch='split_materialized=off, loosescan=off' for; + +# Correct result with split materializied optimization disabled +eval $no_split_materialized_loosescan + $tvc_conversion_threshold + $query; + +DROP TABLE t1; + --echo # End of 10.4 tests diff --git a/sql/opt_split.cc b/sql/opt_split.cc index 63d430384b8..0d479bbda3e 100644 --- a/sql/opt_split.cc +++ b/sql/opt_split.cc @@ -1332,6 +1332,7 @@ bool JOIN::fix_all_splittings_in_plan() { table_map prev_tables= 0; table_map all_tables= (table_map(1) << table_count) - 1; + table_map prev_sjm_lookup_tables= 0; for (uint tablenr= 0; tablenr < table_count; tablenr++) { POSITION *cur_pos= &best_positions[tablenr]; @@ -1340,7 +1341,7 @@ bool JOIN::fix_all_splittings_in_plan() { SplM_plan_info *spl_plan= cur_pos->spl_plan; table_map excluded_tables= (all_tables & ~prev_tables) | - sjm_lookup_tables; + prev_sjm_lookup_tables; ; if (spl_plan) { @@ -1358,6 +1359,8 @@ bool JOIN::fix_all_splittings_in_plan() return true; } prev_tables|= tab->table->map; + if (cur_pos->sj_strategy == SJ_OPT_MATERIALIZE) + prev_sjm_lookup_tables|= tab->table->map; } return false; }