-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Bug#20901025: SLAVE ASSERTION IN UNPACK_ROW WITH ROLLBACK TO
SAVEPOINT IN ERROR HANDLER Problem: ======= When issuing a ROLLBACK TO SAVEPOINT within an error handler, the slave asserts as follows: mysqlcom-pro-5.6.24/sql/rpl_record.cc:246: int unpack_row(const Relay_log_info, TABLE, uint, const uchar, const MY_BITMAP, const uchar, ulong, const uchar): Assertion `table_found' failed. Analysis: ======== The above assert is caused in two cases. CASE 1: SAVEPOINT and ROLLBACK TO SAVEPOINT are inside a trigger and followed by other DML statements. SAVEPOINT, ROLLBACK TO SAVEPOINT statements are logged into binary log as Query_log_events. Query_log_event::do_apply_event() wipes out table map. Events that follow the above Query_log_event will not find table map event and it results in the above assertion. CASE 2: SAVEPOINT and ROLLBACK TO SAVEPOINT are inside a trigger and and they does not follow with any DML statement. In the above scenario On master during execution of savepoint inside the trigger "mysql_bin_log.write_event(&qinfo)" is called, which intern invokes thd->binlog_flush_pending_rows_event() call with "end_stmt" = false. This causes the peding event to be flushed to the IO_CACHE without a STMT_END_F flag. Since it does't follow with any DML stements no proper clean up is done. i.e table maps are not cleared this causes the next query that follows to be logged as part of query1 without its own table map event resulting in same assert as mentioned in the bug. Fix: ==== When a SAVEPOINT is executed inside a stored function/trigger we force the pending event to be flushed with a STMT_END_F flag and clear the table maps as well to ensure that following DMLs will have a clean state to start with. In the same way when we are rolling back to a SAVEPOINT we should not only set the position back to SAVEPOINT but also we must ensure that the same clean state is preserved. In order to do that we clean the table maps during rolling back to savepoint.
- Loading branch information
Sujatha Sivakumar
committed
Aug 28, 2015
1 parent
854f926
commit 69d4e72
Showing
6 changed files
with
1,273 additions
and
3 deletions.
There are no files selected for viewing
327 changes: 327 additions & 0 deletions
327
mysql-test/extra/rpl_tests/rpl_rollback_to_savepoint.inc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,327 @@ | |||
############################################################################### | |||
# Bug#76727: SLAVE ASSERTION IN UNPACK_ROW WITH ROLLBACK TO | |||
# SAVEPOINT IN ERROR HANDLER | |||
# | |||
# Problem: | |||
# ======== | |||
# "SAVEPOINT", "ROLLBACK TO savepoint" wipe out table map on slave during | |||
# execution binary log events. For trigger the map is written to binary log once | |||
# for all trigger body and if trigger contains "SAVEPOINT" or | |||
# "ROLLBACK TO savepoint" statements any trigger's events after these | |||
# statements will not have table map. This results in an assert on slave. | |||
# | |||
# Test: | |||
# ===== | |||
# Test case 1: | |||
# Create a trigger with exception handler which rollsback to a savepoint. | |||
# Test proves that there will not be any assert during execution of rolling | |||
# back to savepoint. | |||
# | |||
# Test case 2: | |||
# Create a trigger which calls a procedure which in turn calls an exception | |||
# handler which rollsback to a savepoint. Prove that it doesn't cause any | |||
# assertion during execution. | |||
# | |||
# Test case 3: | |||
# Create a simple trigger which creates a SAVEPOINT and ROLLSBACK to savepoint | |||
# and doesn't follow with any other DML statement. Prove that it doesn't cause | |||
# any assertion during execution. | |||
# | |||
# Test case 4: | |||
# Create a trigger with SAVEPOINT and follows with a DML without ROLLBACK TO | |||
# savepoint. Ensure that data is replicated properly. | |||
# | |||
# Test case 5: | |||
# Create a trigger with SAVEPOINT and it does nothing. Do few DMLS following | |||
# the trigger ensure that the data is replicated properly | |||
# | |||
# Test case 6: | |||
# Create a stored function which creates a SAVEPOINT and ROLLSBACK to | |||
# savepoint. Do few inserts following the stored function call and ensure that | |||
# no assert is generated on slave and all the rows are replicated to slave. | |||
# | |||
# Test case 7: | |||
# Create a stored function which creates a SAVEPOINT alone and follows with | |||
# DMLS without ROLLBACK TO savepoint. Ensure that data is replicated properly. | |||
# | |||
# Test case 8: | |||
# Create a stored function which has SAVEPOINT inside it and does noting. It | |||
# should follow with other DMLs. Ensure that data is replicated properly. | |||
############################################################################### | |||
--source include/have_innodb.inc | |||
--source include/master-slave.inc | |||
|
|||
--echo #Test case 1: | |||
CREATE TABLE t1 (f1 INTEGER PRIMARY KEY); | |||
CREATE TABLE t2 (f1 INTEGER PRIMARY KEY); | |||
CREATE TABLE t3 (f1 INTEGER PRIMARY KEY); | |||
DELIMITER |; | |||
|
|||
CREATE TRIGGER tr1 AFTER INSERT ON t1 FOR EACH ROW | |||
BEGIN | |||
DECLARE EXIT HANDLER FOR SQLEXCEPTION | |||
BEGIN | |||
ROLLBACK TO event_logging_1; | |||
INSERT t3 VALUES (1); | |||
END; | |||
|
|||
SAVEPOINT event_logging_1; | |||
|
|||
INSERT INTO t2 VALUES (1); | |||
|
|||
RELEASE SAVEPOINT event_logging_1; | |||
|
|||
END| | |||
DELIMITER ;| | |||
|
|||
INSERT INTO t2 VALUES (1); | |||
INSERT INTO t1 VALUES (1); | |||
|
|||
--source include/show_binlog_events.inc | |||
|
|||
--sync_slave_with_master | |||
|
|||
--source include/rpl_connection_master.inc | |||
|
|||
DROP TRIGGER tr1; | |||
DELETE FROM t1; | |||
DELETE FROM t2; | |||
DELETE FROM t3; | |||
|
|||
--echo # Test case 2: | |||
|
|||
DELIMITER |; | |||
|
|||
CREATE PROCEDURE p1() | |||
BEGIN | |||
DECLARE EXIT HANDLER FOR SQLEXCEPTION | |||
BEGIN | |||
ROLLBACK TO event_logging_2; | |||
INSERT t3 VALUES (3); | |||
END; | |||
|
|||
SAVEPOINT event_logging_2; | |||
|
|||
INSERT INTO t2 VALUES (1); | |||
|
|||
RELEASE SAVEPOINT event_logging_2; | |||
END| | |||
|
|||
CREATE TRIGGER tr1 AFTER INSERT ON t1 FOR EACH ROW CALL p1()| | |||
|
|||
DELIMITER ;| | |||
|
|||
INSERT INTO t2 VALUES (1); | |||
INSERT INTO t1 VALUES (1); | |||
|
|||
--source include/show_binlog_events.inc | |||
|
|||
--sync_slave_with_master | |||
|
|||
--source include/rpl_connection_master.inc | |||
|
|||
DROP TABLE t1; | |||
DROP TABLE t2; | |||
DROP TABLE t3; | |||
|
|||
DROP PROCEDURE p1; | |||
|
|||
--echo # Test case 3: | |||
--source include/rpl_reset.inc | |||
--source include/rpl_connection_master.inc | |||
|
|||
CREATE TABLE t (f1 int(10) unsigned NOT NULL, PRIMARY KEY (f1)) ENGINE=InnoDB; | |||
|
|||
--delimiter | | |||
CREATE TRIGGER t_insert_trig AFTER INSERT ON t | |||
FOR EACH ROW | |||
BEGIN | |||
|
|||
SAVEPOINT savepoint_1; | |||
ROLLBACK TO savepoint_1; | |||
|
|||
END | | |||
--delimiter ; | |||
|
|||
INSERT INTO t VALUES (2); | |||
INSERT INTO t VALUES (3); | |||
|
|||
--source include/show_binlog_events.inc | |||
|
|||
SELECT * FROM t; | |||
|
|||
--source include/sync_slave_sql_with_master.inc | |||
|
|||
SELECT * FROM t; | |||
|
|||
--source include/rpl_connection_master.inc | |||
DROP TABLE t; | |||
|
|||
--echo # Test case 4: | |||
--source include/rpl_reset.inc | |||
--source include/rpl_connection_master.inc | |||
CREATE TABLE t (f1 int(10) unsigned NOT NULL) ENGINE=InnoDB; | |||
CREATE TABLE t1 (f1 int(10) unsigned NOT NULL) ENGINE=InnoDB; | |||
|
|||
--delimiter | | |||
CREATE TRIGGER t_insert_trig BEFORE INSERT ON t | |||
FOR EACH ROW | |||
BEGIN | |||
|
|||
SAVEPOINT savepoint_1; | |||
INSERT INTO t1 VALUES (5); | |||
END | | |||
--delimiter ; | |||
|
|||
INSERT INTO t VALUES (2), (3); | |||
INSERT INTO t1 VALUES (30); | |||
--source include/show_binlog_events.inc | |||
|
|||
SELECT * FROM t; | |||
SELECT * FROM t1; | |||
--source include/sync_slave_sql_with_master.inc | |||
|
|||
SELECT * FROM t; | |||
SELECT * FROM t1; | |||
|
|||
--source include/rpl_connection_master.inc | |||
DROP TABLE t; | |||
DROP TABLE t1; | |||
|
|||
--echo # Test case 5: | |||
--source include/rpl_reset.inc | |||
--source include/rpl_connection_master.inc | |||
CREATE TABLE t (f1 int(10) unsigned NOT NULL) ENGINE=InnoDB; | |||
CREATE TABLE t1 (f1 int(10) unsigned NOT NULL) ENGINE=InnoDB; | |||
|
|||
--delimiter | | |||
CREATE TRIGGER t_insert_trig BEFORE INSERT ON t | |||
FOR EACH ROW | |||
BEGIN | |||
|
|||
SAVEPOINT savepoint_1; | |||
END | | |||
|
|||
--delimiter ; | |||
|
|||
INSERT INTO t VALUES (2), (3); | |||
INSERT INTO t1 VALUES (30); | |||
--source include/show_binlog_events.inc | |||
|
|||
SELECT * FROM t; | |||
SELECT * FROM t1; | |||
--source include/sync_slave_sql_with_master.inc | |||
|
|||
SELECT * FROM t; | |||
SELECT * FROM t1; | |||
|
|||
--source include/rpl_connection_master.inc | |||
DROP TABLE t; | |||
DROP TABLE t1; | |||
|
|||
--echo # Test case 6: | |||
--source include/rpl_reset.inc | |||
--source include/rpl_connection_master.inc | |||
CREATE TABLE t1 (f1 INTEGER ) ENGINE=INNODB; | |||
CREATE TABLE t2 (f1 INTEGER ) ENGINE=INNODB; | |||
|
|||
--delimiter | | |||
|
|||
CREATE FUNCTION f1() RETURNS INT | |||
BEGIN | |||
SAVEPOINT event_logging_2; | |||
|
|||
INSERT INTO t1 VALUES (1); | |||
|
|||
ROLLBACK TO event_logging_2; | |||
RETURN 0; | |||
END| | |||
|
|||
--delimiter ; | |||
|
|||
BEGIN; | |||
INSERT INTO t2 VALUES (1), (f1()), (2), (4); | |||
COMMIT; | |||
INSERT INTO t2 VALUES (10); | |||
--source include/show_binlog_events.inc | |||
|
|||
--source include/rpl_connection_master.inc | |||
SELECT * FROM t2; | |||
SELECT * FROM t1; | |||
--source include/sync_slave_sql_with_master.inc | |||
SELECT * FROM t2; | |||
SELECT * FROM t1; | |||
|
|||
--source include/rpl_connection_master.inc | |||
DROP TABLE t1; | |||
DROP TABLE t2; | |||
DROP FUNCTION f1; | |||
|
|||
--echo # Test case 7: | |||
--source include/rpl_reset.inc | |||
--source include/rpl_connection_master.inc | |||
CREATE TABLE t1 (f1 INTEGER ) ENGINE=INNODB; | |||
CREATE TABLE t2 (f1 INTEGER ) ENGINE=INNODB; | |||
|
|||
--delimiter | | |||
|
|||
CREATE FUNCTION f1() RETURNS INT | |||
BEGIN | |||
SAVEPOINT event_logging_2; | |||
|
|||
INSERT INTO t1 VALUES (1); | |||
|
|||
RETURN 0; | |||
END| | |||
|
|||
--delimiter ; | |||
|
|||
BEGIN; | |||
INSERT INTO t2 VALUES (1), (f1()), (2), (4); | |||
COMMIT; | |||
INSERT INTO t2 VALUES (10); | |||
--source include/show_binlog_events.inc | |||
|
|||
--source include/rpl_connection_master.inc | |||
SELECT * FROM t2; | |||
SELECT * FROM t1; | |||
--source include/sync_slave_sql_with_master.inc | |||
SELECT * FROM t2; | |||
SELECT * FROM t1; | |||
|
|||
--source include/rpl_connection_master.inc | |||
DROP TABLE t1; | |||
DROP TABLE t2; | |||
DROP FUNCTION f1; | |||
|
|||
--echo # Test case 8: | |||
--source include/rpl_reset.inc | |||
--source include/rpl_connection_master.inc | |||
CREATE TABLE t1 (f1 INTEGER ) ENGINE=INNODB; | |||
|
|||
--delimiter | | |||
|
|||
CREATE FUNCTION f1() RETURNS INT | |||
BEGIN | |||
SAVEPOINT event_logging_2; | |||
RETURN 0; | |||
END| | |||
|
|||
--delimiter ; | |||
|
|||
BEGIN; | |||
INSERT INTO t1 VALUES (1), (f1()), (2), (4); | |||
COMMIT; | |||
INSERT INTO t1 VALUES (10); | |||
--source include/show_binlog_events.inc | |||
|
|||
--source include/rpl_connection_master.inc | |||
SELECT * FROM t1; | |||
--source include/sync_slave_sql_with_master.inc | |||
SELECT * FROM t1; | |||
|
|||
--source include/rpl_connection_master.inc | |||
DROP TABLE t1; | |||
DROP FUNCTION f1; | |||
|
|||
--source include/rpl_end.inc |
Oops, something went wrong.