mirror of
https://github.com/MariaDB/server.git
synced 2025-01-29 02:05:57 +01:00
ddd7d5d8e3
Under unknown circumstances, the SQL layer may wrongly disregard an invocation of thd_mark_transaction_to_rollback() when an InnoDB transaction had been aborted (rolled back) due to one of the following errors: * HA_ERR_LOCK_DEADLOCK * HA_ERR_RECORD_CHANGED (if innodb_snapshot_isolation=ON) * HA_ERR_LOCK_WAIT_TIMEOUT (if innodb_rollback_on_timeout=ON) Such an error used to cause a crash of InnoDB during transaction commit. These changes aim to catch and report the error earlier, so that not only this crash can be avoided but also the original root cause be found and fixed more easily later. The idea of this fix is from Michael 'Monty' Widenius. HA_ERR_ROLLBACK: A new error code that will be translated into ER_ROLLBACK_ONLY, signalling that the current transaction has been aborted and the only allowed action is ROLLBACK. trx_t::state: Add TRX_STATE_ABORTED that is like TRX_STATE_NOT_STARTED, but noting that the transaction had been rolled back and aborted. trx_t::is_started(): Replaces trx_is_started(). ha_innobase: Check the transaction state in various places. Simplify the logic around SAVEPOINT. ha_innobase::is_valid_trx(): Replaces ha_innobase::is_read_only(). The InnoDB logic around transaction savepoints, commit, and rollback was unnecessarily complex and might have contributed to this inconsistency. So, we are simplifying that logic as well. trx_savept_t: Replace with const undo_no_t*. When we rollback to a savepoint, all we need to know is the number of undo log records that must survive. trx_named_savept_t, DB_NO_SAVEPOINT: Remove. We can store undo_no_t directly in the space allocated at innobase_hton->savepoint_offset. fts_trx_create(): Do not copy previous savepoints. fts_savepoint_rollback(): If a savepoint was not found, roll back everything after the default savepoint of fts_trx_create(). The test innodb_fts.savepoint is extended to cover this code. Reviewed by: Vladislav Lesin Tested by: Matthias Leich
77 lines
2.3 KiB
Text
77 lines
2.3 KiB
Text
--source include/have_innodb.inc
|
|
--source include/count_sessions.inc
|
|
--source include/have_debug_sync.inc
|
|
|
|
--source include/innodb_stable_estimates.inc
|
|
|
|
--disable_query_log
|
|
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
|
|
--enable_query_log
|
|
|
|
CREATE TABLE t1 (
|
|
pk INT,
|
|
f1 VARCHAR(10) NOT NULL,
|
|
f2 VARCHAR(10) NULL,
|
|
f3 INT UNSIGNED NULL,
|
|
KEY (f1),
|
|
PRIMARY KEY (pk)
|
|
) ENGINE=InnoDB;
|
|
|
|
CREATE OR REPLACE ALGORITHM=MERGE VIEW v4 AS SELECT * FROM t1;
|
|
INSERT INTO t1 VALUES (1,'k','g',6),(2,'y','r',0),(3,'t','q',1),(4,'a','r',NULL),(5,'z','t',NULL);
|
|
|
|
CREATE TABLE t2 (f VARCHAR(10) NULL) ENGINE=InnoDB;
|
|
INSERT INTO t2 VALUES (NULL),('g'),('e'),('g');
|
|
|
|
CREATE TABLE t3 (
|
|
f1 VARCHAR(10) NOT NULL,
|
|
f2 VARCHAR(10) NULL,
|
|
f3 INT UNSIGNED NULL
|
|
) ENGINE=InnoDB;
|
|
|
|
INSERT INTO t3 VALUES ('k','n',9),('y','b',8),('m','w',6);
|
|
|
|
CREATE TABLE t4 (f INT NULL) ENGINE=InnoDB;
|
|
INSERT INTO t4 VALUES (8),(9);
|
|
UPDATE t1 SET t1.pk = -109 WHERE t1.f1 IN ( SELECT 'a' FROM t4 WHERE f >= 1 );
|
|
SET DEBUG_SYNC='now SIGNAL con1_dml';
|
|
|
|
--connect (con1,localhost,root,,test)
|
|
SET DEBUG_SYNC='now WAIT_FOR con1_dml';
|
|
begin;
|
|
SELECT * FROM t1 for update; # Holds x lock of all records in the table t1
|
|
SET DEBUG_SYNC='now SIGNAL default_dml';
|
|
SET DEBUG_SYNC='now SIGNAL con2_dml';
|
|
|
|
--connection default
|
|
SET DEBUG_SYNC='now WAIT_FOR default_dml';
|
|
--send UPDATE t3 AS alias1 LEFT JOIN t3 AS alias2 ON ( alias1.f1 <> alias1.f2 ) SET alias1.f3 = 59 WHERE ( EXISTS ( SELECT t1.f3 FROM t1 WHERE t1.f1 = alias1.f1 ) ) OR alias2.f1 = 'h'
|
|
# It holds the lock of all record in t3 and tries to acquire record lock for the table t1.
|
|
|
|
--connect (con2,localhost,root,,test)
|
|
set debug_sync='now WAIT_FOR con2_dml';
|
|
let $wait_condition=
|
|
select count(*) > 0 from information_schema.innodb_lock_waits;
|
|
--source include/wait_condition.inc
|
|
SET DEBUG_SYNC='now SIGNAL con1_dml2';
|
|
disconnect con2;
|
|
|
|
# Cleanup
|
|
--connection con1
|
|
SET DEBUG_SYNC='now WAIT_FOR con1_dml2';
|
|
UPDATE v4, t1 SET t1.pk = 76 WHERE t1.f2 IN ( SELECT t2.f FROM t2 INNER JOIN t3 );
|
|
# It holds the record lock on table t1 and tries to acquire record lock on t3.
|
|
# leads to deadlock (con1 trx is waiting for default trx and vice versa)
|
|
|
|
--connection default
|
|
--error ER_LOCK_DEADLOCK
|
|
--reap
|
|
|
|
connection con1;
|
|
COMMIT;
|
|
disconnect con1;
|
|
|
|
--connection default
|
|
DROP VIEW v4;
|
|
DROP TABLE t1, t2, t3, t4;
|
|
set debug_sync= reset;
|