From 12f6db967bd1d83cf035aa281aee1a1d46b9b1a4 Mon Sep 17 00:00:00 2001 From: sjaakola Date: Mon, 1 Jun 2020 12:34:33 +0300 Subject: MDEV-22763 backporting MDEV-20225 fix into 10.1 Backported the support for aborting and replaying stored procedure and fix for trigger key assigments from 10.4 version. Backported also two mtr tests: wsrep_sp_bf_abort and MDEV-20225 --- mysql-test/suite/galera/r/MDEV-20225.result | 16 + .../suite/galera/r/galera_sp_bf_abort.result | 294 +++++++++++++++++ mysql-test/suite/galera/t/MDEV-20225.test | 49 +++ mysql-test/suite/galera/t/galera_sp_bf_abort.inc | 36 +++ mysql-test/suite/galera/t/galera_sp_bf_abort.test | 347 +++++++++++++++++++++ sql/sp_head.cc | 89 ++++++ sql/sql_trigger.cc | 16 +- sql/wsrep_hton.cc | 22 +- sql/wsrep_mysqld.cc | 9 +- sql/wsrep_thd.cc | 111 ++++++- sql/wsrep_thd.h | 1 + 11 files changed, 978 insertions(+), 12 deletions(-) create mode 100644 mysql-test/suite/galera/r/MDEV-20225.result create mode 100644 mysql-test/suite/galera/r/galera_sp_bf_abort.result create mode 100644 mysql-test/suite/galera/t/MDEV-20225.test create mode 100644 mysql-test/suite/galera/t/galera_sp_bf_abort.inc create mode 100644 mysql-test/suite/galera/t/galera_sp_bf_abort.test diff --git a/mysql-test/suite/galera/r/MDEV-20225.result b/mysql-test/suite/galera/r/MDEV-20225.result new file mode 100644 index 00000000000..582353f10a6 --- /dev/null +++ b/mysql-test/suite/galera/r/MDEV-20225.result @@ -0,0 +1,16 @@ +CREATE TABLE t1 (f1 INT NOT NULL PRIMARY KEY AUTO_INCREMENT) ENGINE=InnoDB; +CREATE TABLE t2 (f1 INT NOT NULL PRIMARY KEY AUTO_INCREMENT, f2 INT) ENGINE=InnoDB; +CREATE TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW INSERT INTO t2 VALUES (NULL, NEW.f1); +SET SESSION wsrep_sync_wait = 0; +SET GLOBAL wsrep_slave_threads = 2; +SET GLOBAL debug_dbug = 'd,sync.mdev_20225'; +DROP TRIGGER tr1; +INSERT INTO t1 VALUES (NULL); +SET GLOBAL debug_dbug = 'RESET'; +SET DEBUG_SYNC = 'now SIGNAL signal.mdev_20225_continue'; +SET DEBUG_SYNC = 'RESET'; +SET GLOBAL wsrep_slave_threads = 1; +SHOW TRIGGERS; +Trigger Event Table Statement Timing Created sql_mode Definer character_set_client collation_connection Database Collation +DROP TABLE t1; +DROP TABLE t2; diff --git a/mysql-test/suite/galera/r/galera_sp_bf_abort.result b/mysql-test/suite/galera/r/galera_sp_bf_abort.result new file mode 100644 index 00000000000..205d73dd763 --- /dev/null +++ b/mysql-test/suite/galera/r/galera_sp_bf_abort.result @@ -0,0 +1,294 @@ +CREATE TABLE t1 (f1 INTEGER PRIMARY KEY, f2 CHAR(1)); +SET SESSION wsrep_sync_wait = 0; +CREATE PROCEDURE proc_update_insert() +BEGIN +UPDATE t1 SET f2 = 'b'; +INSERT INTO t1 VALUES (4, 'd'); +END| +INSERT INTO t1 VALUES (1, 'a'), (3, 'a'); +SET SESSION wsrep_sync_wait = 0; +SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync'; +INSERT INTO t1 VALUES (2, 'c'); +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +SET GLOBAL wsrep_provider_options = 'dbug=d,after_replicate_sync'; +CALL proc_update_insert; +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync'; +SET GLOBAL wsrep_provider_options = 'signal=after_replicate_sync'; +SET SESSION wsrep_sync_wait = default; +SELECT * FROM t1; +f1 f2 +1 b +2 c +3 b +4 d +wsrep_local_replays +1 +DELETE FROM t1; +CREATE PROCEDURE proc_update_insert_with_exit_handler() +BEGIN +DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN END; +UPDATE t1 SET f2 = 'b'; +INSERT INTO t1 VALUES (4, 'd'); +END| +INSERT INTO t1 VALUES (1, 'a'), (3, 'a'); +SET SESSION wsrep_sync_wait = 0; +SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync'; +INSERT INTO t1 VALUES (2, 'c'); +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +SET GLOBAL wsrep_provider_options = 'dbug=d,after_replicate_sync'; +CALL proc_update_insert_with_exit_handler; +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync'; +SET GLOBAL wsrep_provider_options = 'signal=after_replicate_sync'; +SET SESSION wsrep_sync_wait = default; +SELECT * FROM t1; +f1 f2 +1 b +2 c +3 b +4 d +wsrep_local_replays +1 +DELETE FROM t1; +CREATE PROCEDURE proc_update_insert_with_continue_handler() +BEGIN +DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN END; +UPDATE t1 SET f2 = 'b'; +INSERT INTO t1 VALUES (4, 'd'); +END| +INSERT INTO t1 VALUES (1, 'a'), (3, 'a'); +SET SESSION wsrep_sync_wait = 0; +SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync'; +INSERT INTO t1 VALUES (2, 'c'); +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +SET GLOBAL wsrep_provider_options = 'dbug=d,after_replicate_sync'; +CALL proc_update_insert_with_continue_handler; +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync'; +SET GLOBAL wsrep_provider_options = 'signal=after_replicate_sync'; +SET SESSION wsrep_sync_wait = default; +SELECT * FROM t1; +f1 f2 +1 b +2 c +3 b +4 d +wsrep_local_replays +1 +DELETE FROM t1; +CREATE PROCEDURE proc_update_insert_transaction() +BEGIN +START TRANSACTION; +UPDATE t1 SET f2 = 'b'; +INSERT INTO t1 VALUES (4, 'd'); +COMMIT; +END| +INSERT INTO t1 VALUES (1, 'a'), (3, 'a'); +SET SESSION wsrep_sync_wait = 0; +SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync'; +INSERT INTO t1 VALUES (2, 'c'); +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +SET GLOBAL wsrep_provider_options = 'dbug=d,after_replicate_sync'; +CALL proc_update_insert_transaction; +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync'; +SET GLOBAL wsrep_provider_options = 'signal=after_replicate_sync'; +Warnings: +Error 1317 Query execution was interrupted +SET SESSION wsrep_sync_wait = default; +SELECT * FROM t1; +f1 f2 +1 b +2 c +3 b +4 d +wsrep_local_replays +1 +DELETE FROM t1; +CREATE PROCEDURE proc_update_insert_transaction_with_continue_handler() +BEGIN +DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN END; +START TRANSACTION; +UPDATE t1 SET f2 = 'b'; +INSERT INTO t1 VALUES (4, 'd'); +COMMIT; +END| +INSERT INTO t1 VALUES (1, 'a'), (3, 'a'); +SET SESSION wsrep_sync_wait = 0; +SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync'; +INSERT INTO t1 VALUES (2, 'c'); +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +SET GLOBAL wsrep_provider_options = 'dbug=d,after_replicate_sync'; +CALL proc_update_insert_transaction_with_continue_handler; +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync'; +SET GLOBAL wsrep_provider_options = 'signal=after_replicate_sync'; +Warnings: +Error 1317 Query execution was interrupted +SET SESSION wsrep_sync_wait = default; +SELECT * FROM t1; +f1 f2 +1 b +2 c +3 b +4 d +wsrep_local_replays +1 +DELETE FROM t1; +CREATE PROCEDURE proc_update_insert_transaction_with_exit_handler() +BEGIN +DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN END; +START TRANSACTION; +UPDATE t1 SET f2 = 'b'; +INSERT INTO t1 VALUES (4, 'd'); +COMMIT; +END| +INSERT INTO t1 VALUES (1, 'a'), (3, 'a'); +SET SESSION wsrep_sync_wait = 0; +SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync'; +INSERT INTO t1 VALUES (2, 'c'); +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +SET GLOBAL wsrep_provider_options = 'dbug=d,after_replicate_sync'; +CALL proc_update_insert_transaction_with_exit_handler; +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync'; +SET GLOBAL wsrep_provider_options = 'signal=after_replicate_sync'; +Warnings: +Error 1317 Query execution was interrupted +SET SESSION wsrep_sync_wait = default; +SELECT * FROM t1; +f1 f2 +1 b +2 c +3 b +4 d +wsrep_local_replays +1 +DELETE FROM t1; +CREATE PROCEDURE proc_insert_insert_conflict() +BEGIN +INSERT INTO t1 VALUES (2, 'd'); +INSERT INTO t1 VALUES (4, 'd'); +END| +INSERT INTO t1 VALUES (1, 'a'), (3, 'a'); +SET SESSION wsrep_sync_wait = 0; +SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync'; +INSERT INTO t1 VALUES (2, 'c'); +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +SET GLOBAL wsrep_provider_options = 'dbug=d,after_replicate_sync'; +CALL proc_insert_insert_conflict; +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync'; +SET GLOBAL wsrep_provider_options = 'signal=after_replicate_sync'; +Got one of the listed errors +SET SESSION wsrep_sync_wait = default; +SELECT * FROM t1; +f1 f2 +1 a +2 c +3 a +wsrep_local_replays +1 +DELETE FROM t1; +CREATE PROCEDURE proc_insert_insert_conflict_with_exit_handler() +BEGIN +DECLARE EXIT HANDLER FOR SQLEXCEPTION SELECT "Conflict exit handler"; +INSERT INTO t1 VALUES (2, 'd'); +INSERT INTO t1 VALUES (4, 'd'); +END| +INSERT INTO t1 VALUES (1, 'a'), (3, 'a'); +SET SESSION wsrep_sync_wait = 0; +SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync'; +INSERT INTO t1 VALUES (2, 'c'); +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +SET GLOBAL wsrep_provider_options = 'dbug=d,after_replicate_sync'; +CALL proc_insert_insert_conflict_with_exit_handler; +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync'; +SET GLOBAL wsrep_provider_options = 'signal=after_replicate_sync'; +Conflict exit handler +Conflict exit handler +SET SESSION wsrep_sync_wait = default; +SELECT * FROM t1; +f1 f2 +1 a +2 c +3 a +wsrep_local_replays +1 +DELETE FROM t1; +CREATE PROCEDURE proc_insert_insert_conflict_with_continue_handler() +BEGIN +DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SELECT "Conflict continue handler"; +INSERT INTO t1 VALUES (2, 'd'); +INSERT INTO t1 VALUES (4, 'd'); +END| +INSERT INTO t1 VALUES (1, 'a'), (3, 'a'); +SET SESSION wsrep_sync_wait = 0; +SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync'; +INSERT INTO t1 VALUES (2, 'c'); +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +SET GLOBAL wsrep_provider_options = 'dbug=d,after_replicate_sync'; +CALL proc_insert_insert_conflict_with_continue_handler; +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync'; +SET GLOBAL wsrep_provider_options = 'signal=after_replicate_sync'; +Conflict continue handler +Conflict continue handler +SET SESSION wsrep_sync_wait = default; +SELECT * FROM t1; +f1 f2 +1 a +2 c +3 a +4 d +wsrep_local_replays +1 +DELETE FROM t1; +DROP PROCEDURE proc_update_insert; +DROP PROCEDURE proc_update_insert_with_continue_handler; +DROP PROCEDURE proc_update_insert_with_exit_handler; +DROP PROCEDURE proc_update_insert_transaction; +DROP PROCEDURE proc_update_insert_transaction_with_continue_handler; +DROP PROCEDURE proc_update_insert_transaction_with_exit_handler; +DROP PROCEDURE proc_insert_insert_conflict; +DROP PROCEDURE proc_insert_insert_conflict_with_exit_handler; +DROP PROCEDURE proc_insert_insert_conflict_with_continue_handler; +DROP TABLE t1; diff --git a/mysql-test/suite/galera/t/MDEV-20225.test b/mysql-test/suite/galera/t/MDEV-20225.test new file mode 100644 index 00000000000..5fbd0965217 --- /dev/null +++ b/mysql-test/suite/galera/t/MDEV-20225.test @@ -0,0 +1,49 @@ +# +# MDEV-20225 - Verify that DROP TRIGGER gets keys assigned corresponding +# to all affected tables. +# + +--source include/galera_cluster.inc +--source include/have_innodb.inc + +CREATE TABLE t1 (f1 INT NOT NULL PRIMARY KEY AUTO_INCREMENT) ENGINE=InnoDB; +CREATE TABLE t2 (f1 INT NOT NULL PRIMARY KEY AUTO_INCREMENT, f2 INT) ENGINE=InnoDB; + +CREATE TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW INSERT INTO t2 VALUES (NULL, NEW.f1); + +--connection node_2 +SET SESSION wsrep_sync_wait = 0; +SET GLOBAL wsrep_slave_threads = 2; +SET GLOBAL debug_dbug = 'd,sync.mdev_20225'; + +--let $galera_connection_name = node_1a +--let $galera_server_number = 1 +--source include/galera_connect.inc +DROP TRIGGER tr1; + +--connection node_2 +--let $wait_condition = SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE STATE = 'debug sync point: now' +--source include/wait_condition.inc + + +--connection node_1 +INSERT INTO t1 VALUES (NULL); +# We must rely on sleep here. If the bug is fixed, the second applier +# is not allowed to go past apply monitor which would trigger the bug, +# so there is no sync point or condition to wait. +--sleep 1 + +--connection node_2 +SET GLOBAL debug_dbug = 'RESET'; +SET DEBUG_SYNC = 'now SIGNAL signal.mdev_20225_continue'; +SET DEBUG_SYNC = 'RESET'; +SET GLOBAL wsrep_slave_threads = 1; + +--let $wait_condition = SELECT COUNT(*) = 1 FROM test.t1; +--source include/wait_condition.inc + +# Trigger should now be dropped on node_2. +SHOW TRIGGERS; + +DROP TABLE t1; +DROP TABLE t2; diff --git a/mysql-test/suite/galera/t/galera_sp_bf_abort.inc b/mysql-test/suite/galera/t/galera_sp_bf_abort.inc new file mode 100644 index 00000000000..7ca8ecf20a9 --- /dev/null +++ b/mysql-test/suite/galera/t/galera_sp_bf_abort.inc @@ -0,0 +1,36 @@ +# +# Issue an INSERT for gap between 1 and 3 to node_2 and wait until it hits +# apply monitor sync point on node_1 +# + +--connection node_1a +--let $galera_sync_point = apply_monitor_slave_enter_sync +--source include/galera_set_sync_point.inc + +--connection node_2 +--eval $galera_sp_bf_abort_conflict + +--connection node_1a +--source include/galera_wait_sync_point.inc +--source include/galera_clear_sync_point.inc + +# Send a procedure to node_1 which should take a gap lock between +# rows 1 and 3. It does not conflict with INSERT from node_2 in +# certification. Park the UPDATE after replicate and let INSERT to +# continue applying, generating a BF abort. + +--let $galera_sync_point = after_replicate_sync +--source include/galera_set_sync_point.inc + +--connection node_1 +--send_eval CALL $galera_sp_bf_abort_proc + +--connection node_1a +--let $galera_sync_point = after_replicate_sync apply_monitor_slave_enter_sync +--source include/galera_wait_sync_point.inc +--source include/galera_clear_sync_point.inc + +--let $galera_sync_point = apply_monitor_slave_enter_sync +--source include/galera_signal_sync_point.inc +--let $galera_sync_point = after_replicate_sync +--source include/galera_signal_sync_point.inc diff --git a/mysql-test/suite/galera/t/galera_sp_bf_abort.test b/mysql-test/suite/galera/t/galera_sp_bf_abort.test new file mode 100644 index 00000000000..484e2ca478d --- /dev/null +++ b/mysql-test/suite/galera/t/galera_sp_bf_abort.test @@ -0,0 +1,347 @@ +# +# Test cases for stored procedure BF aborts. +# + +--source include/galera_cluster.inc +--source include/have_innodb.inc + +--connection node_1 + +CREATE TABLE t1 (f1 INTEGER PRIMARY KEY, f2 CHAR(1)); + +# Control connection for Galera sync point management +--connect node_1a, 127.0.0.1, root, , test, $NODE_MYPORT_1 +--connection node_1a +SET SESSION wsrep_sync_wait = 0; + +--connection node_1 +# +# Case 1a: Procedure does and UPDATE which will suffer BF abort +# but there is no actual conflict and non-conflicting INSERT. +# The expected outcome is that both UPDATE and INSERT will succedd +# and no errors are reported to the client, wsrep_local_replays is +# incremented by one. +# +DELIMITER |; +CREATE PROCEDURE proc_update_insert() +BEGIN + UPDATE t1 SET f2 = 'b'; + INSERT INTO t1 VALUES (4, 'd'); +END| +DELIMITER ;| + +INSERT INTO t1 VALUES (1, 'a'), (3, 'a'); +SET SESSION wsrep_sync_wait = 0; +--let $wsrep_local_replays_orig = `SELECT VARIABLE_VALUE FROM information_schema.global_status WHERE VARIABLE_NAME = 'wsrep_local_replays'` +--let $galera_sp_bf_abort_proc = proc_update_insert +--let $galera_sp_bf_abort_conflict = INSERT INTO t1 VALUES (2, 'c') +--source galera_sp_bf_abort.inc +--connection node_1 +--reap +SET SESSION wsrep_sync_wait = default; +SELECT * FROM t1; +--let $wsrep_local_replays_curr = `SELECT VARIABLE_VALUE FROM information_schema.global_status WHERE VARIABLE_NAME = 'wsrep_local_replays'` +--disable_query_log +--eval SELECT $wsrep_local_replays_curr - $wsrep_local_replays_orig = 1 AS wsrep_local_replays; +--enable_query_log + +DELETE FROM t1; + +--connection node_1 +# +# Case 1b: Procedure does and UPDATE which will suffer BF abort +# but there is no actual conflict and non-conflicting INSERT. +# An EXIT HANDLER is declared for the procedure. +# The expected outcome is that both UPDATE and INSERT will succedd +# and no errors are reported to the client, wsrep_local_replays is +# incremented by one. +# +DELIMITER |; +CREATE PROCEDURE proc_update_insert_with_exit_handler() +BEGIN + DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN END; + UPDATE t1 SET f2 = 'b'; + INSERT INTO t1 VALUES (4, 'd'); +END| +DELIMITER ;| + +INSERT INTO t1 VALUES (1, 'a'), (3, 'a'); +SET SESSION wsrep_sync_wait = 0; +--let $wsrep_local_replays_orig = `SELECT VARIABLE_VALUE FROM information_schema.global_status WHERE VARIABLE_NAME = 'wsrep_local_replays'` +--let $galera_sp_bf_abort_proc = proc_update_insert_with_exit_handler +--let $galera_sp_bf_abort_conflict = INSERT INTO t1 VALUES (2, 'c') +--source galera_sp_bf_abort.inc +--connection node_1 +--reap +SET SESSION wsrep_sync_wait = default; +SELECT * FROM t1; +--let $wsrep_local_replays_curr = `SELECT VARIABLE_VALUE FROM information_schema.global_status WHERE VARIABLE_NAME = 'wsrep_local_replays'` +--disable_query_log +--eval SELECT $wsrep_local_replays_curr - $wsrep_local_replays_orig = 1 AS wsrep_local_replays; +--enable_query_log + +DELETE FROM t1; + +--connection node_1 +# +# Case 1c: Procedure does and UPDATE which will suffer BF abort +# but there is no actual conflict and non-conflicting INSERT. +# A CONTINUE HANDLER is declared for the procedure. +# The expected outcome is that both UPDATE and INSERT will succedd +# and no errors are reported to the client, wsrep_local_replays is +# incremented by one. +# +DELIMITER |; +CREATE PROCEDURE proc_update_insert_with_continue_handler() +BEGIN + + DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN END; + UPDATE t1 SET f2 = 'b'; + INSERT INTO t1 VALUES (4, 'd'); +END| +DELIMITER ;| + +INSERT INTO t1 VALUES (1, 'a'), (3, 'a'); +SET SESSION wsrep_sync_wait = 0; +--let $wsrep_local_replays_orig = `SELECT VARIABLE_VALUE FROM information_schema.global_status WHERE VARIABLE_NAME = 'wsrep_local_replays'` +--let $galera_sp_bf_abort_proc = proc_update_insert_with_continue_handler +--let $galera_sp_bf_abort_conflict = INSERT INTO t1 VALUES (2, 'c') +--source galera_sp_bf_abort.inc +--connection node_1 +--reap +SET SESSION wsrep_sync_wait = default; +SELECT * FROM t1; +--let $wsrep_local_replays_curr = `SELECT VARIABLE_VALUE FROM information_schema.global_status WHERE VARIABLE_NAME = 'wsrep_local_replays'` +--disable_query_log +--eval SELECT $wsrep_local_replays_curr - $wsrep_local_replays_orig = 1 AS wsrep_local_replays; +--enable_query_log + +DELETE FROM t1; + +--connection node_1 +# +# Case 2a: UPDATE and INSERT are run inside a transaction and the transaction +# will be BF aborted on COMMIT. The expected outcome is that the transaction +# succeeds and no errors are reported to the client, wsrep_local_replays +# is incremented by one. +# + +DELIMITER |; +CREATE PROCEDURE proc_update_insert_transaction() +BEGIN + START TRANSACTION; + UPDATE t1 SET f2 = 'b'; + INSERT INTO t1 VALUES (4, 'd'); + COMMIT; +END| +DELIMITER ;| + +INSERT INTO t1 VALUES (1, 'a'), (3, 'a'); +--let $wsrep_local_replays_orig = `SELECT VARIABLE_VALUE FROM information_schema.global_status WHERE VARIABLE_NAME = 'wsrep_local_replays'` +--let $galera_sp_bf_abort_proc = proc_update_insert_transaction +--let $galera_sp_bf_abort_conflict = INSERT INTO t1 VALUES (2, 'c') +SET SESSION wsrep_sync_wait = 0; +--source galera_sp_bf_abort.inc +--connection node_1 +--reap +SET SESSION wsrep_sync_wait = default; +SELECT * FROM t1; +--let $wsrep_local_replays_curr = `SELECT VARIABLE_VALUE FROM information_schema.global_status WHERE VARIABLE_NAME = 'wsrep_local_replays'` +--disable_query_log +--eval SELECT $wsrep_local_replays_curr - $wsrep_local_replays_orig = 1 AS wsrep_local_replays; +--enable_query_log + +DELETE FROM t1; + +--connection node_1 +# +# Case 2b: UPDATE and INSERT are run inside a transaction and the transaction +# will be BF aborted on COMMIT. A CONTINUE HANDLER is declared for the +# procedure. The expected outcome is that the transaction +# succeeds and no errors are reported to the client, wsrep_local_replays +# is incremented by one. +# + +DELIMITER |; +CREATE PROCEDURE proc_update_insert_transaction_with_continue_handler() +BEGIN + DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN END; + START TRANSACTION; + UPDATE t1 SET f2 = 'b'; + INSERT INTO t1 VALUES (4, 'd'); + COMMIT; +END| +DELIMITER ;| + +INSERT INTO t1 VALUES (1, 'a'), (3, 'a'); +--let $wsrep_local_replays_orig = `SELECT VARIABLE_VALUE FROM information_schema.global_status WHERE VARIABLE_NAME = 'wsrep_local_replays'` +--let $galera_sp_bf_abort_proc = proc_update_insert_transaction_with_continue_handler +--let $galera_sp_bf_abort_conflict = INSERT INTO t1 VALUES (2, 'c') +SET SESSION wsrep_sync_wait = 0; +--source galera_sp_bf_abort.inc +--connection node_1 +--reap +SET SESSION wsrep_sync_wait = default; +SELECT * FROM t1; +--let $wsrep_local_replays_curr = `SELECT VARIABLE_VALUE FROM information_schema.global_status WHERE VARIABLE_NAME = 'wsrep_local_replays'` +--disable_query_log +--eval SELECT $wsrep_local_replays_curr - $wsrep_local_replays_orig = 1 AS wsrep_local_replays; +--enable_query_log + +DELETE FROM t1; + +--connection node_1 +# +# Case 2c: UPDATE and INSERT are run inside a transaction and the transaction +# will be BF aborted on COMMIT. An EXIT HANDLE is declared for the procedure. +# The expected outcome is that the transaction succeeds and no errors are +# reported to the client, wsrep_local_replays is incremented by one. +# + +DELIMITER |; +CREATE PROCEDURE proc_update_insert_transaction_with_exit_handler() +BEGIN + DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN END; + START TRANSACTION; + UPDATE t1 SET f2 = 'b'; + INSERT INTO t1 VALUES (4, 'd'); + COMMIT; +END| +DELIMITER ;| + +INSERT INTO t1 VALUES (1, 'a'), (3, 'a'); +--let $wsrep_local_replays_orig = `SELECT VARIABLE_VALUE FROM information_schema.global_status WHERE VARIABLE_NAME = 'wsrep_local_replays'` +--let $galera_sp_bf_abort_proc = proc_update_insert_transaction_with_exit_handler +--let $galera_sp_bf_abort_conflict = INSERT INTO t1 VALUES (2, 'c') +SET SESSION wsrep_sync_wait = 0; +--source galera_sp_bf_abort.inc +--connection node_1 +--reap +SET SESSION wsrep_sync_wait = default; +SELECT * FROM t1; +--let $wsrep_local_replays_curr = `SELECT VARIABLE_VALUE FROM information_schema.global_status WHERE VARIABLE_NAME = 'wsrep_local_replays'` +--disable_query_log +--eval SELECT $wsrep_local_replays_curr - $wsrep_local_replays_orig = 1 AS wsrep_local_replays; +--enable_query_log + +DELETE FROM t1; + +--connection node_1 + +# +# Case 3a: Two INSERTs are run inside stored procedure, this time +# the first INSERT will have a BF abort and real conflict. The expected outcome +# is that the INSERT fails and an error is reported to the client. +# wsrep_local_replays is not incremented. +# +# Notice that the resulting error code may be both +# ER_DUP_ENTRY (procedure will exit with cert failure conflict state and +# will be) or ER_LOCK_DEADLOCK depending on timing. +# +DELIMITER |; +CREATE PROCEDURE proc_insert_insert_conflict() +BEGIN + INSERT INTO t1 VALUES (2, 'd'); + INSERT INTO t1 VALUES (4, 'd'); +END| +DELIMITER ;| + +INSERT INTO t1 VALUES (1, 'a'), (3, 'a'); +--let $wsrep_local_replays_orig = `SELECT VARIABLE_VALUE FROM information_schema.global_status WHERE VARIABLE_NAME = 'wsrep_local_replays'` +--let $galera_sp_bf_abort_proc = proc_insert_insert_conflict +--let $galera_sp_bf_abort_conflict = INSERT INTO t1 VALUES (2, 'c') +SET SESSION wsrep_sync_wait = 0; +--source galera_sp_bf_abort.inc +--connection node_1 +--error ER_DUP_ENTRY,ER_LOCK_DEADLOCK, ER_ERROR_DURING_COMMIT +--reap +SET SESSION wsrep_sync_wait = default; +SELECT * FROM t1; +--let $wsrep_local_replays_curr = `SELECT VARIABLE_VALUE FROM information_schema.global_status WHERE VARIABLE_NAME = 'wsrep_local_replays'` +--disable_query_log +--eval SELECT $wsrep_local_replays_curr - $wsrep_local_replays_orig = 0 AS wsrep_local_replays; +--enable_query_log + +DELETE FROM t1; + +--connection node_1 + +# +# Case 3b: Two INSERTs are run inside stored procedure, this time +# the first INSERT will have a BF abort and real conflict. +# An EXIT HANDLER is declared for the procedure. The expected outcome +# is that the INSERT fails and an error is reported to the client. +# wsrep_local_replays is not incremented. +# +DELIMITER |; +CREATE PROCEDURE proc_insert_insert_conflict_with_exit_handler() +BEGIN + DECLARE EXIT HANDLER FOR SQLEXCEPTION SELECT "Conflict exit handler"; + INSERT INTO t1 VALUES (2, 'd'); + INSERT INTO t1 VALUES (4, 'd'); +END| +DELIMITER ;| + +INSERT INTO t1 VALUES (1, 'a'), (3, 'a'); +--let $wsrep_local_replays_orig = `SELECT VARIABLE_VALUE FROM information_schema.global_status WHERE VARIABLE_NAME = 'wsrep_local_replays'` +--let $galera_sp_bf_abort_proc = proc_insert_insert_conflict_with_exit_handler +--let $galera_sp_bf_abort_conflict = INSERT INTO t1 VALUES (2, 'c') +SET SESSION wsrep_sync_wait = 0; +--source galera_sp_bf_abort.inc +--connection node_1 +--reap +SET SESSION wsrep_sync_wait = default; +SELECT * FROM t1; +--let $wsrep_local_replays_curr = `SELECT VARIABLE_VALUE FROM information_schema.global_status WHERE VARIABLE_NAME = 'wsrep_local_replays'` +--disable_query_log +--eval SELECT $wsrep_local_replays_curr - $wsrep_local_replays_orig = 0 AS wsrep_local_replays; +--enable_query_log + +DELETE FROM t1; + +--connection node_1 + +# +# Case 3c: Two INSERTs are run inside stored procedure, this time +# the first INSERT will have a BF abort and real conflict. +# A CONTINUE HANDLER is declared for the procedure. The expected outcome +# is that the the first INSERT fails but the second is executed without +# errors. wsrep_local_replays is not incremented. +# +DELIMITER |; +CREATE PROCEDURE proc_insert_insert_conflict_with_continue_handler() +BEGIN + DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SELECT "Conflict continue handler"; + INSERT INTO t1 VALUES (2, 'd'); + INSERT INTO t1 VALUES (4, 'd'); +END| +DELIMITER ;| + +INSERT INTO t1 VALUES (1, 'a'), (3, 'a'); +--let $wsrep_local_replays_orig = `SELECT VARIABLE_VALUE FROM information_schema.global_status WHERE VARIABLE_NAME = 'wsrep_local_replays'` +--let $galera_sp_bf_abort_proc = proc_insert_insert_conflict_with_continue_handler +--let $galera_sp_bf_abort_conflict = INSERT INTO t1 VALUES (2, 'c') +SET SESSION wsrep_sync_wait = 0; +--source galera_sp_bf_abort.inc +--connection node_1 +--reap +SET SESSION wsrep_sync_wait = default; +SELECT * FROM t1; +--let $wsrep_local_replays_curr = `SELECT VARIABLE_VALUE FROM information_schema.global_status WHERE VARIABLE_NAME = 'wsrep_local_replays'` +--disable_query_log +--eval SELECT $wsrep_local_replays_curr - $wsrep_local_replays_orig = 0 AS wsrep_local_replays; +--enable_query_log + +DELETE FROM t1; + + +DROP PROCEDURE proc_update_insert; +DROP PROCEDURE proc_update_insert_with_continue_handler; +DROP PROCEDURE proc_update_insert_with_exit_handler; +DROP PROCEDURE proc_update_insert_transaction; +DROP PROCEDURE proc_update_insert_transaction_with_continue_handler; +DROP PROCEDURE proc_update_insert_transaction_with_exit_handler; +DROP PROCEDURE proc_insert_insert_conflict; +DROP PROCEDURE proc_insert_insert_conflict_with_exit_handler; +DROP PROCEDURE proc_insert_insert_conflict_with_continue_handler; +DROP TABLE t1; diff --git a/sql/sp_head.cc b/sql/sp_head.cc index f940040b480..0428c0198a1 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -44,6 +44,9 @@ #include "transaction.h" // trans_commit_stmt #include "sql_audit.h" #include "debug_sync.h" +#ifdef WITH_WSREP +#include "wsrep_thd.h" +#endif /* WITH_WSREP */ /* Sufficient max length of printed destinations and frame offsets (all uints). @@ -1307,7 +1310,93 @@ sp_head::execute(THD *thd, bool merge_da_on_success) thd->m_digest= NULL; err_status= i->execute(thd, &ip); +#ifdef WITH_WSREP + if (m_type == TYPE_ENUM_PROCEDURE) + { + mysql_mutex_lock(&thd->LOCK_thd_data); + if (thd->wsrep_conflict_state == MUST_REPLAY) + { + wsrep_replay_sp_transaction(thd); + err_status= thd->get_stmt_da()->is_set(); + thd->wsrep_conflict_state= NO_CONFLICT; + } + else if (thd->wsrep_conflict_state == ABORTED || + thd->wsrep_conflict_state == CERT_FAILURE) + { + /* + If the statement execution was BF aborted or was aborted + due to certification failure, clean up transaction here + and reset conflict state to NO_CONFLICT and thd->killed + to THD::NOT_KILLED. Error handling is done based on err_status + below. Error must have been raised by wsrep hton code before + entering here. + */ + DBUG_ASSERT(err_status); + DBUG_ASSERT(thd->get_stmt_da()->is_error()); + thd->wsrep_conflict_state= NO_CONFLICT; + thd->killed= NOT_KILLED; + } + mysql_mutex_unlock(&thd->LOCK_thd_data); + } +#endif /* WITH_WSREP */ +#ifdef WITH_WSREP_NO + if (WSREP(thd)) + { + if (((thd->wsrep_trx().state() == wsrep::transaction::s_executing) && + (thd->is_fatal_error || thd->killed))) + { + WSREP_DEBUG("SP abort err status %d in sub %d trx state %d", + err_status, thd->in_sub_stmt, thd->wsrep_trx().state()); + err_status= 1; + thd->is_fatal_error= 1; + /* + SP was killed, and it is not due to a wsrep conflict. + We skip after_command hook at this point because + otherwise it clears the error, and cleans up the + whole transaction. For now we just return and finish + our handling once we are back to mysql_parse. + */ + WSREP_DEBUG("Skipping after_command hook for killed SP"); + } + else + { + const bool must_replay= wsrep_must_replay(thd); + if (must_replay) + { + WSREP_DEBUG("MUST_REPLAY set after SP, err_status %d trx state: %d", + err_status, thd->wsrep_trx().state()); + } + (void) wsrep_after_statement(thd); + /* + Reset the return code to zero if the transaction was + replayed succesfully. + */ + if (must_replay && !wsrep_current_error(thd)) + { + err_status= 0; + thd->get_stmt_da()->reset_diagnostics_area(); + } + /* + Final wsrep error status for statement is known only after + wsrep_after_statement() call. If the error is set, override + error in thd diagnostics area and reset wsrep client_state error + so that the error does not get propagated via client-server protocol. + */ + if (wsrep_current_error(thd)) + { + wsrep_override_error(thd, wsrep_current_error(thd), + wsrep_current_error_status(thd)); + thd->wsrep_cs().reset_error(); + /* Reset also thd->killed if it has been set during BF abort. */ + if (thd->killed == KILL_QUERY) + thd->killed= NOT_KILLED; + /* if failed transaction was not replayed, must return with error from here */ + if (!must_replay) err_status = 1; + } + } + } +#endif /* WITH_WSREP */ thd->m_digest= parent_digest; if (i->free_list) diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index c4d348ce400..2972ceecd8a 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -34,7 +34,9 @@ #include "sql_handler.h" // mysql_ha_rm_tables #include "sp_cache.h" // sp_invalidate_cache #include - +#ifdef WITH_WSREP +#include "debug_sync.h" +#endif /* WITH_WSREP */ /*************************************************************************/ template @@ -506,7 +508,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) #ifdef WITH_WSREP if (thd->wsrep_exec_mode == LOCAL_STATE) - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, tables); #endif /* We should have only one table in table list. */ @@ -568,6 +570,16 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) goto end; } +#ifdef WITH_WSREP + DBUG_EXECUTE_IF("sync.mdev_20225", + { + const char act[]= + "now " + "wait_for signal.mdev_20225_continue"; + DBUG_ASSERT(!debug_sync_set_action(thd, + STRING_WITH_LEN(act))); + };); +#endif /* WITH_WSREP */ result= (create ? table->triggers->create_trigger(thd, tables, &stmt_query): table->triggers->drop_trigger(thd, tables, &stmt_query)); diff --git a/sql/wsrep_hton.cc b/sql/wsrep_hton.cc index d8f82b13108..030c4244a30 100644 --- a/sql/wsrep_hton.cc +++ b/sql/wsrep_hton.cc @@ -37,6 +37,8 @@ extern "C" int thd_binlog_format(const MYSQL_THD thd); void wsrep_cleanup_transaction(THD *thd) { if (!WSREP(thd)) return; + DBUG_ASSERT(thd->wsrep_conflict_state != MUST_REPLAY && + thd->wsrep_conflict_state != REPLAYING); if (wsrep_emulate_bin_log) thd_binlog_trx_reset(thd); thd->wsrep_ws_handle.trx_id= WSREP_UNDEFINED_TRX_ID; @@ -136,7 +138,11 @@ void wsrep_post_commit(THD* thd, bool all) /* non-InnoDB statements may have populated events in stmt cache => cleanup */ - WSREP_DEBUG("cleanup transaction for LOCAL_STATE"); + if (thd->wsrep_conflict_state != MUST_REPLAY) + { + WSREP_DEBUG("cleanup transaction for LOCAL_STATE: %s", + WSREP_QUERY(thd)); + } /* Run post-rollback hook to clean up in the case if some keys were populated for the transaction in provider @@ -145,13 +151,18 @@ void wsrep_post_commit(THD* thd, bool all) rolls back to savepoint after first operation. */ if (all && thd->wsrep_conflict_state != MUST_REPLAY && - wsrep && wsrep->post_rollback(wsrep, &thd->wsrep_ws_handle)) + thd->wsrep_conflict_state != REPLAYING && + wsrep->post_rollback(wsrep, &thd->wsrep_ws_handle)) { WSREP_WARN("post_rollback fail: %llu %d", (long long)thd->thread_id, thd->get_stmt_da()->status()); } - wsrep_cleanup_transaction(thd); - break; + if (thd->wsrep_conflict_state != MUST_REPLAY && + thd->wsrep_conflict_state != REPLAYING) + { + wsrep_cleanup_transaction(thd); + } + break; } default: break; } @@ -575,7 +586,8 @@ wsrep_run_wsrep_commit(THD *thd, bool all) DBUG_ASSERT(thd->wsrep_trx_meta.gtid.seqno != WSREP_SEQNO_UNDEFINED); /* fall through */ case WSREP_TRX_FAIL: - WSREP_DEBUG("commit failed for reason: %d", rcode); + WSREP_DEBUG("commit failed for reason: %d conf %d", + rcode, thd->wsrep_conflict_state); DBUG_PRINT("wsrep", ("replicating commit fail")); thd->wsrep_query_state= QUERY_EXEC; diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index f38bf85cd1a..38f8ca413db 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -1570,7 +1570,6 @@ static bool wsrep_can_run_in_toi(THD *thd, const char *db, const char *table, case SQLCOM_CREATE_TRIGGER: - DBUG_ASSERT(!table_list); DBUG_ASSERT(first_table); if (find_temporary_table(thd, first_table)) @@ -1579,6 +1578,14 @@ static bool wsrep_can_run_in_toi(THD *thd, const char *db, const char *table, } return true; + case SQLCOM_DROP_TRIGGER: + DBUG_ASSERT(table_list); + if (find_temporary_table(thd, table_list)) + { + return false; + } + return true; + default: if (table && !find_temporary_table(thd, db, table)) { diff --git a/sql/wsrep_thd.cc b/sql/wsrep_thd.cc index fad9e3f70c8..d37af157783 100644 --- a/sql/wsrep_thd.cc +++ b/sql/wsrep_thd.cc @@ -90,10 +90,10 @@ void wsrep_client_rollback(THD *thd) #define NUMBER_OF_FIELDS_TO_IDENTIFY_COORDINATOR 1 #define NUMBER_OF_FIELDS_TO_IDENTIFY_WORKER 2 -static rpl_group_info* wsrep_relay_group_init(const char* log_fname) +static rpl_group_info* wsrep_relay_group_init(THD *thd, const char* log_fname) { Relay_log_info* rli= new Relay_log_info(false); - + WSREP_DEBUG("wsrep_relay_group_init %s", log_fname); rli->no_storage= true; if (!rli->relay_log.description_event_for_exec) { @@ -123,7 +123,7 @@ static rpl_group_info* wsrep_relay_group_init(const char* log_fname) rli->mi = new Master_info(&connection_name, false); struct rpl_group_info *rgi= new rpl_group_info(rli); - rgi->thd= rli->sql_driver_thd= current_thd; + rgi->thd= rli->sql_driver_thd= thd; if ((rgi->deferred_events_collecting= rli->mi->rpl_filter->is_on())) { @@ -148,7 +148,7 @@ static void wsrep_prepare_bf_thd(THD *thd, struct wsrep_thd_shadow* shadow) else thd->variables.option_bits&= ~(OPTION_BIN_LOG); - if (!thd->wsrep_rgi) thd->wsrep_rgi= wsrep_relay_group_init("wsrep_relay"); + if (!thd->wsrep_rgi) thd->wsrep_rgi= wsrep_relay_group_init(thd, "wsrep_relay"); /* thd->system_thread_info.rpl_sql_info isn't initialized. */ thd->system_thread_info.rpl_sql_info= @@ -189,6 +189,109 @@ static void wsrep_return_from_bf_mode(THD *thd, struct wsrep_thd_shadow* shadow) thd->set_row_count_func(shadow->row_count_func); } +void wsrep_replay_sp_transaction(THD* thd) +{ + DBUG_ENTER("wsrep_replay_sp_transaction"); + mysql_mutex_assert_owner(&thd->LOCK_thd_data); + DBUG_ASSERT(thd->wsrep_conflict_state == MUST_REPLAY); + DBUG_ASSERT(wsrep_thd_trx_seqno(thd) > 0); + + WSREP_DEBUG("replaying SP transaction %llu", thd->thread_id); + close_thread_tables(thd); + if (thd->locked_tables_mode && thd->lock) + { + WSREP_DEBUG("releasing table lock for replaying (%u)", + thd->thread_id); + thd->locked_tables_list.unlock_locked_tables(thd); + thd->variables.option_bits&= ~(OPTION_TABLE_LOCK); + } + thd->mdl_context.release_transactional_locks(); + + mysql_mutex_unlock(&thd->LOCK_thd_data); + THD *replay_thd= new THD(true); + replay_thd->thread_stack= thd->thread_stack; + + struct wsrep_thd_shadow shadow; + wsrep_prepare_bf_thd(replay_thd, &shadow); + WSREP_DEBUG("replaying set for %p rgi %p", replay_thd, replay_thd->wsrep_rgi); replay_thd->wsrep_trx_meta= thd->wsrep_trx_meta; + replay_thd->wsrep_ws_handle= thd->wsrep_ws_handle; + replay_thd->wsrep_ws_handle.trx_id= WSREP_UNDEFINED_TRX_ID; + replay_thd->wsrep_conflict_state= REPLAYING; + + replay_thd->variables.option_bits|= OPTION_BEGIN; + replay_thd->server_status|= SERVER_STATUS_IN_TRANS; + + thd->reset_globals(); + replay_thd->store_globals(); + wsrep_status_t rcode= wsrep->replay_trx(wsrep, + &replay_thd->wsrep_ws_handle, + (void*) replay_thd); + + wsrep_return_from_bf_mode(replay_thd, &shadow); + replay_thd->reset_globals(); + delete replay_thd; + + mysql_mutex_lock(&thd->LOCK_thd_data); + + thd->store_globals(); + + switch (rcode) + { + case WSREP_OK: + { + thd->wsrep_conflict_state= NO_CONFLICT; + thd->killed= NOT_KILLED; + wsrep_status_t rcode= wsrep->post_commit(wsrep, &thd->wsrep_ws_handle); + if (rcode != WSREP_OK) + { + WSREP_WARN("Post commit failed for SP replay: thd: %u error: %d", + thd->thread_id, rcode); + } + /* As replaying the transaction was successful, an error must not + be returned to client, so we need to reset the error state of + the diagnostics area */ + thd->get_stmt_da()->reset_diagnostics_area(); + break; + } + case WSREP_TRX_FAIL: + { + thd->wsrep_conflict_state= ABORTED; + wsrep_status_t rcode= wsrep->post_rollback(wsrep, &thd->wsrep_ws_handle); + if (rcode != WSREP_OK) + { + WSREP_WARN("Post rollback failed for SP replay: thd: %u error: %d", + thd->thread_id, rcode); + } + if (thd->get_stmt_da()->is_set()) + { + thd->get_stmt_da()->reset_diagnostics_area(); + } + my_error(ER_LOCK_DEADLOCK, MYF(0)); + break; + } + default: + WSREP_ERROR("trx_replay failed for: %d, schema: %s, query: %s", + rcode, + (thd->db ? thd->db : "(null)"), + WSREP_QUERY(thd)); + /* we're now in inconsistent state, must abort */ + mysql_mutex_unlock(&thd->LOCK_thd_data); + unireg_abort(1); + break; + } + + wsrep_cleanup_transaction(thd); + + mysql_mutex_lock(&LOCK_wsrep_replaying); + wsrep_replaying--; + WSREP_DEBUG("replaying decreased: %d, thd: %u", + wsrep_replaying, thd->thread_id); + mysql_cond_broadcast(&COND_wsrep_replaying); + mysql_mutex_unlock(&LOCK_wsrep_replaying); + + DBUG_VOID_RETURN; +} + void wsrep_replay_transaction(THD *thd) { DBUG_ENTER("wsrep_replay_transaction"); diff --git a/sql/wsrep_thd.h b/sql/wsrep_thd.h index 6ce14a4eb0e..8e82a0fbe21 100644 --- a/sql/wsrep_thd.h +++ b/sql/wsrep_thd.h @@ -25,6 +25,7 @@ int wsrep_show_bf_aborts (THD *thd, SHOW_VAR *var, char *buff, enum enum_var_type scope); void wsrep_client_rollback(THD *thd); +void wsrep_replay_sp_transaction(THD* thd); void wsrep_replay_transaction(THD *thd); void wsrep_create_appliers(long threads); void wsrep_create_rollbacker(); -- cgit v1.2.1