diff --git a/mysql-test/suite/galera/r/MDEV-28452.result b/mysql-test/suite/galera/r/MDEV-28452.result new file mode 100644 index 0000000000000..796ed6060382f --- /dev/null +++ b/mysql-test/suite/galera/r/MDEV-28452.result @@ -0,0 +1,60 @@ +connection node_2; +connection node_1; +connect node_2a, 127.0.0.1, root, , test, $NODE_MYPORT_2; +connection node_1; +CREATE TABLE `user` ( +`id` char(36) COLLATE utf8mb4_unicode_ci NOT NULL, +`environment_id` char(36) COLLATE utf8mb4_unicode_ci NOT NULL, +`username` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, +`password` char(60) COLLATE utf8mb4_unicode_ci NOT NULL, +`status` enum('sign_up','invited','active','archived') +COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'invited', +`last_login` datetime DEFAULT NULL, +PRIMARY KEY (`id`), +UNIQUE KEY `user_username_unique` (`username`), +KEY `user_environment_id_index` (`environment_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +CREATE TABLE `log_history_daily` ( +`type` enum('unknown','user','developer','api_key','api_env', +'mail_token','device') COLLATE utf8mb4_unicode_ci NOT NULL, +`status` enum('valid','invalid') COLLATE utf8mb4_unicode_ci NOT NULL, +`origin` enum('unknown','browser','go','android','ios','third_party') +COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'unknown', +`ip` varchar(45) COLLATE utf8mb4_unicode_ci NOT NULL, +`value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, +`user_id` char(36) COLLATE utf8mb4_unicode_ci DEFAULT NULL, +`date` date NOT NULL, +PRIMARY KEY (`type`,`status`,`origin`,`ip`,`value`,`date`), +KEY `log_history_daily_user_id_foreign` (`user_id`), +CONSTRAINT `log_history_daily_user_id_foreign` FOREIGN KEY (`user_id`) +REFERENCES `user` (`id`) ON DELETE SET NULL ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +SET SESSION autocommit = 0; +INSERT INTO `user` VALUES ("id_1", "abc", "user 1", "secret", "active", NULL); +INSERT INTO `log_history_daily` VALUES ("user", "valid", "unknown", +"192.168.0.1", "Sample", "id_1", "2024-04-22"); +COMMIT; +connection node_2; +SET GLOBAL wsrep_slave_threads = 2; +SELECT @@wsrep_slave_threads; +@@wsrep_slave_threads +2 +SET GLOBAL debug_dbug = "+d,sync.mdev_28452"; +connection node_1; +OPTIMIZE TABLE `log_history_daily`; +Table Op Msg_type Msg_text +test.log_history_daily optimize note Table does not support optimize, doing recreate + analyze instead +test.log_history_daily optimize status OK +COMMIT; +UPDATE `user` SET `last_login` = '2024-04-23' WHERE `id`="id_1"; +COMMIT; +connection node_2a; +SET DEBUG_SYNC='NOW WAIT_FOR mdev_28452_reached'; +SET GLOBAL debug_dbug = 'RESET'; +SET DEBUG_SYNC = 'now SIGNAL signal.mdev_28452'; +SET DEBUG_SYNC = 'RESET'; +connection node_1; +DROP TABLE `log_history_daily`; +DROP TABLE `user`; +connection node_2; +SET GLOBAL wsrep_slave_threads = DEFAULT; diff --git a/mysql-test/suite/galera/t/MDEV-28452.test b/mysql-test/suite/galera/t/MDEV-28452.test new file mode 100644 index 0000000000000..34b507c55559d --- /dev/null +++ b/mysql-test/suite/galera/t/MDEV-28452.test @@ -0,0 +1,93 @@ +# +# Test for MDEV-28452: wsrep_ready: OFF after MDL BF-BF conflict +# + +--source include/galera_cluster.inc +--source include/have_debug.inc +--source include/have_debug_sync.inc + +--connect node_2a, 127.0.0.1, root, , test, $NODE_MYPORT_2 + +--connection node_1 + +# create two tables +CREATE TABLE `user` ( + `id` char(36) COLLATE utf8mb4_unicode_ci NOT NULL, + `environment_id` char(36) COLLATE utf8mb4_unicode_ci NOT NULL, + `username` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, + `password` char(60) COLLATE utf8mb4_unicode_ci NOT NULL, + `status` enum('sign_up','invited','active','archived') + COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'invited', + `last_login` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `user_username_unique` (`username`), + KEY `user_environment_id_index` (`environment_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + + +CREATE TABLE `log_history_daily` ( + `type` enum('unknown','user','developer','api_key','api_env', + 'mail_token','device') COLLATE utf8mb4_unicode_ci NOT NULL, + `status` enum('valid','invalid') COLLATE utf8mb4_unicode_ci NOT NULL, + `origin` enum('unknown','browser','go','android','ios','third_party') + COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'unknown', + `ip` varchar(45) COLLATE utf8mb4_unicode_ci NOT NULL, + `value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, + `user_id` char(36) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `date` date NOT NULL, + PRIMARY KEY (`type`,`status`,`origin`,`ip`,`value`,`date`), + KEY `log_history_daily_user_id_foreign` (`user_id`), + CONSTRAINT `log_history_daily_user_id_foreign` FOREIGN KEY (`user_id`) + REFERENCES `user` (`id`) ON DELETE SET NULL ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + + +SET SESSION autocommit = 0; + +# insert one row in each table +INSERT INTO `user` VALUES ("id_1", "abc", "user 1", "secret", "active", NULL); +INSERT INTO `log_history_daily` VALUES ("user", "valid", "unknown", + "192.168.0.1", "Sample", "id_1", "2024-04-22"); +COMMIT; + +# Start two slave threads in node 2. +# Set up a synchronizaton point (sync.mdev_28452) for slave thread 1 +# where the thread stops when it holds an exclusive metadata lock +# (MDL_EXCLUSIVE) for the OPTIMIZE TABLE statement (see below). +--connection node_2 +SET GLOBAL wsrep_slave_threads = 2; +SELECT @@wsrep_slave_threads; +SET GLOBAL debug_dbug = "+d,sync.mdev_28452"; + +# Run two transactions sequentially on node 1: +# 1) OPTIMIZE TABLE on the child table of the foreign key constraint, +--connection node_1 +OPTIMIZE TABLE `log_history_daily`; +COMMIT; + +# 2) and UPDATE on the parent table of the foreign key constraint. +# update the row in "user" so that it affects the foreign key in +# "log_history_daily" +UPDATE `user` SET `last_login` = '2024-04-23' WHERE `id`="id_1"; +COMMIT; + +# allow the stopped OPTIMIZE TABLE transaction to proceed +--connection node_2a + +# wait till debug sync point has been set on node 2 +SET DEBUG_SYNC='NOW WAIT_FOR mdev_28452_reached'; + +SET GLOBAL debug_dbug = 'RESET'; +SET DEBUG_SYNC = 'now SIGNAL signal.mdev_28452'; +SET DEBUG_SYNC = 'RESET'; + +# wait till the UPDATE has finished on node 2 +--let $wait_condition = SELECT COUNT(*) > 0 FROM `user` WHERE `last_login` = '2024-04-23' +--source include/wait_condition.inc + +# cleanup +--connection node_1 +DROP TABLE `log_history_daily`; +DROP TABLE `user`; +--connection node_2 +SET GLOBAL wsrep_slave_threads = DEFAULT; diff --git a/sql/mdl.cc b/sql/mdl.cc index 30aaf8311c512..ebc89aaea0a94 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -2308,6 +2308,21 @@ MDL_context::acquire_lock(MDL_request *mdl_request, double lock_wait_timeout) */ DBUG_PRINT("info", ("Got lock without waiting")); DBUG_PRINT("mdl", ("Seized: %s", dbug_print_mdl(mdl_request->ticket))); +#ifdef WITH_WSREP +#ifdef ENABLED_DEBUG_SYNC + if (0 == strcmp(dbug_print_mdl(mdl_request->ticket), + "test/log_history_daily (MDL_EXCLUSIVE)")) { + DBUG_EXECUTE_IF("sync.mdev_28452", + { + const char act[]= + "NOW SIGNAL mdev_28452_reached " + "WAIT_FOR signal.mdev_28452"; + DBUG_ASSERT(!debug_sync_set_action(get_thd(), + STRING_WITH_LEN(act))); + };); + } +#endif /* ENABLED_DEBUG_SYNC */ +#endif /* WITH_WSREP! */ DBUG_RETURN(FALSE); } diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc index 2b4c99ebc9de6..0b5966c9f26c2 100644 --- a/sql/sql_admin.cc +++ b/sql/sql_admin.cc @@ -1608,7 +1608,38 @@ bool Sql_cmd_optimize_table::execute(THD *thd) FALSE, UINT_MAX, FALSE)) goto error; /* purecov: inspected */ - WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, first_table); +#ifdef WITH_WSREP + if (WSREP(thd) && + (!thd->is_current_stmt_binlog_format_row() || + !thd->find_temporary_table(first_table))) + { + /* + It makes sense to set auto_increment_* to defaults in TOI operations. + Must be done before wsrep_TOI_begin() since Query_log_event encapsulating + TOI statement and auto inc variables for wsrep replication is constructed + there. Variables are reset back in THD::reset_for_next_command() before + processing of next command. + */ + if (wsrep_auto_increment_control) + { + thd->variables.auto_increment_offset = 1; + thd->variables.auto_increment_increment = 1; + } + + wsrep::key_array keys; + if (wsrep_thd_is_local(thd) && + !wsrep_append_fk_parent_table(thd, first_table, &keys)) + { + WSREP_TO_ISOLATION_BEGIN_ALTER(first_table->db.str, + first_table->table_name.str, + first_table, NULL, &keys, NULL) + { + WSREP_WARN("OPTIMIZE TABLE isolation failure"); + DBUG_RETURN(TRUE); + } + } + } +#endif /* WITH_WSREP */ res= (specialflag & SPECIAL_NO_NEW_FUNC) ? mysql_recreate_table(thd, first_table, &recreate_info, true) : mysql_admin_table(thd, first_table, &m_lex->check_opt, @@ -1616,10 +1647,6 @@ bool Sql_cmd_optimize_table::execute(THD *thd) &handler::ha_optimize, 0, true); m_lex->first_select_lex()->table_list.first= first_table; m_lex->query_tables= first_table; - -#ifdef WITH_WSREP -wsrep_error_label: -#endif /* WITH_WSREP */ error: DBUG_RETURN(res); }