diff --git a/mysql-test/main/alter_table.result b/mysql-test/main/alter_table.result index 879bc0edfec..7b9eab3f3f2 100644 --- a/mysql-test/main/alter_table.result +++ b/mysql-test/main/alter_table.result @@ -3094,6 +3094,40 @@ drop table t1; # End of 10.5 tests # # +# MDEV-32449 Server crashes in Alter_info::add_stat_drop_index upon CREATE TABLE +# +CREATE TABLE t1 ( +`altcol1` blob DEFAULT '', +KEY `altcol1` (`altcol1`(2300)) +) ROW_FORMAT=PAGE, ENGINE=Aria; +ALTER TABLE t1 ADD FOREIGN KEY h (`altcol1`) REFERENCES t1 (`altcol1`) ON UPDATE SET DEFAULT, ALGORITHM=COPY; +Warnings: +Note 1071 Specified key was too long; max key length is 2300 bytes +create or replace table t2 like t1; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `altcol1` blob DEFAULT '', + KEY `altcol1` (`altcol1`(2300)), + KEY `h` (`altcol1`(2300)) +) ENGINE=Aria DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci PAGE_CHECKSUM=1 ROW_FORMAT=PAGE +show create table t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `altcol1` blob DEFAULT '', + KEY `altcol1` (`altcol1`(2300)) +) ENGINE=Aria DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci PAGE_CHECKSUM=1 ROW_FORMAT=PAGE +drop table t1,t2; +# Another test for MDEV-32449 +CREATE TABLE t1 (a POINT, b POINT, KEY(a)) ENGINE=Aria; +ALTER TABLE t1 ADD FOREIGN KEY (a) REFERENCES t (b); +CREATE TEMPORARY TABLE t2 LIKE t1; +DROP TEMPORARY TABLE t2; +DROP TABLE t1; +# +# End of 10.6 tests +# +# # MDEV-26767 Server crashes when rename table and alter storage engine # alter table txxx engine=innodb, rename to tyyy; diff --git a/mysql-test/main/alter_table.test b/mysql-test/main/alter_table.test index 8ddf60f26d8..c6aba3fd08d 100644 --- a/mysql-test/main/alter_table.test +++ b/mysql-test/main/alter_table.test @@ -2383,6 +2383,32 @@ drop table t1; --echo # End of 10.5 tests --echo # +--echo # +--echo # MDEV-32449 Server crashes in Alter_info::add_stat_drop_index upon CREATE TABLE +--echo # + +CREATE TABLE t1 ( + `altcol1` blob DEFAULT '', + KEY `altcol1` (`altcol1`(2300)) +) ROW_FORMAT=PAGE, ENGINE=Aria; +ALTER TABLE t1 ADD FOREIGN KEY h (`altcol1`) REFERENCES t1 (`altcol1`) ON UPDATE SET DEFAULT, ALGORITHM=COPY; +create or replace table t2 like t1; +show create table t1; +show create table t2; +drop table t1,t2; + +--echo # Another test for MDEV-32449 + +CREATE TABLE t1 (a POINT, b POINT, KEY(a)) ENGINE=Aria; +ALTER TABLE t1 ADD FOREIGN KEY (a) REFERENCES t (b); +CREATE TEMPORARY TABLE t2 LIKE t1; +DROP TEMPORARY TABLE t2; +DROP TABLE t1; + +--echo # +--echo # End of 10.6 tests +--echo # + --echo # --echo # MDEV-26767 Server crashes when rename table and alter storage engine --echo # diff --git a/mysql-test/suite/encryption/r/innodb-redo-nokeys.result b/mysql-test/suite/encryption/r/innodb-redo-nokeys.result index 859a73db056..b7d6eb50e70 100644 --- a/mysql-test/suite/encryption/r/innodb-redo-nokeys.result +++ b/mysql-test/suite/encryption/r/innodb-redo-nokeys.result @@ -11,6 +11,7 @@ call mtr.add_suppression("InnoDB: OPT_PAGE_CHECKSUM mismatch"); call mtr.add_suppression("InnoDB: Missing FILE_CHECKPOINT"); call mtr.add_suppression("InnoDB: Log scan aborted at LSN"); call mtr.add_suppression("InnoDB: Set innodb_force_recovery=1 to ignore corruption"); +call mtr.add_suppression("InnoDB: Encryption key is not found for"); # restart: --file-key-management-filename=MYSQL_TEST_DIR/std_data/keys2.txt SET GLOBAL innodb_file_per_table = ON; create table t1(a int not null primary key auto_increment, c char(200), b blob, index(b(10))) engine=innodb row_format=compressed encrypted=yes encryption_key_id=20; @@ -41,5 +42,6 @@ SELECT * FROM INFORMATION_SCHEMA.ENGINES WHERE engine = 'innodb' AND support IN ('YES', 'DEFAULT', 'ENABLED'); ENGINE SUPPORT COMMENT TRANSACTIONS XA SAVEPOINTS +FOUND 1 /\[ERROR\] InnoDB: Encryption key is not found for .*test.t1.ibd/ in mysqld.1.err # restart: --file-key-management-filename=MYSQL_TEST_DIR/std_data/keys2.txt drop table t1,t2,t3,t4,t5; diff --git a/mysql-test/suite/encryption/t/innodb-redo-nokeys.test b/mysql-test/suite/encryption/t/innodb-redo-nokeys.test index 713b98130be..52c12761f69 100644 --- a/mysql-test/suite/encryption/t/innodb-redo-nokeys.test +++ b/mysql-test/suite/encryption/t/innodb-redo-nokeys.test @@ -16,6 +16,7 @@ call mtr.add_suppression("InnoDB: OPT_PAGE_CHECKSUM mismatch"); call mtr.add_suppression("InnoDB: Missing FILE_CHECKPOINT"); call mtr.add_suppression("InnoDB: Log scan aborted at LSN"); call mtr.add_suppression("InnoDB: Set innodb_force_recovery=1 to ignore corruption"); +call mtr.add_suppression("InnoDB: Encryption key is not found for"); -- let $restart_parameters=--file-key-management-filename=$MYSQL_TEST_DIR/std_data/keys2.txt -- source include/restart_mysqld.inc @@ -75,6 +76,10 @@ SELECT * FROM INFORMATION_SCHEMA.ENGINES WHERE engine = 'innodb' AND support IN ('YES', 'DEFAULT', 'ENABLED'); +let SEARCH_FILE = $MYSQLTEST_VARDIR/log/mysqld.1.err; +let SEARCH_PATTERN = \[ERROR\] InnoDB: Encryption key is not found for .*test.t1.ibd; +--source include/search_pattern_in_file.inc + # # In above server does start but InnoDB refuses to start # thus we need to restart server with correct key file diff --git a/mysql-test/suite/innodb/r/innodb-table-online.result b/mysql-test/suite/innodb/r/innodb-table-online.result index 5a8b3d24d75..caefcce47f1 100644 --- a/mysql-test/suite/innodb/r/innodb-table-online.result +++ b/mysql-test/suite/innodb/r/innodb-table-online.result @@ -470,6 +470,29 @@ SET DEBUG_SYNC="now SIGNAL con1_signal"; connection con1; DROP TABLE t1; connection default; +# +# MDEV-28122 Optimize table crash while applying online log +# +CREATE TABLE t1(f1 INT NOT NULL, f2 INT NOT NULL, +f3 CHAR(200), f4 CHAR(200), +f5 VARCHAR(87), PRIMARY KEY(f1))ENGINE=InnoDB; +INSERT INTO t1 VALUES(6000, 6000, "InnoDB", +"MariaDB", repeat('a', 87)); +SET DEBUG_SYNC="inplace_after_index_build SIGNAL dml_start WAIT_FOR dml_commit"; +ALTER TABLE t1 FORCE; +connection con1; +SET DEBUG_SYNC="now WAIT_FOR dml_start"; +BEGIN; +INSERT INTO t1 SELECT seq, seq, "IDB", "MDB", repeat('a', 87) FROM seq_1_to_127; +INSERT INTO t1 VALUES(128, 128, "IDB", "MDB", repeat('a', 86)); +INSERT INTO t1 VALUES(129, 129, "idb", "mdb", repeat('a', 2)); +COMMIT; +SET DEBUG_SYNC="now SIGNAL dml_commit"; +connection default; +CHECK TABLE t1; +Table Op Msg_type Msg_text +test.t1 check status OK +DROP TABLE t1; SET DEBUG_SYNC=RESET; disconnect con1; SET GLOBAL innodb_file_per_table = @global_innodb_file_per_table_orig; diff --git a/mysql-test/suite/innodb/r/xa_prepare_reset_supremum_lock.result b/mysql-test/suite/innodb/r/xa_prepare_reset_supremum_lock.result index 475363444e9..bd971adf966 100644 --- a/mysql-test/suite/innodb/r/xa_prepare_reset_supremum_lock.result +++ b/mysql-test/suite/innodb/r/xa_prepare_reset_supremum_lock.result @@ -14,6 +14,19 @@ XA PREPARE '1'; connect con1,localhost,root; SET innodb_lock_wait_timeout=1; INSERT INTO t VALUES(50); +connection default; +XA COMMIT '1'; +XA START '1'; +SELECT * FROM t WHERE a > 20 LOCK IN SHARE MODE; +a +40 +50 +INSERT INTO t VALUES (5); +XA END '1'; +XA PREPARE '1'; +connection con1; +INSERT INTO t VALUES (60); +INSERT INTO t VALUES (30); disconnect con1; connection default; XA COMMIT '1'; diff --git a/mysql-test/suite/innodb/r/xap_release_locks_on_dict_stats_table.result b/mysql-test/suite/innodb/r/xap_release_locks_on_dict_stats_table.result new file mode 100644 index 00000000000..1a849f1c477 --- /dev/null +++ b/mysql-test/suite/innodb/r/xap_release_locks_on_dict_stats_table.result @@ -0,0 +1,17 @@ +call mtr.add_suppression("bytes freed by"); +SET @old_innodb_stats_persistent = @@innodb_stats_persistent; +SET GLOBAL innodb_stats_persistent=1; +CREATE TABLE t ENGINE=InnoDB AS SELECT 1; +SET @old_debug_dbug = @@global.debug_dbug; +XA START 'a'; +INSERT INTO mysql.innodb_index_stats SELECT '','' AS table_name,index_name,LAST_UPDATE,stat_name,0 AS stat_value,sample_size,stat_description FROM mysql.innodb_index_stats WHERE table_name='dummy' FOR UPDATE; +SET GLOBAL debug_dbug = "+d,dict_stats_save_exit_notify"; +INSERT INTO t VALUES (1); +XA END 'a'; +XA PREPARE 'a'; +SET DEBUG_SYNC="now WAIT_FOR dict_stats_save_finished"; +SET @@global.debug_dbug = @old_debug_dbug; +SET DEBUG_SYNC="RESET"; +SET GLOBAL innodb_stats_persistent = @old_innodb_stats_persistent; +XA COMMIT 'a'; +DROP TABLE t; diff --git a/mysql-test/suite/innodb/t/innodb-table-online.test b/mysql-test/suite/innodb/t/innodb-table-online.test index 45b1bc1ec8e..4271c683e2f 100644 --- a/mysql-test/suite/innodb/t/innodb-table-online.test +++ b/mysql-test/suite/innodb/t/innodb-table-online.test @@ -2,6 +2,7 @@ --source include/innodb_encrypt_log.inc --source include/have_debug.inc --source include/have_debug_sync.inc +--source include/have_sequence.inc let $innodb_metrics_select= SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl'; @@ -440,6 +441,32 @@ connection con1; reap; DROP TABLE t1; connection default; + +--echo # +--echo # MDEV-28122 Optimize table crash while applying online log +--echo # +CREATE TABLE t1(f1 INT NOT NULL, f2 INT NOT NULL, + f3 CHAR(200), f4 CHAR(200), + f5 VARCHAR(87), PRIMARY KEY(f1))ENGINE=InnoDB; +INSERT INTO t1 VALUES(6000, 6000, "InnoDB", + "MariaDB", repeat('a', 87)); + +SET DEBUG_SYNC="inplace_after_index_build SIGNAL dml_start WAIT_FOR dml_commit"; +SEND ALTER TABLE t1 FORCE; + +connection con1; +SET DEBUG_SYNC="now WAIT_FOR dml_start"; +BEGIN; +INSERT INTO t1 SELECT seq, seq, "IDB", "MDB", repeat('a', 87) FROM seq_1_to_127; +INSERT INTO t1 VALUES(128, 128, "IDB", "MDB", repeat('a', 86)); +INSERT INTO t1 VALUES(129, 129, "idb", "mdb", repeat('a', 2)); +COMMIT; +SET DEBUG_SYNC="now SIGNAL dml_commit"; + +connection default; +reap; +CHECK TABLE t1; +DROP TABLE t1; SET DEBUG_SYNC=RESET; disconnect con1; diff --git a/mysql-test/suite/innodb/t/xa_prepare_reset_supremum_lock.test b/mysql-test/suite/innodb/t/xa_prepare_reset_supremum_lock.test index d285f6f4f3a..180e44d05ae 100644 --- a/mysql-test/suite/innodb/t/xa_prepare_reset_supremum_lock.test +++ b/mysql-test/suite/innodb/t/xa_prepare_reset_supremum_lock.test @@ -13,19 +13,36 @@ INSERT INTO t VALUES(20); SET TRANSACTION ISOLATION LEVEL REPEATABLE READ; XA START '1'; SELECT * FROM t WHERE a > 20 FOR UPDATE; +# The following INSERT is necessary because trx_prepare() resets locks +# only if there were modifications in transaction. INSERT INTO t VALUES(40); XA END '1'; XA PREPARE '1'; connect (con1,localhost,root); SET innodb_lock_wait_timeout=1; -# This will be finished with lock wait timeout error if XA PREPARE did not -# reset lock on supremum +# The following INSERT must not be blocked if XA PREPARE released supremum lock INSERT INTO t VALUES(50); + +--connection default +XA COMMIT '1'; + +XA START '1'; +SELECT * FROM t WHERE a > 20 LOCK IN SHARE MODE; +# The following INSERT is necessary because trx_prepare() resets locks +# only if there were modifications in transaction. +INSERT INTO t VALUES (5); +XA END '1'; +XA PREPARE '1'; + +--connection con1 +# The following INSERT must not be blocked if XA PREPARE released supremum lock +INSERT INTO t VALUES (60); +# The following INSERT must not be blocked if XA PREPARE released shared lock +INSERT INTO t VALUES (30); --disconnect con1 --connection default XA COMMIT '1'; DROP TABLE t; - --source include/wait_until_count_sessions.inc diff --git a/mysql-test/suite/innodb/t/xap_release_locks_on_dict_stats_table.test b/mysql-test/suite/innodb/t/xap_release_locks_on_dict_stats_table.test new file mode 100644 index 00000000000..a02a032ef61 --- /dev/null +++ b/mysql-test/suite/innodb/t/xap_release_locks_on_dict_stats_table.test @@ -0,0 +1,33 @@ +--source include/have_innodb.inc +--source include/have_debug.inc +--source include/have_debug_sync.inc + +# Some memory is allocated in dict_stats_save() function for additional sync +# point. The memory is allocated in current_thd->mem_root pool after +# dict_stats_func() arranged new thd and freed after destroy_background_thd() +# attached background thread thd to the current_thd. That's there are +# different thread id's for memory allocation and deallocation, what causes +# the following warnings. This is not an error because the memory is still +# allocated and deallocated by the same thread in pool. +call mtr.add_suppression("bytes freed by"); + +SET @old_innodb_stats_persistent = @@innodb_stats_persistent; +SET GLOBAL innodb_stats_persistent=1; +CREATE TABLE t ENGINE=InnoDB AS SELECT 1; + +SET @old_debug_dbug = @@global.debug_dbug; + +XA START 'a'; +INSERT INTO mysql.innodb_index_stats SELECT '','' AS table_name,index_name,LAST_UPDATE,stat_name,0 AS stat_value,sample_size,stat_description FROM mysql.innodb_index_stats WHERE table_name='dummy' FOR UPDATE; # Note the SELECT is empty +SET GLOBAL debug_dbug = "+d,dict_stats_save_exit_notify"; +INSERT INTO t VALUES (1); +XA END 'a'; +XA PREPARE 'a'; + +# Locking queue validation will crash the server if the bug is not fixed +SET DEBUG_SYNC="now WAIT_FOR dict_stats_save_finished"; +SET @@global.debug_dbug = @old_debug_dbug; +SET DEBUG_SYNC="RESET"; +SET GLOBAL innodb_stats_persistent = @old_innodb_stats_persistent; +XA COMMIT 'a'; +DROP TABLE t; diff --git a/sql/sql_alter.h b/sql/sql_alter.h index 9c421bb3a5f..1d1ad5b220a 100644 --- a/sql/sql_alter.h +++ b/sql/sql_alter.h @@ -199,6 +199,7 @@ public: Alter_info() : flags(0), partition_flags(0), keys_onoff(LEAVE_AS_IS), + original_table(0), num_parts(0), requested_algorithm(ALTER_TABLE_ALGORITHM_NONE), requested_lock(ALTER_TABLE_LOCK_DEFAULT) diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 37f9de5ac17..e308130b912 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -3070,7 +3070,8 @@ mysql_prepare_create_table_finalize(THD *thd, HA_CREATE_INFO *create_info, if (key->type == Key::IGNORE_KEY) { /* The key was replaced by another key */ - if (alter_info->add_stat_drop_index(thd, &key->name)) + if (!create_info->tmp_table() && + alter_info->add_stat_drop_index(thd, &key->name)) DBUG_RETURN(true); continue; } diff --git a/storage/innobase/dict/dict0stats.cc b/storage/innobase/dict/dict0stats.cc index d22f7eb3272..40969335f4f 100644 --- a/storage/innobase/dict/dict0stats.cc +++ b/storage/innobase/dict/dict0stats.cc @@ -34,6 +34,8 @@ Created Jan 06, 2010 Vasil Dimov #include "log.h" #include "btr0btr.h" #include "que0que.h" +#include "scope.h" +#include "debug_sync.h" #include #include @@ -3242,6 +3244,15 @@ dict_stats_save( char db_utf8[MAX_DB_UTF8_LEN]; char table_utf8[MAX_TABLE_UTF8_LEN]; +#ifdef ENABLED_DEBUG_SYNC + DBUG_EXECUTE_IF("dict_stats_save_exit_notify", + SCOPE_EXIT([] { + debug_sync_set_action(current_thd, + STRING_WITH_LEN("now SIGNAL dict_stats_save_finished")); + }); + ); +#endif /* ENABLED_DEBUG_SYNC */ + if (high_level_read_only) { return DB_READ_ONLY; } diff --git a/storage/innobase/fil/fil0fil.cc b/storage/innobase/fil/fil0fil.cc index eea1ac1fcb4..27f9a35fb4c 100644 --- a/storage/innobase/fil/fil0fil.cc +++ b/storage/innobase/fil/fil0fil.cc @@ -2384,6 +2384,17 @@ fil_ibd_discover( /* A datafile was not discovered for the filename given. */ return(false); } + +bool fil_crypt_check(fil_space_crypt_t *crypt_data, const char *f_name) +{ + if (crypt_data->is_key_found()) + return true; + sql_print_error("InnoDB: Encryption key is not found for %s", f_name); + crypt_data->~fil_space_crypt_t(); + ut_free(crypt_data); + return false; +} + /** Open an ibd tablespace and add it to the InnoDB data structures. This is similar to fil_ibd_open() except that it is used while processing the REDO log, so the data dictionary is not available and very little @@ -2529,9 +2540,7 @@ tablespace_check: first_page) : NULL; - if (crypt_data && !crypt_data->is_key_found()) { - crypt_data->~fil_space_crypt_t(); - ut_free(crypt_data); + if (crypt_data && !fil_crypt_check(crypt_data, filename)) { return FIL_LOAD_INVALID; } diff --git a/storage/innobase/include/fil0fil.h b/storage/innobase/include/fil0fil.h index 6fcd0029fa8..ebe5e998a31 100644 --- a/storage/innobase/include/fil0fil.h +++ b/storage/innobase/include/fil0fil.h @@ -1821,4 +1821,10 @@ void test_make_filepath(); @return block size */ ulint fil_space_get_block_size(const fil_space_t* space, unsigned offset); +/** Check whether encryption key found +@param crypt_data Encryption data +@param f_name File name +@return encryption key found */ +bool fil_crypt_check(fil_space_crypt_t *crypt_data, const char *f_name); + #endif /* UNIV_INNOCHECKSUM */ diff --git a/storage/innobase/lock/lock0lock.cc b/storage/innobase/lock/lock0lock.cc index e7638e8f3ba..31e02d2451a 100644 --- a/storage/innobase/lock/lock0lock.cc +++ b/storage/innobase/lock/lock0lock.cc @@ -4345,17 +4345,19 @@ static bool lock_release_on_prepare_try(trx_t *trx) { ut_ad(!lock->index->table->is_temporary()); bool supremum_bit = lock_rec_get_nth_bit(lock, PAGE_HEAP_NO_SUPREMUM); - if (!supremum_bit && lock->is_rec_granted_exclusive_not_gap()) + bool rec_granted_exclusive_not_gap = + lock->is_rec_granted_exclusive_not_gap(); + if (!supremum_bit && rec_granted_exclusive_not_gap) continue; auto &lock_hash= lock_sys.hash_get(lock->type_mode); auto cell= lock_hash.cell_get(lock->un_member.rec_lock.page_id.fold()); auto latch= lock_sys_t::hash_table::latch(cell); if (latch->try_acquire()) { - if (supremum_bit) - lock_rec_unlock_supremum(*cell, lock); - else + if (!rec_granted_exclusive_not_gap) lock_rec_dequeue_from_page(lock, false); + else if (supremum_bit) + lock_rec_unlock_supremum(*cell, lock); latch->release(); } else diff --git a/storage/innobase/log/log0recv.cc b/storage/innobase/log/log0recv.cc index db81af78184..85c7d596855 100644 --- a/storage/innobase/log/log0recv.cc +++ b/storage/innobase/log/log0recv.cc @@ -817,12 +817,8 @@ processed: const std::string &name, uint32_t flags, fil_space_crypt_t *crypt_data, uint32_t size) { - if (crypt_data && !crypt_data->is_key_found()) - { - crypt_data->~fil_space_crypt_t(); - ut_free(crypt_data); + if (crypt_data && !fil_crypt_check(crypt_data, name.c_str())) return nullptr; - } mysql_mutex_lock(&fil_system.mutex); fil_space_t *space= fil_space_t::create(it->first, flags, FIL_TYPE_TABLESPACE, crypt_data); diff --git a/storage/innobase/row/row0log.cc b/storage/innobase/row/row0log.cc index 3302bf934da..59f283539fa 100644 --- a/storage/innobase/row/row0log.cc +++ b/storage/innobase/row/row0log.cc @@ -2167,6 +2167,11 @@ row_log_table_apply_op( *error = DB_SUCCESS; + /* 3 = 1 (op type) + 1 (extra_size) + at least 1 byte payload */ + if (mrec + 3 >= mrec_end) { + return(NULL); + } + const bool is_instant = log->is_instant(dup->index); const mrec_t* const mrec_start = mrec; @@ -2214,11 +2219,6 @@ row_log_table_apply_op( break; case ROW_T_DELETE: - /* 1 (extra_size) + at least 1 (payload) */ - if (mrec + 2 >= mrec_end) { - return(NULL); - } - extra_size = *mrec++; ut_ad(mrec < mrec_end);