Skip to content

Commit

Permalink
MW-388 Fix conflict handling of SPs with DECLARE ... HANDLER
Browse files Browse the repository at this point in the history
It is possible for a stored procedure that has an error handler
that catches SQLEXCEPTION to call thd->clear_error() on a thd
that failed certification. And because the error is cleared,
wsrep patch proceeds with the normal path and may try to commit
statements that should actually abort.
This patch catches the situation where wsrep_conflict_state is
still set, but the thd's error has been cleared, and rolls back
the statement in such cases.
  • Loading branch information
sciascid authored and Jan Lindström committed Nov 8, 2017
1 parent 3cecb1b commit 76f1195
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 0 deletions.
33 changes: 33 additions & 0 deletions mysql-test/suite/galera/r/MW-388.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
CREATE TABLE t1 (f1 INTEGER PRIMARY KEY, f2 CHAR(255)) Engine=InnoDB;
CREATE PROCEDURE insert_proc ()
BEGIN
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
BEGIN
GET DIAGNOSTICS CONDITION 1 @errno = MYSQL_ERRNO;
END;
INSERT INTO t1 VALUES (1, 'node 1'),(2, 'node 1');
INSERT INTO t1 VALUES (3, 'node 1');
END|
SET GLOBAL DEBUG = "d,sync.wsrep_apply_cb";
INSERT INTO t1 VALUES (1, 'node 2');;
SET SESSION DEBUG_SYNC = "now WAIT_FOR sync.wsrep_apply_cb_reached";
SET SESSION wsrep_sync_wait = 0;
SET SESSION DEBUG_SYNC = 'wsrep_before_replication SIGNAL wsrep_before_replication_reached WAIT_FOR wsrep_before_replication_continue';
CALL insert_proc ();;
SET SESSION DEBUG_SYNC = "now WAIT_FOR wsrep_before_replication_reached";
SET GLOBAL DEBUG = "";
SET DEBUG_SYNC = "now SIGNAL wsrep_before_replication_continue";
SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_cb";
SELECT @errno;
@errno
1213
SELECT * FROM t1;
f1 f2
1 node 2
3 node 1
SELECT * FROM t1;
f1 f2
1 node 2
3 node 1
DROP TABLE t1;
DROP PROCEDURE insert_proc;
54 changes: 54 additions & 0 deletions mysql-test/suite/galera/t/MW-388.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
--source include/galera_cluster.inc
--source include/have_innodb.inc

--connection node_1
CREATE TABLE t1 (f1 INTEGER PRIMARY KEY, f2 CHAR(255)) Engine=InnoDB;

DELIMITER |;
CREATE PROCEDURE insert_proc ()
BEGIN
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
BEGIN
GET DIAGNOSTICS CONDITION 1 @errno = MYSQL_ERRNO;
END;
INSERT INTO t1 VALUES (1, 'node 1'),(2, 'node 1');
INSERT INTO t1 VALUES (3, 'node 1');
END|
DELIMITER ;|

SET GLOBAL DEBUG = "d,sync.wsrep_apply_cb";

--connection node_2
--send INSERT INTO t1 VALUES (1, 'node 2');

--connect node_1a, 127.0.0.1, root, , test, $NODE_MYPORT_1
--connection node_1a
SET SESSION DEBUG_SYNC = "now WAIT_FOR sync.wsrep_apply_cb_reached";

--connection node_1
SET SESSION wsrep_sync_wait = 0;
SET SESSION DEBUG_SYNC = 'wsrep_before_replication SIGNAL wsrep_before_replication_reached WAIT_FOR wsrep_before_replication_continue';
--send CALL insert_proc ();

--connection node_1a
SET SESSION DEBUG_SYNC = "now WAIT_FOR wsrep_before_replication_reached";

--connection node_1a
SET GLOBAL DEBUG = "";
SET DEBUG_SYNC = "now SIGNAL wsrep_before_replication_continue";
SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_cb";

--connection node_2
--reap

--connection node_1
# We expect no errors here, because the handler in insert_proc() caught the deadlock error
--reap
SELECT @errno;
SELECT * FROM t1;

--connection node_2
SELECT * FROM t1;

DROP TABLE t1;
DROP PROCEDURE insert_proc;
20 changes: 20 additions & 0 deletions sql/sql_parse.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5620,6 +5620,26 @@ case SQLCOM_PREPARE:
}
if (thd->is_error() || (thd->variables.option_bits & OPTION_MASTER_SQL_ERROR))
trans_rollback_stmt(thd);
#ifdef WITH_WSREP
else if (thd->sp_runtime_ctx &&
!thd->is_error() &&
!thd->in_multi_stmt_transaction_mode() &&
(thd->wsrep_conflict_state == MUST_ABORT ||
thd->wsrep_conflict_state == CERT_FAILURE))
{
/*
The error was cleared, but THD was aborted by wsrep and
wsrep_conflict_state is still set accordingly. This
situation is expected if we are running a stored procedure
that declares a handler that catches ER_LOCK_DEADLOCK error.
In which case the error may have been cleared in method
sp_rcontext::handle_sql_condition().
*/
trans_rollback_stmt(thd);
thd->wsrep_conflict_state= NO_CONFLICT;
thd->killed= THD::NOT_KILLED;
}
#endif /* WITH_WSREP */
else
{
/* If commit fails, we should be able to reset the OK status. */
Expand Down

0 comments on commit 76f1195

Please sign in to comment.