From a18d1cc7778633a9d05f46082e7ddeb8e40130ba Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Tue, 13 Aug 2019 16:26:17 +0300 Subject: [PATCH 01/14] MDEV-20315: MyRocks tests produce valgrind failures (Backport to 10.2) - Include the valgrind suppressions from the FB upstream - Use HAVE_Valgrind, not HAVE_Purify (like the rest of MariaDB code does) The call to DisownData() is now actually disabled under Valgrind --- mysql-test/valgrind.supp | 94 +++++++++++++++++++++++++++++++++++ storage/rocksdb/ha_rocksdb.cc | 4 +- 2 files changed, 96 insertions(+), 2 deletions(-) diff --git a/mysql-test/valgrind.supp b/mysql-test/valgrind.supp index a8ca80e203a..c113447b229 100644 --- a/mysql-test/valgrind.supp +++ b/mysql-test/valgrind.supp @@ -1771,3 +1771,97 @@ obj:/usr/lib64/libcrypto.so* } + + +## +## The following is a copy of facebook/mysql-5.6 suppressions: +## + +# +# RocksDB Storage Engine suppressions start +# + +{ + Still reachable for once-per-process initializations + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN7rocksdb16ThreadStatusUtil19NewColumnFamilyInfoEPKNS_2DBEPKNS_16ColumnFamilyDataERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEPKNS_3EnvE + fun:_ZNK7rocksdb6DBImpl21NewThreadStatusCfInfoEPNS_16ColumnFamilyDataE + fun:_ZN7rocksdb2DB4OpenERKNS_9DBOptionsERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKSt6vectorINS_22ColumnFamilyDescriptorESaISD_EEPSC_IPNS_18ColumnFamilyHandleESaISJ_EEPPS0_ + fun:_ZN7rocksdb13TransactionDB4OpenERKNS_9DBOptionsERKNS_20TransactionDBOptionsERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKSt6vectorINS_22ColumnFamilyDescriptorESaISG_EEPSF_IPNS_18ColumnFamilyHandleESaISM_EEPPS0_ + fun:_ZN7myrocksL17rocksdb_init_funcEPv +} + + +{ + Still reachable for once-per-process initializations + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN7rocksdb19ThreadStatusUpdater14RegisterThreadENS_12ThreadStatus10ThreadTypeEm + fun:_ZN7rocksdb16ThreadStatusUtil14RegisterThreadEPKNS_3EnvENS_12ThreadStatus10ThreadTypeE + fun:_ZN7rocksdb14ThreadPoolImpl4Impl15BGThreadWrapperEPv + fun:_ZNSt12_Bind_simpleIFPFPvS0_EPN7rocksdb16BGThreadMetadataEEE9_M_invokeIILm0EEEES0_St12_Index_tupleIIXspT_EEE + ... +} + +{ + Still reachable for once-per-process initializations + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN7rocksdb14ThreadLocalPtr14InitSingletonsEv + fun:_ZN7rocksdb3Env7DefaultEv + fun:_ZN7rocksdb9DBOptionsC1Ev + ... + fun:_ZN7myrocksL27rdb_init_rocksdb_db_optionsEv +} + +{ + Still reachable for once-per-process initializations + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN7rocksdb12_GLOBAL__N_18PosixEnv* + fun:_ZN7rocksdb3Env7DefaultEv + fun:_ZN7rocksdb9DBOptionsC1Ev + ... + fun:_ZN7myrocksL27rdb_init_rocksdb_db_optionsEv +} + +{ + Still reachable for thread local storage initialization (SetHandle) + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZNSt13unordered_mapIjPFvPvESt4hashIjESt8equal_toIjESaISt4pairIKjS2_EEEixERS8_ + fun:_ZN7rocksdb14ThreadLocalPtr10StaticMeta10SetHandlerEjPFvPvE + fun:_ZN7rocksdb14ThreadLocalPtrC1EPFvPvE + ... +} + +{ + Still reachable for thread local storage initialization (ReclaimId) + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN7rocksdb10autovectorIjLm8EE9push_backERKj + fun:_ZN7rocksdb14ThreadLocalPtr10StaticMeta9ReclaimIdEj + fun:_ZN7rocksdb14ThreadLocalPtrD1Ev + ... +} + +{ + Static initialization + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_Z41__static_initialization_and_destruction_0ii + ... +} + +## +## RocksDB Storage Engine suppressions end +## + diff --git a/storage/rocksdb/ha_rocksdb.cc b/storage/rocksdb/ha_rocksdb.cc index 613ac0884a7..bf9183d7dea 100644 --- a/storage/rocksdb/ha_rocksdb.cc +++ b/storage/rocksdb/ha_rocksdb.cc @@ -5836,11 +5836,11 @@ static int rocksdb_done_func(void *const p) { // Disown the cache data since we're shutting down. // This results in memory leaks but it improved the shutdown time. // Don't disown when running under valgrind -#ifndef HAVE_purify +#ifndef HAVE_valgrind if (rocksdb_tbl_options->block_cache) { rocksdb_tbl_options->block_cache->DisownData(); } -#endif /* HAVE_purify */ +#endif /* HAVE_valgrind */ /* MariaDB: don't clear rocksdb_db_options and rocksdb_tbl_options. From c5b4697b2432d1692227ddf4abb46eb625d94d61 Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Tue, 13 Aug 2019 16:27:51 +0300 Subject: [PATCH 02/14] MDEV-20315: Backport to 10.2: Myrocks: Get the upstream's valgrind suppressions to work --- mysql-test/valgrind.supp | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/mysql-test/valgrind.supp b/mysql-test/valgrind.supp index c113447b229..43f22db69c6 100644 --- a/mysql-test/valgrind.supp +++ b/mysql-test/valgrind.supp @@ -1794,6 +1794,30 @@ } +{ + Still reachable for once-per-process initializations 2 + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN7rocksdb16ThreadStatusUtil19NewColumnFamilyInfoEPKNS_2DBEPKNS_16ColumnFamilyDataERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEPKNS_3EnvE + fun:_ZNK7rocksdb6DBImpl21NewThreadStatusCfInfoEPNS_16ColumnFamilyDataE + fun:_ZN7rocksdb6DBImpl4OpenERKNS_9DBOptionsERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKSt6vectorINS_22ColumnFamilyDescriptorESaISD_EEPSC_IPNS_18ColumnFamilyHandleESaISJ_EEPPNS_2DBEbb + fun:_ZN7rocksdb13TransactionDB4OpenERKNS_9DBOptionsERKNS_20TransactionDBOptionsERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKSt6vectorINS_22ColumnFamilyDescriptorESaISG_EEPSF_IPNS_18ColumnFamilyHandleESaISM_EEPPS0_ +# fun:_ZN7myrocksL17rocksdb_init_funcEPv +# ^ commenting the above out because we are hitting the --num-callers=16 +# limitation that MTR sets for valgrind +} + +{ + Still reachable for once-per-process initializations 3 + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN7rocksdb16ThreadStatusUtil19NewColumnFamilyInfoEPKNS_2DBEPKNS_16ColumnFamilyDataERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEPKNS_3EnvE + fun:_ZNK7rocksdb6DBImpl21NewThreadStatusCfInfoEPNS_16ColumnFamilyDataE + fun:_ZN7rocksdb6DBImpl22CreateColumnFamilyImplERKNS_19ColumnFamilyOptionsERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEPPNS_18ColumnFamilyHandleE +} + { Still reachable for once-per-process initializations Memcheck:Leak @@ -1802,7 +1826,6 @@ fun:_ZN7rocksdb19ThreadStatusUpdater14RegisterThreadENS_12ThreadStatus10ThreadTypeEm fun:_ZN7rocksdb16ThreadStatusUtil14RegisterThreadEPKNS_3EnvENS_12ThreadStatus10ThreadTypeE fun:_ZN7rocksdb14ThreadPoolImpl4Impl15BGThreadWrapperEPv - fun:_ZNSt12_Bind_simpleIFPFPvS0_EPN7rocksdb16BGThreadMetadataEEE9_M_invokeIILm0EEEES0_St12_Index_tupleIIXspT_EEE ... } From 65296123d0fcaeb122bc3b9d9e387468052c06b6 Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Tue, 13 Aug 2019 16:37:21 +0300 Subject: [PATCH 03/14] MDEV-12439: MariaRocks produces numerous (spurious?) valgrind failures Enable the rocksdb test suite. It now passes the valgrind tests. --- storage/rocksdb/mysql-test/rocksdb/suite.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/storage/rocksdb/mysql-test/rocksdb/suite.pm b/storage/rocksdb/mysql-test/rocksdb/suite.pm index 3423d34ded5..e4f074df471 100644 --- a/storage/rocksdb/mysql-test/rocksdb/suite.pm +++ b/storage/rocksdb/mysql-test/rocksdb/suite.pm @@ -20,8 +20,8 @@ my $sst_dump= return "RocksDB is not compiled, no sst_dump" unless $sst_dump; $ENV{MARIAROCKS_SST_DUMP}="$sst_dump"; -# Temporarily disable testing under valgrind, due to MDEV-12439 -return "RocksDB tests disabled under valgrind" if ($::opt_valgrind); +## Temporarily disable testing under valgrind, due to MDEV-12439 +#return "RocksDB tests disabled under valgrind" if ($::opt_valgrind); bless { }; From 2347ffd843b8e4ee9d8eaafab05368435db59ece Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Fri, 9 Aug 2019 00:31:35 +0300 Subject: [PATCH 04/14] MDEV-20301 InnoDB's MVCC has O(N^2) behaviors If there're multiple row versions in InnoDB, reading one row from PK may have O(N) complexity and reading from secondary keys may have O(N^2) complexity. The problem occurs when there are many pending versions of the same row, meaning that the primary key is the same, but a secondary key is different. The slowdown occurs when the secondary index is traversed. This patch creates a helper class for the function row_sel_get_clust_rec_for_mysql() which can remember and re-use cached_clust_rec & cached_old_vers so that rec_get_offsets() does not need to be called over and over for the clustered record. Corrections by Kevin Lewis MDEV-20341 Unstable innodb.innodb_bug14704286 Removed test that tested the ability of interrupting long query which is not long anymore. --- .../suite/innodb/r/innodb_bug14704286.result | 65 ------------- .../suite/innodb/r/innodb_bug84958.result | 81 ++++++++++++++++ .../suite/innodb/t/innodb_bug14704286.test | 95 ------------------- .../suite/innodb/t/innodb_bug84958.test | 86 +++++++++++++++++ storage/innobase/row/row0sel.cc | 55 +++++++++-- 5 files changed, 213 insertions(+), 169 deletions(-) delete mode 100644 mysql-test/suite/innodb/r/innodb_bug14704286.result create mode 100644 mysql-test/suite/innodb/r/innodb_bug84958.result delete mode 100644 mysql-test/suite/innodb/t/innodb_bug14704286.test create mode 100644 mysql-test/suite/innodb/t/innodb_bug84958.test diff --git a/mysql-test/suite/innodb/r/innodb_bug14704286.result b/mysql-test/suite/innodb/r/innodb_bug14704286.result deleted file mode 100644 index f84d5206e07..00000000000 --- a/mysql-test/suite/innodb/r/innodb_bug14704286.result +++ /dev/null @@ -1,65 +0,0 @@ -use test; -drop table if exists t1; -create table t1 (id int primary key, value int, value2 int, -value3 int, index(value,value2)) engine=innodb; -insert into t1 values -(10,10,10,10),(11,11,11,11),(12,12,12,12),(13,13,13,13),(14,14,14,14), -(15,15,15,15),(16,16,16,16),(17,17,17,17),(18,18,18,18),(19,19,19,19), -(20,20,20,20); -connect conn1, localhost, root,,; -connect conn2, localhost, root,,; -connect conn3, localhost, root,,; -connection conn1; -use test; -start transaction with consistent snapshot; -connection conn2; -use test; -CREATE PROCEDURE update_t1() -BEGIN -DECLARE i INT DEFAULT 1; -while (i <= 5000) DO -update test.t1 set value2=value2+1, value3=value3+1 where id=12; -SET i = i + 1; -END WHILE; -END| -set autocommit=0; -CALL update_t1(); -select * from t1; -id value value2 value3 -10 10 10 10 -11 11 11 11 -12 12 5012 5012 -13 13 13 13 -14 14 14 14 -15 15 15 15 -16 16 16 16 -17 17 17 17 -18 18 18 18 -19 19 19 19 -20 20 20 20 -set autocommit=1; -select * from t1; -id value value2 value3 -10 10 10 10 -11 11 11 11 -12 12 5012 5012 -13 13 13 13 -14 14 14 14 -15 15 15 15 -16 16 16 16 -17 17 17 17 -18 18 18 18 -19 19 19 19 -20 20 20 20 -connection conn1; -select * from t1 force index(value) where value=12; -connection conn3; -kill query @id; -connection conn1; -ERROR 70100: Query execution was interrupted -connection default; -disconnect conn1; -disconnect conn2; -disconnect conn3; -drop procedure if exists update_t1; -drop table if exists t1; diff --git a/mysql-test/suite/innodb/r/innodb_bug84958.result b/mysql-test/suite/innodb/r/innodb_bug84958.result new file mode 100644 index 00000000000..1a59a10eb2f --- /dev/null +++ b/mysql-test/suite/innodb/r/innodb_bug84958.result @@ -0,0 +1,81 @@ +# +# Bug #84958 InnoDB's MVCC has O(N^2) behaviors +# https://bugs.mysql.com/bug.php?id=84958 +# +# Set up the test with a procedure and a function. +# +CREATE PROCEDURE insert_n(start int, end int) +BEGIN +DECLARE i INT DEFAULT start; +WHILE i <= end do +INSERT INTO t1 VALUES (1, 2, 3) ON DUPLICATE KEY UPDATE c = i; +SET i = i + 1; +END WHILE; +END~~ +CREATE FUNCTION num_pages_get() +RETURNS INT +BEGIN +DECLARE ret INT; +SELECT variable_value INTO ret +FROM information_schema.global_status +WHERE variable_name = 'innodb_buffer_pool_read_requests'; +RETURN ret; +END~~ +# +# Create a table with one record in it and start an RR transaction +# +CREATE TABLE t1 (a INT, b INT, c INT, PRIMARY KEY(a,b), KEY (b,c)) +ENGINE=InnoDB; +BEGIN; +SELECT * FROM t1; +a b c +# +# Create 100 newer record versions in con2 and con3 +# +connect con2, localhost, root,,; +connection con2; +INSERT INTO t1 VALUES (1, 2, 3) ON DUPLICATE KEY UPDATE c = NULL; +CALL insert_n(1, 50);; +connect con3, localhost, root,,; +connection con3; +CALL insert_n(51, 100);; +connection con2; +connection con3; +INSERT INTO t1 VALUES (1, 2, 1) ON DUPLICATE KEY UPDATE c = NULL; +connection default; +# +# Connect to default and record how many pages were accessed +# when selecting the record using the secondary key. +# +SET @num_pages_1 = num_pages_get(); +SELECT * FROM t1 force index (b); +a b c +SET @num_pages_2= num_pages_get(); +SELECT @num_pages_2 - @num_pages_1 < 500; +@num_pages_2 - @num_pages_1 < 500 +1 +# +# Commit and show the final record. +# +SELECT * FROM t1; +a b c +SELECT * FROM t1 force index (b); +a b c +COMMIT; +SELECT * FROM t1 force index (b); +a b c +1 2 NULL +SELECT * FROM t1; +a b c +1 2 NULL +CHECK TABLE t1; +Table Op Msg_type Msg_text +test.t1 check status OK +# +# Cleanup +# +disconnect con2; +disconnect con3; +DROP TABLE t1; +DROP PROCEDURE insert_n; +DROP FUNCTION num_pages_get; diff --git a/mysql-test/suite/innodb/t/innodb_bug14704286.test b/mysql-test/suite/innodb/t/innodb_bug14704286.test deleted file mode 100644 index fb5e6b829a1..00000000000 --- a/mysql-test/suite/innodb/t/innodb_bug14704286.test +++ /dev/null @@ -1,95 +0,0 @@ ---source include/have_innodb.inc - -# -# create test-bed to run test -# -use test; ---disable_warnings -drop table if exists t1; ---enable_warnings -create table t1 (id int primary key, value int, value2 int, -value3 int, index(value,value2)) engine=innodb; - -insert into t1 values -(10,10,10,10),(11,11,11,11),(12,12,12,12),(13,13,13,13),(14,14,14,14), -(15,15,15,15),(16,16,16,16),(17,17,17,17),(18,18,18,18),(19,19,19,19), -(20,20,20,20); -let $ID= `SELECT @id := CONNECTION_ID()`; - -# -# we need multiple connections as we need to keep one connection -# active with trx requesting consistent read. -# -connect (conn1, localhost, root,,); -connect (conn2, localhost, root,,); -connect (conn3, localhost, root,,); - -# -# start trx with consistent read -# -connection conn1; -use test; - -start transaction with consistent snapshot; - -# -# update table such that secondary index is updated. -# -connection conn2; -use test; -delimiter |; -CREATE PROCEDURE update_t1() -BEGIN - DECLARE i INT DEFAULT 1; - while (i <= 5000) DO - update test.t1 set value2=value2+1, value3=value3+1 where id=12; - SET i = i + 1; - END WHILE; -END| - -delimiter ;| -set autocommit=0; -CALL update_t1(); -select * from t1; -set autocommit=1; -select * from t1; - -# -# Now try to fire select query from connection-1 enforcing -# use of secondary index. -# -connection conn1; -let $ID= `SELECT @id := CONNECTION_ID()`; -#--error ER_QUERY_INTERRUPTED ---send -select * from t1 force index(value) where value=12; - -# -# select is going to take good time so let's kill query. -# -connection conn3; -let $wait_condition= - select * from information_schema.processlist where state = 'Sending data' and - info = 'select * from t1 force index(value) where value=12'; ---source include/wait_condition.inc -let $ignore= `SELECT @id := $ID`; -kill query @id; - -# -# reap the value of connection-1 -# -connection conn1; ---error ER_QUERY_INTERRUPTED -reap; - -# -# clean test-bed. -# -connection default; -disconnect conn1; -disconnect conn2; -disconnect conn3; -drop procedure if exists update_t1; -drop table if exists t1; - - diff --git a/mysql-test/suite/innodb/t/innodb_bug84958.test b/mysql-test/suite/innodb/t/innodb_bug84958.test new file mode 100644 index 00000000000..4456df21cb9 --- /dev/null +++ b/mysql-test/suite/innodb/t/innodb_bug84958.test @@ -0,0 +1,86 @@ +--echo # +--echo # Bug #84958 InnoDB's MVCC has O(N^2) behaviors +--echo # https://bugs.mysql.com/bug.php?id=84958 +--echo # +--echo # Set up the test with a procedure and a function. +--echo # + +--source include/have_innodb.inc + +DELIMITER ~~; +CREATE PROCEDURE insert_n(start int, end int) +BEGIN + DECLARE i INT DEFAULT start; + WHILE i <= end do + INSERT INTO t1 VALUES (1, 2, 3) ON DUPLICATE KEY UPDATE c = i; + SET i = i + 1; + END WHILE; +END~~ + +CREATE FUNCTION num_pages_get() +RETURNS INT +BEGIN + DECLARE ret INT; + SELECT variable_value INTO ret + FROM information_schema.global_status + WHERE variable_name = 'innodb_buffer_pool_read_requests'; + RETURN ret; +END~~ +DELIMITER ;~~ + +--echo # +--echo # Create a table with one record in it and start an RR transaction +--echo # +CREATE TABLE t1 (a INT, b INT, c INT, PRIMARY KEY(a,b), KEY (b,c)) +ENGINE=InnoDB; +BEGIN; +SELECT * FROM t1; + +--echo # +--echo # Create 100 newer record versions in con2 and con3 +--echo # +connect (con2, localhost, root,,); +connection con2; +INSERT INTO t1 VALUES (1, 2, 3) ON DUPLICATE KEY UPDATE c = NULL; +--send CALL insert_n(1, 50); + +connect (con3, localhost, root,,); +connection con3; +--send CALL insert_n(51, 100); + +connection con2; +reap; +connection con3; +reap; +INSERT INTO t1 VALUES (1, 2, 1) ON DUPLICATE KEY UPDATE c = NULL; + +connection default; + +--echo # +--echo # Connect to default and record how many pages were accessed +--echo # when selecting the record using the secondary key. +--echo # +SET @num_pages_1 = num_pages_get(); +SELECT * FROM t1 force index (b); +SET @num_pages_2= num_pages_get(); + +SELECT @num_pages_2 - @num_pages_1 < 500; + +--echo # +--echo # Commit and show the final record. +--echo # +SELECT * FROM t1; +SELECT * FROM t1 force index (b); +COMMIT; +SELECT * FROM t1 force index (b); +SELECT * FROM t1; +CHECK TABLE t1; + +--echo # +--echo # Cleanup +--echo # +disconnect con2; +disconnect con3; +DROP TABLE t1; +DROP PROCEDURE insert_n; +DROP FUNCTION num_pages_get; diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc index 877d9c3ef78..d5b1a596f01 100644 --- a/storage/innobase/row/row0sel.cc +++ b/storage/innobase/row/row0sel.cc @@ -3309,14 +3309,29 @@ row_sel_build_prev_vers_for_mysql( return(err); } +/** Helper class to cache clust_rec and old_ver */ +class Row_sel_get_clust_rec_for_mysql +{ + const rec_t *cached_clust_rec; + rec_t *cached_old_vers; + +public: + Row_sel_get_clust_rec_for_mysql() : + cached_clust_rec(NULL), cached_old_vers(NULL) {} + + dberr_t operator()(row_prebuilt_t *prebuilt, dict_index_t *sec_index, + const rec_t *rec, que_thr_t *thr, const rec_t **out_rec, + ulint **offsets, mem_heap_t **offset_heap, + dtuple_t **vrow, mtr_t *mtr); +}; + /*********************************************************************//** Retrieves the clustered index record corresponding to a record in a non-clustered index. Does the necessary locking. Used in the MySQL interface. @return DB_SUCCESS, DB_SUCCESS_LOCKED_REC, or error code */ -static MY_ATTRIBUTE((warn_unused_result)) dberr_t -row_sel_get_clust_rec_for_mysql( +Row_sel_get_clust_rec_for_mysql::operator()( /*============================*/ row_prebuilt_t* prebuilt,/*!< in: prebuilt struct in the handle */ dict_index_t* sec_index,/*!< in: secondary index where rec resides */ @@ -3508,15 +3523,36 @@ row_sel_get_clust_rec_for_mysql( clust_rec, clust_index, *offsets, trx_get_read_view(trx))) { - /* The following call returns 'offsets' associated with - 'old_vers' */ - err = row_sel_build_prev_vers_for_mysql( - trx->read_view, clust_index, prebuilt, - clust_rec, offsets, offset_heap, &old_vers, - vrow, mtr); + if (clust_rec != cached_clust_rec) { + /* The following call returns 'offsets' associated with + 'old_vers' */ + err = row_sel_build_prev_vers_for_mysql( + trx->read_view, clust_index, prebuilt, + clust_rec, offsets, offset_heap, &old_vers, + vrow, mtr); - if (err != DB_SUCCESS || old_vers == NULL) { + if (err != DB_SUCCESS) { + goto err_exit; + } + cached_clust_rec = clust_rec; + cached_old_vers = old_vers; + } else { + err = DB_SUCCESS; + old_vers = cached_old_vers; + + /* The offsets need not be same for the latest + version of clust_rec and its old version + old_vers. Re-calculate the offsets for old_vers. */ + + if (old_vers != NULL) { + *offsets = rec_get_offsets( + old_vers, clust_index, *offsets, + true, ULINT_UNDEFINED, offset_heap); + } + } + + if (old_vers == NULL) { goto err_exit; } @@ -4233,6 +4269,7 @@ row_search_mvcc( dtuple_t* vrow = NULL; const rec_t* result_rec = NULL; const rec_t* clust_rec; + Row_sel_get_clust_rec_for_mysql row_sel_get_clust_rec_for_mysql; dberr_t err = DB_SUCCESS; ibool unique_search = FALSE; ibool mtr_has_extra_clust_latch = FALSE; From 29e560cdf32a2c6aa10298c2404bbeb54e628d6e Mon Sep 17 00:00:00 2001 From: Sujatha Date: Wed, 14 Aug 2019 22:53:16 +0530 Subject: [PATCH 05/14] MDEV-20348: DROP TABLE IF EXISTS killed on master but was replicated Problem: ======= DROP TABLE IF EXISTS was killed. The table still exists on the master but the DDL was still logged. Analysis: ========= During the execution of DROP TABLE command "ha_delete_table" call is invoked to delete the table. If the query is killed at this point, the kill command is not handled within the code. This results in two issues. 1) The table which is not dropped also gets written into the binary log. 2) The code continues further upon receiving 'KILL QUERY'. Fix: === Upon receiving the KILL command the query should stop its current execution. Tables which were successfully dropped prior to KILL command should be included in the binary log. --- .../rpl/r/rpl_failed_drop_tbl_binlog.result | 32 +++++++++ .../rpl/t/rpl_failed_drop_tbl_binlog.test | 64 ++++++++++++++++++ sql/sql_table.cc | 67 +++++++++---------- 3 files changed, 127 insertions(+), 36 deletions(-) create mode 100644 mysql-test/suite/rpl/r/rpl_failed_drop_tbl_binlog.result create mode 100644 mysql-test/suite/rpl/t/rpl_failed_drop_tbl_binlog.test diff --git a/mysql-test/suite/rpl/r/rpl_failed_drop_tbl_binlog.result b/mysql-test/suite/rpl/r/rpl_failed_drop_tbl_binlog.result new file mode 100644 index 00000000000..df36fa82e0f --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_failed_drop_tbl_binlog.result @@ -0,0 +1,32 @@ +include/master-slave.inc +[connection master] +create table t1 (a int) engine=innodb; +create table t2 (b longblob) engine=innodb; +create table t3 (c int) engine=innodb; +insert into t2 values (repeat('b',1024*1024)); +insert into t2 select * from t2; +insert into t2 select * from t2; +insert into t2 select * from t2; +insert into t2 select * from t2; +set debug_sync='rm_table_no_locks_before_delete_table SIGNAL nogo WAIT_FOR go EXECUTE 2'; +drop table t1, t2, t3; +connect foo,localhost,root; +set debug_sync='now SIGNAL go'; +kill query CONNECTION_ID; +connection master; +ERROR 70100: Query execution was interrupted +"Tables t2 and t3 should be listed" +SHOW TABLES; +Tables_in_test +t2 +t3 +include/show_binlog_events.inc +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # use `test`; DROP TABLE `t1` /* generated by server */ +connection slave; +drop table t2, t3; +connection master; +set debug_sync='RESET'; +drop table t2, t3; +include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_failed_drop_tbl_binlog.test b/mysql-test/suite/rpl/t/rpl_failed_drop_tbl_binlog.test new file mode 100644 index 00000000000..281e2a2ab47 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_failed_drop_tbl_binlog.test @@ -0,0 +1,64 @@ +# ==== Purpose ==== +# +# Check that when the execution of a DROP TABLE command with single table +# fails it should not be written to the binary log. Also test that when the +# execution of DROP TABLE command with multiple tables fails the command +# should be written into the binary log. +# +# ==== Implementation ==== +# +# Steps: +# 0 - Create tables named t1, t2, t3 +# 1 - Execute DROP TABLE t1,t2,t3 command. +# 2 - Kill the DROP TABLE command while it is trying to drop table 't2'. +# 3 - Verify that tables t2,t3 are present after the DROP command execution +# was interrupted. +# 4 - Check that table 't1' is present in binary log as part of DROP +# command. +# +# ==== References ==== +# +# MDEV-20348: DROP TABLE IF EXISTS killed on master but was replicated. +# + +--source include/have_innodb.inc +--source include/have_debug_sync.inc +--source include/have_binlog_format_statement.inc +--source include/master-slave.inc + +create table t1 (a int) engine=innodb; +create table t2 (b longblob) engine=innodb; +create table t3 (c int) engine=innodb; +insert into t2 values (repeat('b',1024*1024)); +insert into t2 select * from t2; +insert into t2 select * from t2; +insert into t2 select * from t2; +insert into t2 select * from t2; +let $binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1); + +let $id=`select connection_id()`; +set debug_sync='rm_table_no_locks_before_delete_table SIGNAL nogo WAIT_FOR go EXECUTE 2'; +send drop table t1, t2, t3; + +connect foo,localhost,root; +set debug_sync='now SIGNAL go'; +let $wait_condition=select 1 from information_schema.processlist where state like 'debug sync point:%'; +source include/wait_condition.inc; +--replace_result $id CONNECTION_ID +eval kill query $id; + +connection master; +error ER_QUERY_INTERRUPTED; +reap; + +--echo "Tables t2 and t3 should be listed" +SHOW TABLES; +--source include/show_binlog_events.inc +--sync_slave_with_master +drop table t2, t3; + +connection master; +set debug_sync='RESET'; +drop table t2, t3; + +source include/rpl_end.inc; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 9d7e03727d3..cc58dc4a7e8 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -2362,35 +2362,6 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, path_length= build_table_filename(path, sizeof(path) - 1, db, alias, reg_ext, 0); - /* - This handles the case where a "DROP" was executed and a regular - table "may be" dropped as drop_temporary is FALSE and error is - TRUE. If the error was FALSE a temporary table was dropped and - regardless of the status of drop_temporary a "DROP TEMPORARY" - must be used. - */ - if (!dont_log_query) - { - /* - Note that unless if_exists is TRUE or a temporary table was deleted, - there is no means to know if the statement should be written to the - binary log. See further information on this variable in what follows. - */ - non_tmp_table_deleted= (if_exists ? TRUE : non_tmp_table_deleted); - /* - Don't write the database name if it is the current one (or if - thd->db is NULL). - */ - if (thd->db == NULL || strcmp(db,thd->db) != 0) - { - append_identifier(thd, &built_query, db, db_length); - built_query.append("."); - } - - append_identifier(thd, &built_query, table->table_name, - table->table_name_length); - built_query.append(","); - } } DEBUG_SYNC(thd, "rm_table_no_locks_before_delete_table"); error= 0; @@ -2464,14 +2435,20 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, // Remove extension for delete *(end= path + path_length - reg_ext_length)= '\0'; - error= ha_delete_table(thd, table_type, path, db, table->table_name, - !dont_log_query); - - if (!error) + if ((error= ha_delete_table(thd, table_type, path, db, table->table_name, + !dont_log_query))) + { + if (thd->is_killed()) + { + error= -1; + goto err; + } + } + else { int frm_delete_error, trigger_drop_error= 0; - /* Delete the table definition file */ - strmov(end,reg_ext); + /* Delete the table definition file */ + strmov(end,reg_ext); if (table_type && table_type != view_pseudo_hton && table_type->discover_table) { @@ -2505,7 +2482,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, if (error) { if (wrong_tables.length()) - wrong_tables.append(','); + wrong_tables.append(','); wrong_tables.append(db); wrong_tables.append('.'); wrong_tables.append(table->table_name); @@ -2518,6 +2495,24 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, mysql_audit_drop_table(thd, table); } + if (!dont_log_query && !drop_temporary) + { + non_tmp_table_deleted= (if_exists ? TRUE : non_tmp_table_deleted); + /* + Don't write the database name if it is the current one (or if + thd->db is NULL). + */ + if (thd->db == NULL || strcmp(db,thd->db) != 0) + { + append_identifier(thd, &built_query, db, db_length); + built_query.append("."); + } + + append_identifier(thd, &built_query, table->table_name, + table->table_name_length); + built_query.append(","); + } + DBUG_PRINT("table", ("table: %p s: %p", table->table, table->table ? table->table->s : NULL)); } From 841294cfaa0e9591de446a4f083477b61e05e3cc Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Thu, 15 Aug 2019 10:32:42 +0400 Subject: [PATCH 06/14] MDEV-20351 Window function BIT_OR() OVER (..) return a wrong data type --- mysql-test/main/win.result | 35 +++++++++++++++++++++++++++++++++++ mysql-test/main/win.test | 23 +++++++++++++++++++++++ sql/item_windowfunc.h | 2 +- 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/mysql-test/main/win.result b/mysql-test/main/win.result index 412bfca4bf2..80c4109fe27 100644 --- a/mysql-test/main/win.result +++ b/mysql-test/main/win.result @@ -3654,5 +3654,40 @@ d x 00:00:02 NULL DROP TABLE t1; # +# MDEV-20351 Window function BIT_OR() OVER (..) return a wrong data type +# +CREATE TABLE t1 (pk INT, a INT, b BIGINT UNSIGNED); +INSERT INTO t1 VALUES (1, 0, 1), (2, 0, 18446744073709551615); +CREATE TABLE t2 AS +SELECT pk, a, bit_or(b) AS bit_or FROM t1 GROUP BY pk; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `pk` int(11) DEFAULT NULL, + `a` int(11) DEFAULT NULL, + `bit_or` bigint(21) unsigned NOT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SELECT * FROM t1; +pk a b +1 0 1 +2 0 18446744073709551615 +DROP TABLE t2; +CREATE OR REPLACE TABLE t2 AS +SELECT pk, a, BIT_OR(b) OVER (PARTITION BY a ORDER BY pk ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS bit_or +FROM t1; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `pk` int(11) DEFAULT NULL, + `a` int(11) DEFAULT NULL, + `bit_or` bigint(21) unsigned NOT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SELECT * FROM t2; +pk a bit_or +1 0 18446744073709551615 +2 0 18446744073709551615 +DROP TABLE t2; +DROP TABLE t1; +# # End of 10.3 tests # diff --git a/mysql-test/main/win.test b/mysql-test/main/win.test index d49be4a0433..e71a4376392 100644 --- a/mysql-test/main/win.test +++ b/mysql-test/main/win.test @@ -2359,6 +2359,29 @@ INSERT INTO t1 VALUES ('00:00:01'),('00:00:02'); SELECT *, LEAD(d) OVER (ORDER BY d) AS x FROM t1; DROP TABLE t1; + +--echo # +--echo # MDEV-20351 Window function BIT_OR() OVER (..) return a wrong data type +--echo # +CREATE TABLE t1 (pk INT, a INT, b BIGINT UNSIGNED); +INSERT INTO t1 VALUES (1, 0, 1), (2, 0, 18446744073709551615); + +CREATE TABLE t2 AS +SELECT pk, a, bit_or(b) AS bit_or FROM t1 GROUP BY pk; +SHOW CREATE TABLE t2; +SELECT * FROM t1; +DROP TABLE t2; + +CREATE OR REPLACE TABLE t2 AS +SELECT pk, a, BIT_OR(b) OVER (PARTITION BY a ORDER BY pk ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS bit_or + FROM t1; +SHOW CREATE TABLE t2; +SELECT * FROM t2; +DROP TABLE t2; + +DROP TABLE t1; + + --echo # --echo # End of 10.3 tests --echo # diff --git a/sql/item_windowfunc.h b/sql/item_windowfunc.h index e6389c01832..cc67c02f1ee 100644 --- a/sql/item_windowfunc.h +++ b/sql/item_windowfunc.h @@ -1282,7 +1282,7 @@ public: bool fix_length_and_dec() { - decimals = window_func()->decimals; + Type_std_attributes::set(window_func()); return FALSE; } From ae34d85beb1780c6a20ba1e57cfb9f25e85a270b Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Mon, 12 Aug 2019 15:16:33 +0300 Subject: [PATCH 07/14] MDEV-20311 row_ins_step accesses unitialized memory ins_node_create() does not initialize all members of que_common_t, so zero-init them with mem_heap_zalloc(). Handle out-of-memory correctly. Init insert_node->common.parent to fulfill the contract of thr usage. Free insert_node subtree at row_update_vers_insert() exit. --- storage/innobase/row/row0ins.cc | 12 +++++------- storage/innobase/row/row0mysql.cc | 14 +++++++++++--- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/storage/innobase/row/row0ins.cc b/storage/innobase/row/row0ins.cc index 26e46aedeb5..12bd2001495 100644 --- a/storage/innobase/row/row0ins.cc +++ b/storage/innobase/row/row0ins.cc @@ -74,7 +74,11 @@ ins_node_create( ins_node_t* node; node = static_cast( - mem_heap_alloc(heap, sizeof(ins_node_t))); + mem_heap_zalloc(heap, sizeof(ins_node_t))); + + if (!node) { + return(NULL); + } node->common.type = QUE_NODE_INSERT; @@ -82,12 +86,6 @@ ins_node_create( node->state = INS_NODE_SET_IX_LOCK; node->table = table; - node->index = NULL; - node->entry = NULL; - - node->select = NULL; - - node->trx_id = 0; node->entry_sys_heap = mem_heap_create(128); diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc index fe6214adefe..aed2f87349d 100644 --- a/storage/innobase/row/row0mysql.cc +++ b/storage/innobase/row/row0mysql.cc @@ -2150,7 +2150,9 @@ This is used in UPDATE CASCADE/SET NULL of a system versioning table. @return DB_SUCCESS or some error */ static dberr_t row_update_vers_insert(que_thr_t* thr, upd_node_t* node) { - const trx_t* trx = thr_get_trx(thr); + trx_t* trx = thr_get_trx(thr); + dfield_t* row_end; + char row_end_data[8]; dict_table_t* table = node->table; ut_ad(table->versioned()); @@ -2161,10 +2163,15 @@ static dberr_t row_update_vers_insert(que_thr_t* thr, upd_node_t* node) ins_node_t* insert_node = ins_node_create(INS_DIRECT, table, node->historical_heap); + if (!insert_node) { + trx->error_state = DB_OUT_OF_MEMORY; + goto exit; + } + + insert_node->common.parent = thr; ins_node_set_new_row(insert_node, row); - dfield_t* row_end = dtuple_get_nth_field(row, table->vers_end); - char row_end_data[8]; + row_end = dtuple_get_nth_field(row, table->vers_end); if (dict_table_get_nth_col(table, table->vers_end)->vers_native()) { mach_write_to_8(row_end_data, trx->id); dfield_set_data(row_end, row_end_data, 8); @@ -2202,6 +2209,7 @@ static dberr_t row_update_vers_insert(que_thr_t* thr, upd_node_t* node) } } exit: + que_graph_free_recursive(insert_node); mem_heap_free(node->historical_heap); node->historical_heap = NULL; return trx->error_state; From 0b20b9e91173141edda2cd5649b91bd63b61c127 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Thu, 15 Aug 2019 12:53:08 +0300 Subject: [PATCH 08/14] Disable galera.query_cache as it still fails on bb and azure. --- mysql-test/suite/galera/disabled.def | 1 + 1 file changed, 1 insertion(+) diff --git a/mysql-test/suite/galera/disabled.def b/mysql-test/suite/galera/disabled.def index 90b13bd7aa4..017d8ec1380 100644 --- a/mysql-test/suite/galera/disabled.def +++ b/mysql-test/suite/galera/disabled.def @@ -30,3 +30,4 @@ galera_ssl_upgrade : MDEV-19950 Galera test failure on galera_ssl_upgrade galera_sst_mariabackup_encrypt_with_key : MDEV-19926 Galera SST tests fail galera_wan : MDEV-17259: Test failure on galera.galera_wan partition : MDEV-19958 Galera test failure on galera.partition +query_cache: MDEV-15805 Test failure on galera.query_cache \ No newline at end of file From ec28f9532e92820f59fc7298465407069e289a5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 15 Aug 2019 15:06:27 +0300 Subject: [PATCH 09/14] MDEV-19740: Fix C++11 violations caught by GCC 9.2.1 --- sql/item.h | 3 +-- sql/sql_list.h | 23 ++++++++++++++++++----- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/sql/item.h b/sql/item.h index 49762d3326f..1229f271790 100644 --- a/sql/item.h +++ b/sql/item.h @@ -2,7 +2,7 @@ #define SQL_ITEM_INCLUDED /* Copyright (c) 2000, 2017, Oracle and/or its affiliates. - Copyright (c) 2009, 2018, MariaDB Corporation + Copyright (c) 2009, 2019, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -651,7 +651,6 @@ class Item: public Value_source, public Type_std_attributes, public Type_handler { - void operator=(Item &); /** The index in the JOIN::join_tab array of the JOIN_TAB this Item is attached to. Items are attached (or 'pushed') to JOIN_TABs during optimization by the diff --git a/sql/sql_list.h b/sql/sql_list.h index c14ebfc00f7..a7ece69acb6 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -1,6 +1,7 @@ #ifndef INCLUDES_MYSQL_SQL_LIST_H #define INCLUDES_MYSQL_SQL_LIST_H /* Copyright (c) 2000, 2012, Oracle and/or its affiliates. + Copyright (c) 2019, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -86,6 +87,14 @@ public: next= elements ? tmp.next : &first; } + SQL_I_List& operator=(const SQL_I_List &tmp) + { + elements= tmp.elements; + first= tmp.first; + next= tmp.next; + return *this; + } + inline void empty() { elements= 0; @@ -176,6 +185,13 @@ public: first == rhs.first && last == rhs.last; } + base_list& operator=(const base_list &rhs) + { + elements= rhs.elements; + first= rhs.first; + last= elements ? rhs.last : &first; + return *this; + } inline void empty() { elements=0; first= &end_of_list; last=&first;} inline base_list() { empty(); } @@ -190,9 +206,7 @@ public: */ inline base_list(const base_list &tmp) :Sql_alloc() { - elements= tmp.elements; - first= tmp.first; - last= elements ? tmp.last : &first; + *this= tmp; } /** Construct a deep copy of the argument in memory root mem_root. @@ -202,7 +216,7 @@ public: */ bool copy(const base_list *rhs, MEM_ROOT *mem_root); base_list(const base_list &rhs, MEM_ROOT *mem_root) { copy(&rhs, mem_root); } - inline base_list(bool error) { } + inline base_list(bool) {} inline bool push_back(void *info) { if (((*last)=new list_node(info, &end_of_list))) @@ -523,7 +537,6 @@ template class List :public base_list { public: inline List() :base_list() {} - inline List(const List &tmp) :base_list(tmp) {} inline List(const List &tmp, MEM_ROOT *mem_root) : base_list(tmp, mem_root) {} inline bool push_back(T *a) { return base_list::push_back(a); } From d07936aabadc99be885d6fed24d309e4611887c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 15 Aug 2019 15:29:03 +0300 Subject: [PATCH 10/14] MDEV-19740: Silence a bogus "may be uninitialized" warning --- extra/innochecksum.cc | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/extra/innochecksum.cc b/extra/innochecksum.cc index a1ec663ee1a..4dca6572434 100644 --- a/extra/innochecksum.cc +++ b/extra/innochecksum.cc @@ -1590,10 +1590,8 @@ int main( byte* xdes = NULL; /* bytes read count */ ulint bytes; - /* current time */ - time_t now; /* last time */ - time_t lastt; + time_t lastt = 0; /* stat, to get file size. */ #ifdef _WIN32 struct _stat64 st; @@ -1946,7 +1944,6 @@ int main( /* main checksumming loop */ cur_page_num = start_page ? start_page : cur_page_num + 1; - lastt = 0; while (!feof(fil_in)) { bytes = read_file(buf, partial_page_read, @@ -2026,12 +2023,10 @@ first_non_zero: if (verbose && !read_from_stdin) { if ((cur_page_num % 64) == 0) { - now = time(0); + time_t now = time(0); if (!lastt) { lastt= now; - } - if (now - lastt >= 1 - && is_log_enabled) { + } else if (now - lastt >= 1 && is_log_enabled) { fprintf(log_file, "page::%llu " "okay: %.3f%% done\n", (cur_page_num - 1), From 112589cdeda90b20e015e495912fe7d4dd85c0d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 15 Aug 2019 15:29:32 +0300 Subject: [PATCH 11/14] MDEV-19740: Remove a bogus condition This triggered a "may be uninitialized" warning from GCC 9.2.1. The bogus-looking condition was added in 7e916bb86f512ff79f30d809b813608a625ec5ba --- storage/innobase/row/row0sel.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc index d5b1a596f01..6b3542e7aa3 100644 --- a/storage/innobase/row/row0sel.cc +++ b/storage/innobase/row/row0sel.cc @@ -5726,9 +5726,7 @@ lock_wait_or_error: /*-------------------------------------------------------------*/ if (!dict_index_is_spatial(index)) { - if (rec) { - btr_pcur_store_position(pcur, &mtr); - } + btr_pcur_store_position(pcur, &mtr); } lock_table_wait: From 130d9490c844d82a7a8613c343a1969c0bec4c62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Fri, 16 Aug 2019 08:29:41 +0300 Subject: [PATCH 12/14] Silence GCC 9.2.1 -Warray-bounds No memory access violated the bounds of fake_extra_buf[], but GCC does not like the fact that the pointer fake_extra ends up pointing before the array. Allocate a dummy element at the start of fake_extra_buf[] in order to silence the warning. --- storage/innobase/row/row0log.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/storage/innobase/row/row0log.cc b/storage/innobase/row/row0log.cc index 78f283a3fc8..71fb7fc5f7f 100644 --- a/storage/innobase/row/row0log.cc +++ b/storage/innobase/row/row0log.cc @@ -1003,7 +1003,7 @@ row_log_table_low( extra_size = rec_extra_size + is_instant; unsigned fake_extra_size = 0; - byte fake_extra_buf[2]; + byte fake_extra_buf[3]; if (is_instant && UNIV_UNLIKELY(!index->is_instant())) { /* The source table was emptied after ALTER TABLE started, and it was converted to non-instant format. @@ -1015,9 +1015,9 @@ row_log_table_low( fake_extra_size = rec_get_n_add_field_len(n_add); ut_ad(fake_extra_size == 1 || fake_extra_size == 2); extra_size += fake_extra_size; - byte* fake_extra = fake_extra_buf + fake_extra_size - 1; + byte* fake_extra = fake_extra_buf + fake_extra_size; rec_set_n_add_field(fake_extra, n_add); - ut_ad(fake_extra + 1 == fake_extra_buf); + ut_ad(fake_extra == fake_extra_buf); } mrec_size = ROW_LOG_HEADER_SIZE @@ -1076,7 +1076,7 @@ row_log_table_low( memcpy(b, rec - rec_extra_size - omit_size, rec_extra_size); b += rec_extra_size; - memcpy(b, fake_extra_buf, fake_extra_size); + memcpy(b, fake_extra_buf + 1, fake_extra_size); b += fake_extra_size; ulint len; ulint trx_id_offs = rec_get_nth_field_offs( From 555af003e4291dc41e21fe143ca19649972e2ee6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Fri, 16 Aug 2019 09:54:33 +0300 Subject: [PATCH 13/14] MDEV-8588/MDEV-19740: Restore a condition It looks like the merge of MySQL 5.7.9 to MariaDB 10.2.2 conflicted with earlier changes that were made in MDEV-8588. row_search_mvcc(): If the page is corrupted, avoid invoking btr_cur_store_position(). The caller should not try to fetch the next record after a hard error. --- storage/innobase/row/row0sel.cc | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc index 6b3542e7aa3..19130576425 100644 --- a/storage/innobase/row/row0sel.cc +++ b/storage/innobase/row/row0sel.cc @@ -4725,7 +4725,7 @@ wait_table_again: if (err != DB_SUCCESS) { rec = NULL; - goto lock_wait_or_error; + goto page_read_error; } pcur->trx_if_known = trx; @@ -4779,7 +4779,7 @@ wait_table_again: index->table->file_unreadable = true; } rec = NULL; - goto lock_wait_or_error; + goto page_read_error; } } @@ -4800,7 +4800,7 @@ rec_loop: if (!index->table->is_readable()) { err = DB_DECRYPTION_FAILED; - goto lock_wait_or_error; + goto page_read_error; } ut_ad(!!page_rec_is_comp(rec) == comp); @@ -4895,7 +4895,7 @@ wrong_offs: ut_ad(0); err = DB_CORRUPTION; - goto lock_wait_or_error; + goto page_read_error; } else { /* The user may be dumping a corrupt table. Jump over the corruption to recover as much as possible. */ @@ -5717,6 +5717,10 @@ not_moved: goto normal_return; lock_wait_or_error: + if (!dict_index_is_spatial(index)) { + btr_pcur_store_position(pcur, &mtr); + } +page_read_error: /* Reset the old and new "did semi-consistent read" flags. */ if (UNIV_UNLIKELY(prebuilt->row_read_type == ROW_READ_DID_SEMI_CONSISTENT)) { @@ -5724,11 +5728,6 @@ lock_wait_or_error: } did_semi_consistent_read = FALSE; - /*-------------------------------------------------------------*/ - if (!dict_index_is_spatial(index)) { - btr_pcur_store_position(pcur, &mtr); - } - lock_table_wait: mtr.commit(); mtr_has_extra_clust_latch = FALSE; From fe6eac0cf7ecf1b38e8cf243104e185b8c67eae8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Fri, 16 Aug 2019 09:50:54 +0300 Subject: [PATCH 14/14] MDEV-19200: shutdown timeout on innodb.undo_truncate_recover Optimize the test by dropping the table early and by using only one undo log thread, so that purge will be doing more useful work and less busy work of suspending and resuming the worker threads. The test used to cause shutdown timeout on 10.4 on buildbot, and for me locally when using --mysqld=--innodb-sync-debug. With these tweaks, it passes for me with --mysqld=--innodb-sync-debug. --- mysql-test/suite/innodb/r/undo_truncate_recover.result | 2 +- mysql-test/suite/innodb/t/undo_truncate_recover.opt | 1 + mysql-test/suite/innodb/t/undo_truncate_recover.test | 3 +-- 3 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 mysql-test/suite/innodb/t/undo_truncate_recover.opt diff --git a/mysql-test/suite/innodb/r/undo_truncate_recover.result b/mysql-test/suite/innodb/r/undo_truncate_recover.result index e1b6a67368b..03393f38aed 100644 --- a/mysql-test/suite/innodb/r/undo_truncate_recover.result +++ b/mysql-test/suite/innodb/r/undo_truncate_recover.result @@ -9,8 +9,8 @@ update t1 set c = 'MariaDB'; update t1 set c = 'InnoDB'; set global debug_dbug = '+d,ib_undo_trunc'; commit; +drop table t1; call mtr.add_suppression("InnoDB: innodb_undo_tablespaces=0 disables dedicated undo log tablespaces"); call mtr.add_suppression("InnoDB: The transaction log size is too large"); SET GLOBAL innodb_fast_shutdown=0; FOUND 1 /ib_undo_trunc/ in mysqld.1.err -drop table t1; diff --git a/mysql-test/suite/innodb/t/undo_truncate_recover.opt b/mysql-test/suite/innodb/t/undo_truncate_recover.opt new file mode 100644 index 00000000000..a1207721427 --- /dev/null +++ b/mysql-test/suite/innodb/t/undo_truncate_recover.opt @@ -0,0 +1 @@ +--innodb-purge-threads=1 diff --git a/mysql-test/suite/innodb/t/undo_truncate_recover.test b/mysql-test/suite/innodb/t/undo_truncate_recover.test index c28a2154f92..94e09ed0e64 100644 --- a/mysql-test/suite/innodb/t/undo_truncate_recover.test +++ b/mysql-test/suite/innodb/t/undo_truncate_recover.test @@ -34,6 +34,7 @@ update t1 set c = 'MariaDB'; update t1 set c = 'InnoDB'; eval set global debug_dbug = '+d,$SEARCH_PATTERN'; commit; +drop table t1; call mtr.add_suppression("InnoDB: innodb_undo_tablespaces=0 disables dedicated undo log tablespaces"); # FIXME: remove this work-around, and generate less log! call mtr.add_suppression("InnoDB: The transaction log size is too large"); @@ -43,5 +44,3 @@ SET GLOBAL innodb_fast_shutdown=0; # FIXME: remove this work-around, and generate less log! --let $restart_parameters= --innodb-buffer-pool-size=16m --innodb-undo-tablespaces=1 --source include/start_mysqld.inc - -drop table t1;