From d61bc94fa02c76fc3d9b0acf22d59377fc4a5d0a Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Tue, 21 Feb 2023 12:04:37 +0300 Subject: [PATCH] MDEV-30659 Server crash on EXPLAIN SELECT/SELECT on table with engine Aria for LooseScan Strategy Amended patch from Monty: The issue was that Loose_scan_opt::save_to_position() did not take into account records_out from best_access_path() Make sure that POSITION object filled by Loose_scan_opt::save_to_position() has records_out not higher than any other possible access method. --- mysql-test/main/selectivity.result | 54 +++++++++++++++++++++++ mysql-test/main/selectivity.test | 51 +++++++++++++++++++++ mysql-test/main/selectivity_innodb.result | 54 +++++++++++++++++++++++ sql/opt_subselect.h | 13 +++++- sql/sql_select.cc | 8 ++-- sql/sql_select.h | 1 + 6 files changed, 175 insertions(+), 6 deletions(-) diff --git a/mysql-test/main/selectivity.result b/mysql-test/main/selectivity.result index e8ec0c30e80..6bd8fdf8d2b 100644 --- a/mysql-test/main/selectivity.result +++ b/mysql-test/main/selectivity.result @@ -2011,3 +2011,57 @@ INSERT INTO t2 VALUES (1),(2); SELECT STRAIGHT_JOIN pk FROM t1 JOIN t2 ON a = pk WHERE b >= 'A' ORDER BY t2.pk LIMIT 8 OFFSET 1; pk DROP TABLE t1, t2; +# +# MDEV-30659 Server crash on EXPLAIN SELECT/SELECT on table with +# engine Aria for LooseScan Strategy +# +create table t1 (old_c1 integer, old_c2 integer, c1 integer, +c2 integer, c3 integer) engine=aria; +insert into t1(c1,c2,c3) +values (1,1,1), (1,2,2), (1,3,3), +(2,1,4), (2,2,5), (2,3,6), +(2,4,7), (2,5,8); +create index t1_c2 on t1 (c2,c1); +explain select * from t1 where t1.c2 in (select a.c2 from t1 a) and +c2 >= 3 order by c2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY a range t1_c2 t1_c2 5 NULL 5 Using where; Using index; LooseScan +1 PRIMARY t1 ref t1_c2 t1_c2 5 test.a.c2 1 +drop table t1; +create table t1 (old_c1 integer, old_c2 integer, c1 integer, +c2 integer, c3 integer) engine=aria; +create trigger trg_t1 before update on t1 for each row +begin +set new.old_c1=old.c1; +set new.old_c2=old.c2; +end; +/ +insert into t1 (c1,c2,c3) values +(1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8); +create index t1_c2 on t1 (c2,c1); +analyze table t1 persistent for all; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +create table t2 as select * from t1; +truncate table t1; +insert into t1 select * from t2; +explain select * from t1 where t1.c2 in (select a.c2 from t1 a) and c2 >= 3 order by c2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY a range t1_c2 t1_c2 5 NULL 5 Using where; Using index; LooseScan +1 PRIMARY t1 ref t1_c2 t1_c2 5 test.a.c2 1 +drop trigger trg_t1; +drop table t1,t2; +create table t1 (old_c1 integer, old_c2 integer, c1 integer, +c2 integer, c3 integer) engine=aria; +insert into t1 (c1,c2,c3) values +(1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8); +create index t1_c2 on t1 (c2,c1); +create table t2 as select * from t1; +truncate table t1; +insert into t1 select * from t2; +explain select * from t1 where t1.c2 in (select a.c2 from t1 a) and c2 >= 3 order by c2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY a range t1_c2 t1_c2 5 NULL 5 Using where; Using index; LooseScan +1 PRIMARY t1 ref t1_c2 t1_c2 5 test.a.c2 2 +drop table t1,t2; diff --git a/mysql-test/main/selectivity.test b/mysql-test/main/selectivity.test index d775f8e4370..6957773fc8e 100644 --- a/mysql-test/main/selectivity.test +++ b/mysql-test/main/selectivity.test @@ -1376,3 +1376,54 @@ INSERT INTO t2 VALUES (1),(2); SELECT STRAIGHT_JOIN pk FROM t1 JOIN t2 ON a = pk WHERE b >= 'A' ORDER BY t2.pk LIMIT 8 OFFSET 1; DROP TABLE t1, t2; + +--echo # +--echo # MDEV-30659 Server crash on EXPLAIN SELECT/SELECT on table with +--echo # engine Aria for LooseScan Strategy +--echo # + +create table t1 (old_c1 integer, old_c2 integer, c1 integer, + c2 integer, c3 integer) engine=aria; +insert into t1(c1,c2,c3) + values (1,1,1), (1,2,2), (1,3,3), + (2,1,4), (2,2,5), (2,3,6), + (2,4,7), (2,5,8); +create index t1_c2 on t1 (c2,c1); + +explain select * from t1 where t1.c2 in (select a.c2 from t1 a) and +c2 >= 3 order by c2; +drop table t1; + +create table t1 (old_c1 integer, old_c2 integer, c1 integer, + c2 integer, c3 integer) engine=aria; + +delimiter /; +create trigger trg_t1 before update on t1 for each row +begin + set new.old_c1=old.c1; + set new.old_c2=old.c2; +end; +/ +delimiter ;/ + +insert into t1 (c1,c2,c3) values + (1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8); +create index t1_c2 on t1 (c2,c1); +analyze table t1 persistent for all; +create table t2 as select * from t1; +truncate table t1; +insert into t1 select * from t2; +explain select * from t1 where t1.c2 in (select a.c2 from t1 a) and c2 >= 3 order by c2; +drop trigger trg_t1; +drop table t1,t2; + +create table t1 (old_c1 integer, old_c2 integer, c1 integer, + c2 integer, c3 integer) engine=aria; +insert into t1 (c1,c2,c3) values + (1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8); +create index t1_c2 on t1 (c2,c1); +create table t2 as select * from t1; +truncate table t1; +insert into t1 select * from t2; +explain select * from t1 where t1.c2 in (select a.c2 from t1 a) and c2 >= 3 order by c2; +drop table t1,t2; diff --git a/mysql-test/main/selectivity_innodb.result b/mysql-test/main/selectivity_innodb.result index 274ba62329d..965a1ffd117 100644 --- a/mysql-test/main/selectivity_innodb.result +++ b/mysql-test/main/selectivity_innodb.result @@ -2018,6 +2018,60 @@ INSERT INTO t2 VALUES (1),(2); SELECT STRAIGHT_JOIN pk FROM t1 JOIN t2 ON a = pk WHERE b >= 'A' ORDER BY t2.pk LIMIT 8 OFFSET 1; pk DROP TABLE t1, t2; +# +# MDEV-30659 Server crash on EXPLAIN SELECT/SELECT on table with +# engine Aria for LooseScan Strategy +# +create table t1 (old_c1 integer, old_c2 integer, c1 integer, +c2 integer, c3 integer) engine=aria; +insert into t1(c1,c2,c3) +values (1,1,1), (1,2,2), (1,3,3), +(2,1,4), (2,2,5), (2,3,6), +(2,4,7), (2,5,8); +create index t1_c2 on t1 (c2,c1); +explain select * from t1 where t1.c2 in (select a.c2 from t1 a) and +c2 >= 3 order by c2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY a range t1_c2 t1_c2 5 NULL 5 Using where; Using index; LooseScan +1 PRIMARY t1 ref t1_c2 t1_c2 5 test.a.c2 1 +drop table t1; +create table t1 (old_c1 integer, old_c2 integer, c1 integer, +c2 integer, c3 integer) engine=aria; +create trigger trg_t1 before update on t1 for each row +begin +set new.old_c1=old.c1; +set new.old_c2=old.c2; +end; +/ +insert into t1 (c1,c2,c3) values +(1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8); +create index t1_c2 on t1 (c2,c1); +analyze table t1 persistent for all; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +create table t2 as select * from t1; +truncate table t1; +insert into t1 select * from t2; +explain select * from t1 where t1.c2 in (select a.c2 from t1 a) and c2 >= 3 order by c2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY a range t1_c2 t1_c2 5 NULL 5 Using where; Using index; LooseScan +1 PRIMARY t1 ref t1_c2 t1_c2 5 test.a.c2 1 +drop trigger trg_t1; +drop table t1,t2; +create table t1 (old_c1 integer, old_c2 integer, c1 integer, +c2 integer, c3 integer) engine=aria; +insert into t1 (c1,c2,c3) values +(1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8); +create index t1_c2 on t1 (c2,c1); +create table t2 as select * from t1; +truncate table t1; +insert into t1 select * from t2; +explain select * from t1 where t1.c2 in (select a.c2 from t1 a) and c2 >= 3 order by c2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY a range t1_c2 t1_c2 5 NULL 5 Using where; Using index; LooseScan +1 PRIMARY t1 ref t1_c2 t1_c2 5 test.a.c2 2 +drop table t1,t2; set optimizer_switch=@save_optimizer_switch_for_selectivity_test; set @tmp_ust= @@use_stat_tables; set @tmp_oucs= @@optimizer_use_condition_selectivity; diff --git a/sql/opt_subselect.h b/sql/opt_subselect.h index 1f121855d93..c0398fc8539 100644 --- a/sql/opt_subselect.h +++ b/sql/opt_subselect.h @@ -294,14 +294,23 @@ public: } } - void save_to_position(JOIN_TAB *tab, double record_count, POSITION *pos) + void save_to_position(JOIN_TAB *tab, double record_count, + double records_out, + POSITION *pos) { pos->read_time= best_loose_scan_cost; if (best_loose_scan_cost != DBL_MAX) { + /* + Make sure LooseScan plan doesn't produce more rows than + the records_out of other table access method. + */ + set_if_smaller(best_loose_scan_records, records_out); + pos->loops= record_count; pos->records_read= best_loose_scan_records; - pos->records_init= pos->records_out= pos->records_read; + pos->records_init= pos->records_read; + pos->records_out= best_loose_scan_records; pos->key= best_loose_scan_start_key; pos->cond_selectivity= 1.0; pos->loosescan_picker.loosescan_key= best_loose_scan_key; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index ccec6f1215b..48b6f9e2dfc 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -8155,7 +8155,6 @@ best_access_path(JOIN *join, best.uses_jbuf= FALSE; best.spl_plan= 0; - pos->loops= record_count; disable_jbuf= disable_jbuf || idx == join->const_tables; trace_wrapper.add_table_name(s); @@ -9282,6 +9281,7 @@ best_access_path(JOIN *join, crash_if_first_double_is_bigger(best.records_out, best.records_read); /* Update the cost information for the current partial plan */ + pos->loops= record_count; pos->records_init= best.records_read; pos->records_after_filter= best.records_after_filter; pos->records_read= best.records; @@ -9299,7 +9299,8 @@ best_access_path(JOIN *join, pos->key_dependent= (best.type == JT_EQ_REF ? (table_map) 0 : key_dependent & remaining_tables); - loose_scan_opt.save_to_position(s, record_count, loose_scan_pos); + loose_scan_opt.save_to_position(s, record_count, pos->records_out, + loose_scan_pos); if (!best.key && idx == join->const_tables && // First table @@ -30525,9 +30526,8 @@ static bool get_range_limit_read_cost(const POSITION *pos, cond_selectivity= best_rows / range_rows; else cond_selectivity= 1.0; -#if 0 // FIXME: cond_selectivity=8/4 = 2 in main.update_use_source + DBUG_ASSERT(cond_selectivity <= 1.000000001); -#endif set_if_smaller(cond_selectivity, 1.0); /* diff --git a/sql/sql_select.h b/sql/sql_select.h index a45c4b8ffc2..032437defc0 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -1022,6 +1022,7 @@ public: */ double read_time; + /* record combinations before this table */ double loops; double prefix_record_count;