From c03885cd9ceb1ede7f49a9e218022b401b3a1e28 Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Fri, 9 Oct 2020 17:48:22 +0300 Subject: [PATCH] MDEV-22761: innodb row_search_idx_cond_check handle CHECK_ABORTED_BY_USER Part #2: - row_search_mvcc() should return DB_INTERRUPTED when it got - Move the sync point from innodb internals to handler_rowid_filter_check() where other storage engines can use it too - Add a similar syncpoint for the ICP check. - Add a bigger test and test coverage for Rowid Filter with MyISAM - Add test coverage for killed-during-ICP-check scenario --- mysql-test/include/icp_debug_kill.inc | 50 ++++++++++++++ .../include/rowid_filter_debug_kill.inc | 68 +++++++++++++++++++ mysql-test/main/aria_icp_debug.result | 34 ++++++++++ mysql-test/main/aria_icp_debug.test | 5 ++ mysql-test/main/innodb_icp_debug.result | 34 ++++++++++ mysql-test/main/innodb_icp_debug.test | 6 ++ mysql-test/main/myisam_icp_debug.result | 32 +++++++++ mysql-test/main/myisam_icp_debug.test | 1 + .../main/rowid_filter_innodb_debug.result | 61 ++++++++++++++++- .../main/rowid_filter_innodb_debug.test | 11 ++- .../main/rowid_filter_myisam_debug.result | 54 +++++++++++++++ .../main/rowid_filter_myisam_debug.test | 3 + sql/handler.cc | 2 + storage/innobase/row/row0sel.cc | 6 +- 14 files changed, 359 insertions(+), 8 deletions(-) create mode 100644 mysql-test/include/icp_debug_kill.inc create mode 100644 mysql-test/include/rowid_filter_debug_kill.inc create mode 100644 mysql-test/main/aria_icp_debug.result create mode 100644 mysql-test/main/aria_icp_debug.test create mode 100644 mysql-test/main/innodb_icp_debug.result create mode 100644 mysql-test/main/innodb_icp_debug.test create mode 100644 mysql-test/main/myisam_icp_debug.result create mode 100644 mysql-test/main/myisam_icp_debug.test create mode 100644 mysql-test/main/rowid_filter_myisam_debug.result create mode 100644 mysql-test/main/rowid_filter_myisam_debug.test diff --git a/mysql-test/include/icp_debug_kill.inc b/mysql-test/include/icp_debug_kill.inc new file mode 100644 index 0000000000000..d0ecc8428694c --- /dev/null +++ b/mysql-test/include/icp_debug_kill.inc @@ -0,0 +1,50 @@ +--source include/have_debug.inc +--source include/have_debug_sync.inc +--source include/count_sessions.inc + +--disable_warnings +drop table if exists t0,t1,t2; +--enable_warnings + +create table t0(a int primary key); +insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); + +create table t1(a int primary key); +insert into t1 select A.a + B.a* 10 + C.a * 100 from t0 A, t0 B, t0 C; + +create table t2 ( + kp1 int, + kp2 int, + col char(100), + key(kp1, kp2) +); +insert into t2 select a, a, a from t1; + +select engine from information_schema.tables +where table_schema=database() and table_name='t2'; + +explain +select * from t2 where kp1 between 10 and 20 and kp2 +1 >100; + +let $target_id= `select connection_id()`; + +set debug_sync='handler_index_cond_check SIGNAL at_icp_check WAIT_FOR go'; +send +select * from t2 where kp1 between 10 and 20 and kp2 +1 >100; + +connect (con1, localhost, root,,); +connection con1; +set debug_sync='now WAIT_FOR at_icp_check'; +evalp kill query $target_id; +set debug_sync='now SIGNAL go'; + +connection default; + +--error ER_QUERY_INTERRUPTED +reap; +set debug_sync='RESET'; + +disconnect con1; +drop table t0,t1,t2; +--source include/wait_until_count_sessions.inc + diff --git a/mysql-test/include/rowid_filter_debug_kill.inc b/mysql-test/include/rowid_filter_debug_kill.inc new file mode 100644 index 0000000000000..6a8c5d3f70d2e --- /dev/null +++ b/mysql-test/include/rowid_filter_debug_kill.inc @@ -0,0 +1,68 @@ +--source include/have_debug.inc +--source include/have_debug_sync.inc +--source include/count_sessions.inc + +--echo # +--echo # MDEV-22761 KILL QUERY during rowid_filter, crashes +--echo # + +create table t0(a int); +insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); + +create table t1(a int); +insert into t1 select A.a + B.a* 10 + C.a * 100 from t0 A, t0 B, t0 C; + +# 100 rows +create table t2(a int); +insert into t2 select A.a + B.a* 10 from t0 A, t0 B; + +# 10K rows +CREATE TABLE t3 ( + key1 int , + key2 int, + filler varchar(255), + KEY (key1), + KEY (key2) +); +select engine from information_schema.tables +where table_schema=database() and table_name='t3'; + +insert into t3 +select + A.a, + A.a, + 'filler-data-filler-data' +from + t0 A, t1 B; + +analyze table t2,t3; + +explain +select * from t2, t3 +where + t3.key1=t2.a and t3.key2 in (2,3); + +let $target_id= `select connection_id()`; + +set debug_sync='handler_rowid_filter_check SIGNAL at_rowid_filter_check WAIT_FOR go'; +send +select * from t2, t3 +where + t3.key1=t2.a and t3.key2 in (2,3); + +connect (con1, localhost, root,,); +connection con1; +set debug_sync='now WAIT_FOR at_rowid_filter_check'; +evalp kill query $target_id; +set debug_sync='now SIGNAL go'; + +connection default; +disconnect con1; + +--error ER_QUERY_INTERRUPTED +reap; +set debug_sync='RESET'; + +drop table t0,t1,t2,t3; +--source include/wait_until_count_sessions.inc + diff --git a/mysql-test/main/aria_icp_debug.result b/mysql-test/main/aria_icp_debug.result new file mode 100644 index 0000000000000..fc01ee4fb3bb9 --- /dev/null +++ b/mysql-test/main/aria_icp_debug.result @@ -0,0 +1,34 @@ +set default_storage_engine=aria; +drop table if exists t0,t1,t2; +create table t0(a int primary key); +insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); +create table t1(a int primary key); +insert into t1 select A.a + B.a* 10 + C.a * 100 from t0 A, t0 B, t0 C; +create table t2 ( +kp1 int, +kp2 int, +col char(100), +key(kp1, kp2) +); +insert into t2 select a, a, a from t1; +select engine from information_schema.tables +where table_schema=database() and table_name='t2'; +engine +Aria +explain +select * from t2 where kp1 between 10 and 20 and kp2 +1 >100; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 range kp1 kp1 5 NULL 10 Using index condition +set debug_sync='handler_index_cond_check SIGNAL at_icp_check WAIT_FOR go'; +select * from t2 where kp1 between 10 and 20 and kp2 +1 >100; +connect con1, localhost, root,,; +connection con1; +set debug_sync='now WAIT_FOR at_icp_check'; +kill query $target_id; +set debug_sync='now SIGNAL go'; +connection default; +ERROR 70100: Query execution was interrupted +set debug_sync='RESET'; +disconnect con1; +drop table t0,t1,t2; +set default_storage_engine=default; diff --git a/mysql-test/main/aria_icp_debug.test b/mysql-test/main/aria_icp_debug.test new file mode 100644 index 0000000000000..bbdd59d601200 --- /dev/null +++ b/mysql-test/main/aria_icp_debug.test @@ -0,0 +1,5 @@ + +set default_storage_engine=aria; +--source include/icp_debug_kill.inc +set default_storage_engine=default; + diff --git a/mysql-test/main/innodb_icp_debug.result b/mysql-test/main/innodb_icp_debug.result new file mode 100644 index 0000000000000..5a169650c8eea --- /dev/null +++ b/mysql-test/main/innodb_icp_debug.result @@ -0,0 +1,34 @@ +set default_storage_engine=innodb; +drop table if exists t0,t1,t2; +create table t0(a int primary key); +insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); +create table t1(a int primary key); +insert into t1 select A.a + B.a* 10 + C.a * 100 from t0 A, t0 B, t0 C; +create table t2 ( +kp1 int, +kp2 int, +col char(100), +key(kp1, kp2) +); +insert into t2 select a, a, a from t1; +select engine from information_schema.tables +where table_schema=database() and table_name='t2'; +engine +InnoDB +explain +select * from t2 where kp1 between 10 and 20 and kp2 +1 >100; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 range kp1 kp1 5 NULL 11 Using index condition +set debug_sync='handler_index_cond_check SIGNAL at_icp_check WAIT_FOR go'; +select * from t2 where kp1 between 10 and 20 and kp2 +1 >100; +connect con1, localhost, root,,; +connection con1; +set debug_sync='now WAIT_FOR at_icp_check'; +kill query $target_id; +set debug_sync='now SIGNAL go'; +connection default; +ERROR 70100: Query execution was interrupted +set debug_sync='RESET'; +disconnect con1; +drop table t0,t1,t2; +set default_storage_engine=default; diff --git a/mysql-test/main/innodb_icp_debug.test b/mysql-test/main/innodb_icp_debug.test new file mode 100644 index 0000000000000..4aa08c61e87e6 --- /dev/null +++ b/mysql-test/main/innodb_icp_debug.test @@ -0,0 +1,6 @@ +--source include/have_innodb.inc + +set default_storage_engine=innodb; +--source include/icp_debug_kill.inc +set default_storage_engine=default; + diff --git a/mysql-test/main/myisam_icp_debug.result b/mysql-test/main/myisam_icp_debug.result new file mode 100644 index 0000000000000..cb45a0e227438 --- /dev/null +++ b/mysql-test/main/myisam_icp_debug.result @@ -0,0 +1,32 @@ +drop table if exists t0,t1,t2; +create table t0(a int primary key); +insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); +create table t1(a int primary key); +insert into t1 select A.a + B.a* 10 + C.a * 100 from t0 A, t0 B, t0 C; +create table t2 ( +kp1 int, +kp2 int, +col char(100), +key(kp1, kp2) +); +insert into t2 select a, a, a from t1; +select engine from information_schema.tables +where table_schema=database() and table_name='t2'; +engine +MyISAM +explain +select * from t2 where kp1 between 10 and 20 and kp2 +1 >100; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 range kp1 kp1 5 NULL 11 Using index condition +set debug_sync='handler_index_cond_check SIGNAL at_icp_check WAIT_FOR go'; +select * from t2 where kp1 between 10 and 20 and kp2 +1 >100; +connect con1, localhost, root,,; +connection con1; +set debug_sync='now WAIT_FOR at_icp_check'; +kill query $target_id; +set debug_sync='now SIGNAL go'; +connection default; +ERROR 70100: Query execution was interrupted +set debug_sync='RESET'; +disconnect con1; +drop table t0,t1,t2; diff --git a/mysql-test/main/myisam_icp_debug.test b/mysql-test/main/myisam_icp_debug.test new file mode 100644 index 0000000000000..d5b1af799fa14 --- /dev/null +++ b/mysql-test/main/myisam_icp_debug.test @@ -0,0 +1 @@ +--source include/icp_debug_kill.inc diff --git a/mysql-test/main/rowid_filter_innodb_debug.result b/mysql-test/main/rowid_filter_innodb_debug.result index 91fa66937b62c..6fd75294bdb54 100644 --- a/mysql-test/main/rowid_filter_innodb_debug.result +++ b/mysql-test/main/rowid_filter_innodb_debug.result @@ -1,3 +1,59 @@ +set default_storage_engine=innodb; +# +# MDEV-22761 KILL QUERY during rowid_filter, crashes +# +create table t0(a int); +insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); +create table t1(a int); +insert into t1 select A.a + B.a* 10 + C.a * 100 from t0 A, t0 B, t0 C; +create table t2(a int); +insert into t2 select A.a + B.a* 10 from t0 A, t0 B; +CREATE TABLE t3 ( +key1 int , +key2 int, +filler varchar(255), +KEY (key1), +KEY (key2) +); +select engine from information_schema.tables +where table_schema=database() and table_name='t3'; +engine +InnoDB +insert into t3 +select +A.a, +A.a, +'filler-data-filler-data' +from +t0 A, t1 B; +analyze table t2,t3; +Table Op Msg_type Msg_text +test.t2 analyze status Engine-independent statistics collected +test.t2 analyze status OK +test.t3 analyze status Engine-independent statistics collected +test.t3 analyze status OK +explain +select * from t2, t3 +where +t3.key1=t2.a and t3.key2 in (2,3); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 100 Using where +1 SIMPLE t3 ref|filter key1,key2 key1|key2 5|5 test.t2.a 1000 (20%) Using where; Using rowid filter +set debug_sync='handler_rowid_filter_check SIGNAL at_rowid_filter_check WAIT_FOR go'; +select * from t2, t3 +where +t3.key1=t2.a and t3.key2 in (2,3); +connect con1, localhost, root,,; +connection con1; +set debug_sync='now WAIT_FOR at_rowid_filter_check'; +kill query $target_id; +set debug_sync='now SIGNAL go'; +connection default; +disconnect con1; +ERROR 70100: Query execution was interrupted +set debug_sync='RESET'; +drop table t0,t1,t2,t3; +set default_storage_engine=default; set @save_optimizer_switch= @@optimizer_switch; set @save_use_stat_tables= @@use_stat_tables; set @save_optimizer_use_condition_selectivity= @@optimizer_use_condition_selectivity; @@ -6,10 +62,11 @@ set optimizer_use_condition_selectivity=2; set optimizer_switch='rowid_filter=on'; # # MDEV-22761 KILL QUERY during rowid_filter, crashes +# (The smaller testcase) # CREATE TABLE t1 (a INT, b INT, INDEX(a), INDEX(b)) ENGINE=InnoDB; INSERT INTO t1 VALUES (0,0),(1,0),(-1,1), (-2,1), (-2,3), (-3,4), (-2,4); -set debug_sync='row_search_pre_rowid_filter_check SIGNAL killme WAIT_FOR go'; +set debug_sync='handler_rowid_filter_check SIGNAL killme WAIT_FOR go'; SELECT * FROM t1 WHERE a > 0 AND b=0; connect con1, localhost, root,,; connection con1; @@ -17,7 +74,7 @@ set debug_sync='now WAIT_FOR killme'; kill query @id; set debug_sync='now SIGNAL go'; connection default; -a b +ERROR 70100: Query execution was interrupted set debug_sync='RESET'; disconnect con1; drop table t1; diff --git a/mysql-test/main/rowid_filter_innodb_debug.test b/mysql-test/main/rowid_filter_innodb_debug.test index 3f8eb5b5c38fd..eb43edd9e9e6a 100644 --- a/mysql-test/main/rowid_filter_innodb_debug.test +++ b/mysql-test/main/rowid_filter_innodb_debug.test @@ -1,6 +1,9 @@ ---source include/have_debug.inc ---source include/have_debug_sync.inc --source include/have_innodb.inc + +set default_storage_engine=innodb; +--source include/rowid_filter_debug_kill.inc +set default_storage_engine=default; + --source include/default_optimizer_switch.inc --source include/count_sessions.inc @@ -15,6 +18,7 @@ set optimizer_switch='rowid_filter=on'; --echo # --echo # MDEV-22761 KILL QUERY during rowid_filter, crashes +--echo # (The smaller testcase) --echo # CREATE TABLE t1 (a INT, b INT, INDEX(a), INDEX(b)) ENGINE=InnoDB; @@ -22,7 +26,7 @@ INSERT INTO t1 VALUES (0,0),(1,0),(-1,1), (-2,1), (-2,3), (-3,4), (-2,4); let $ID= `SELECT @id := CONNECTION_ID()`; -set debug_sync='row_search_pre_rowid_filter_check SIGNAL killme WAIT_FOR go'; +set debug_sync='handler_rowid_filter_check SIGNAL killme WAIT_FOR go'; send SELECT * FROM t1 WHERE a > 0 AND b=0; connect (con1, localhost, root,,); @@ -33,6 +37,7 @@ kill query @id; set debug_sync='now SIGNAL go'; connection default; +--error ER_QUERY_INTERRUPTED reap; set debug_sync='RESET'; diff --git a/mysql-test/main/rowid_filter_myisam_debug.result b/mysql-test/main/rowid_filter_myisam_debug.result new file mode 100644 index 0000000000000..16fcb2a416ea1 --- /dev/null +++ b/mysql-test/main/rowid_filter_myisam_debug.result @@ -0,0 +1,54 @@ +# +# MDEV-22761 KILL QUERY during rowid_filter, crashes +# +create table t0(a int); +insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); +create table t1(a int); +insert into t1 select A.a + B.a* 10 + C.a * 100 from t0 A, t0 B, t0 C; +create table t2(a int); +insert into t2 select A.a + B.a* 10 from t0 A, t0 B; +CREATE TABLE t3 ( +key1 int , +key2 int, +filler varchar(255), +KEY (key1), +KEY (key2) +); +select engine from information_schema.tables +where table_schema=database() and table_name='t3'; +engine +MyISAM +insert into t3 +select +A.a, +A.a, +'filler-data-filler-data' +from +t0 A, t1 B; +analyze table t2,t3; +Table Op Msg_type Msg_text +test.t2 analyze status Engine-independent statistics collected +test.t2 analyze status OK +test.t3 analyze status Engine-independent statistics collected +test.t3 analyze status Table is already up to date +explain +select * from t2, t3 +where +t3.key1=t2.a and t3.key2 in (2,3); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 100 Using where +1 SIMPLE t3 ref|filter key1,key2 key1|key2 5|5 test.t2.a 1000 (18%) Using where; Using rowid filter +set debug_sync='handler_rowid_filter_check SIGNAL at_rowid_filter_check WAIT_FOR go'; +select * from t2, t3 +where +t3.key1=t2.a and t3.key2 in (2,3); +connect con1, localhost, root,,; +connection con1; +set debug_sync='now WAIT_FOR at_rowid_filter_check'; +kill query $target_id; +set debug_sync='now SIGNAL go'; +connection default; +disconnect con1; +ERROR 70100: Query execution was interrupted +set debug_sync='RESET'; +drop table t0,t1,t2,t3; diff --git a/mysql-test/main/rowid_filter_myisam_debug.test b/mysql-test/main/rowid_filter_myisam_debug.test new file mode 100644 index 0000000000000..ba2f79772a9de --- /dev/null +++ b/mysql-test/main/rowid_filter_myisam_debug.test @@ -0,0 +1,3 @@ + +--source include/rowid_filter_debug_kill.inc + diff --git a/sql/handler.cc b/sql/handler.cc index d7562bcd2aeb2..6dc17109ffd6a 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6015,6 +6015,7 @@ extern "C" check_result_t handler_index_cond_check(void* h_arg) THD *thd= h->table->in_use; check_result_t res; + DEBUG_SYNC(thd, "handler_index_cond_check"); enum thd_kill_levels abort_at= h->has_transactions() ? THD_ABORT_SOFTLY : THD_ABORT_ASAP; if (thd_kill_level(thd) > abort_at) @@ -6048,6 +6049,7 @@ check_result_t handler_rowid_filter_check(void *h_arg) if (!h->pushed_idx_cond) { THD *thd= h->table->in_use; + DEBUG_SYNC(thd, "handler_rowid_filter_check"); enum thd_kill_levels abort_at= h->has_transactions() ? THD_ABORT_SOFTLY : THD_ABORT_ASAP; if (thd_kill_level(thd) > abort_at) diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc index 7f1362cb9316e..941a49040472e 100644 --- a/storage/innobase/row/row0sel.cc +++ b/storage/innobase/row/row0sel.cc @@ -3975,8 +3975,6 @@ row_search_idx_cond_check( ut_ad(len == DATA_ROW_ID_LEN); memcpy(prebuilt->row_id, data, DATA_ROW_ID_LEN); } - DEBUG_SYNC_C("row_search_pre_rowid_filter_check"); - result = handler_rowid_filter_check(prebuilt->pk_filter); switch (result) { case CHECK_NEG: @@ -5301,8 +5299,10 @@ row_search_mvcc( row_unlock_for_mysql(prebuilt, TRUE); } goto next_rec; - case CHECK_OUT_OF_RANGE: case CHECK_ABORTED_BY_USER: + err = DB_INTERRUPTED; + goto idx_cond_failed; + case CHECK_OUT_OF_RANGE: case CHECK_ERROR: err = DB_RECORD_NOT_FOUND; goto idx_cond_failed;