MDEV-24035 Failing assertion: UT_LIST_GET_LEN(lock.trx_locks) == 0 causing disruption and replication failure

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
This commit is contained in:
Marko Mäkelä 2024-12-12 18:02:00 +02:00
parent 9aa84cf57f
commit ddd7d5d8e3
70 changed files with 880 additions and 1163 deletions

View file

@ -78,5 +78,7 @@
{ "HA_ERR_ABORTED_BY_USER", HA_ERR_ABORTED_BY_USER, "" }, { "HA_ERR_ABORTED_BY_USER", HA_ERR_ABORTED_BY_USER, "" },
{ "HA_ERR_DISK_FULL", HA_ERR_DISK_FULL, "" }, { "HA_ERR_DISK_FULL", HA_ERR_DISK_FULL, "" },
{ "HA_ERR_INCOMPATIBLE_DEFINITION", HA_ERR_INCOMPATIBLE_DEFINITION, "" }, { "HA_ERR_INCOMPATIBLE_DEFINITION", HA_ERR_INCOMPATIBLE_DEFINITION, "" },
{ "HA_ERR_NO_ENCRYPTION", HA_ERR_NO_ENCRYPTION, "" },
{ "HA_ERR_COMMIT_ERROR", HA_ERR_COMMIT_ERROR, "" }, { "HA_ERR_COMMIT_ERROR", HA_ERR_COMMIT_ERROR, "" },
{ "HA_ERR_PARTITION_LIST", HA_ERR_PARTITION_LIST, ""},
{ "HA_ERR_NO_ENCRYPTION", HA_ERR_NO_ENCRYPTION, ""},
{ "HA_ERR_ROLLBACK", HA_ERR_ROLLBACK, "" },

View file

@ -535,7 +535,8 @@ enum ha_base_keytype {
#define HA_ERR_COMMIT_ERROR 197 #define HA_ERR_COMMIT_ERROR 197
#define HA_ERR_PARTITION_LIST 198 #define HA_ERR_PARTITION_LIST 198
#define HA_ERR_NO_ENCRYPTION 199 #define HA_ERR_NO_ENCRYPTION 199
#define HA_ERR_LAST 199 /* Copy of last error nr * */ #define HA_ERR_ROLLBACK 200 /* Automatic rollback done */
#define HA_ERR_LAST 200 /* Copy of last error nr * */
/* Number of different errors */ /* Number of different errors */
#define HA_ERR_ERRORS (HA_ERR_LAST - HA_ERR_FIRST + 1) #define HA_ERR_ERRORS (HA_ERR_LAST - HA_ERR_FIRST + 1)

View file

@ -110,7 +110,8 @@ static const char *handler_error_messages[]=
"Sequence values are conflicting", "Sequence values are conflicting",
"Error during commit", "Error during commit",
"Cannot select partitions", "Cannot select partitions",
"Cannot initialize encryption. Check that all encryption parameters have been set" "Cannot initialize encryption. Check that all encryption parameters have been set",
"Transaction was aborted",
}; };
#endif /* MYSYS_MY_HANDLER_ERRORS_INCLUDED */ #endif /* MYSYS_MY_HANDLER_ERRORS_INCLUDED */

View file

@ -2,6 +2,10 @@
# Bug #24200: Provide backwards compatibility mode for 4.x "rollback on # Bug #24200: Provide backwards compatibility mode for 4.x "rollback on
# transaction timeout" # transaction timeout"
# #
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
--disable_warnings --disable_warnings
drop table if exists t1; drop table if exists t1;
--enable_warnings --enable_warnings

View file

@ -2,6 +2,10 @@
-- source include/have_innodb.inc -- source include/have_innodb.inc
-- source include/not_binlog_format_row.inc -- source include/not_binlog_format_row.inc
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
--echo # --echo #
--echo # Bug #39022: Mysql randomly crashing in lock_sec_rec_cons_read_sees --echo # Bug #39022: Mysql randomly crashing in lock_sec_rec_cons_read_sees
--echo # --echo #

View file

@ -1,6 +1,10 @@
--source include/have_metadata_lock_info.inc --source include/have_metadata_lock_info.inc
-- source include/have_innodb.inc -- source include/have_innodb.inc
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
# Save the initial number of concurrent sessions. # Save the initial number of concurrent sessions.
--source include/count_sessions.inc --source include/count_sessions.inc

View file

@ -2,6 +2,10 @@
--source include/have_partition.inc --source include/have_partition.inc
--source include/have_sequence.inc --source include/have_sequence.inc
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
--echo # --echo #
--echo # MDEV-18707 Server crash in my_hash_sort_bin, ASAN heap-use-after-free in Field::is_null, server hang, corrupted double-linked list --echo # MDEV-18707 Server crash in my_hash_sort_bin, ASAN heap-use-after-free in Field::is_null, server hang, corrupted double-linked list
--echo # --echo #

View file

@ -5,6 +5,9 @@ let $MYSQLD_DATADIR= `SELECT @@datadir`;
call mtr.add_suppression("InnoDB: Table .* does not exist in the InnoDB internal data dictionary .*"); call mtr.add_suppression("InnoDB: Table .* does not exist in the InnoDB internal data dictionary .*");
call mtr.add_suppression("Deadlock found when trying to get lock; try restarting transaction"); call mtr.add_suppression("Deadlock found when trying to get lock; try restarting transaction");
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
--echo # --echo #
--echo # Bug#11766879/Bug#60106: DIFF BETWEEN # OF INDEXES IN MYSQL VS INNODB, --echo # Bug#11766879/Bug#60106: DIFF BETWEEN # OF INDEXES IN MYSQL VS INNODB,

View file

@ -4,6 +4,10 @@
--source include/have_sequence.inc --source include/have_sequence.inc
--source include/innodb_stable_estimates.inc --source include/innodb_stable_estimates.inc
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
SET SESSION DEFAULT_STORAGE_ENGINE='InnoDB'; SET SESSION DEFAULT_STORAGE_ENGINE='InnoDB';
SET @save_stats_persistent=@@GLOBAL.innodb_stats_persistent; SET @save_stats_persistent=@@GLOBAL.innodb_stats_persistent;

View file

@ -2,13 +2,16 @@
# WL#1756 # WL#1756
# #
-- source include/have_innodb.inc -- source include/have_innodb.inc
--source include/not_embedded.inc --source include/not_embedded.inc
# Save the initial number of concurrent sessions # Save the initial number of concurrent sessions
--source include/count_sessions.inc --source include/count_sessions.inc
call mtr.add_suppression("Deadlock found when trying to get lock; try restarting transaction"); call mtr.add_suppression("Deadlock found when trying to get lock; try restarting transaction");
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
--disable_warnings --disable_warnings
drop table if exists t1, t2; drop table if exists t1, t2;

View file

@ -1,10 +1,12 @@
connection node_2; connection node_2;
connection node_1; connection node_1;
connection node_2; connection node_2;
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
CREATE TABLE t1(a int not null primary key auto_increment,b int) engine=InnoDB; CREATE TABLE t1(a int not null primary key auto_increment,b int) engine=InnoDB;
insert into t1 values (NULL,1); insert into t1 values (NULL,1);
connect node_2a, 127.0.0.1, root, , test, $NODE_MYPORT_2; connect node_2a, 127.0.0.1, root, , test, $NODE_MYPORT_2;
connection node_2a; connection node_2a;
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
begin; begin;
update t1 set a = 5; update t1 set a = 5;
connection node_2; connection node_2;

View file

@ -1,8 +1,10 @@
connection node_2; connection node_2;
connection node_1; connection node_1;
connection node_2; connection node_2;
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
call mtr.add_suppression("WSREP: Trying to continue unpaused monitor"); call mtr.add_suppression("WSREP: Trying to continue unpaused monitor");
connection node_1; connection node_1;
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
call mtr.add_suppression("WSREP: Trying to continue unpaused monitor"); call mtr.add_suppression("WSREP: Trying to continue unpaused monitor");
CREATE TABLE t1 ENGINE=InnoDB select 1 as a, 1 as b union select 2, 2; CREATE TABLE t1 ENGINE=InnoDB select 1 as a, 1 as b union select 2, 2;
ALTER TABLE t1 add primary key(a); ALTER TABLE t1 add primary key(a);

View file

@ -7,11 +7,13 @@
# #
--connection node_2 --connection node_2
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
CREATE TABLE t1(a int not null primary key auto_increment,b int) engine=InnoDB; CREATE TABLE t1(a int not null primary key auto_increment,b int) engine=InnoDB;
insert into t1 values (NULL,1); insert into t1 values (NULL,1);
--connect node_2a, 127.0.0.1, root, , test, $NODE_MYPORT_2 --connect node_2a, 127.0.0.1, root, , test, $NODE_MYPORT_2
--connection node_2a --connection node_2a
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
begin; begin;
update t1 set a = 5; update t1 set a = 5;

View file

@ -3,9 +3,11 @@
--source include/big_test.inc --source include/big_test.inc
--connection node_2 --connection node_2
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
call mtr.add_suppression("WSREP: Trying to continue unpaused monitor"); call mtr.add_suppression("WSREP: Trying to continue unpaused monitor");
--connection node_1 --connection node_1
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
call mtr.add_suppression("WSREP: Trying to continue unpaused monitor"); call mtr.add_suppression("WSREP: Trying to continue unpaused monitor");
CREATE TABLE t1 ENGINE=InnoDB select 1 as a, 1 as b union select 2, 2; CREATE TABLE t1 ENGINE=InnoDB select 1 as a, 1 as b union select 2, 2;

View file

@ -456,7 +456,7 @@ restore: t1 .ibd and .cfg files
SET SESSION debug_dbug=@saved_debug_dbug; SET SESSION debug_dbug=@saved_debug_dbug;
SET SESSION debug_dbug="+d,ib_import_open_tablespace_failure"; SET SESSION debug_dbug="+d,ib_import_open_tablespace_failure";
ALTER TABLE t1 IMPORT TABLESPACE; ALTER TABLE t1 IMPORT TABLESPACE;
ERROR HY000: Got error 42 'Tablespace not found' from ./test/t1.ibd ERROR HY000: Got error 41 'Tablespace not found' from ./test/t1.ibd
SET SESSION debug_dbug=@saved_debug_dbug; SET SESSION debug_dbug=@saved_debug_dbug;
restore: t1 .ibd and .cfg files restore: t1 .ibd and .cfg files
SET SESSION debug_dbug="+d,ib_import_check_bitmap_failure"; SET SESSION debug_dbug="+d,ib_import_check_bitmap_failure";

View file

@ -3,6 +3,10 @@
--source include/have_debug_sync.inc --source include/have_debug_sync.inc
--source include/not_embedded.inc --source include/not_embedded.inc
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
# Two parallel connection with autoinc column after restart. # Two parallel connection with autoinc column after restart.
CREATE TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY)ENGINE=INNODB; CREATE TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY)ENGINE=INNODB;

View file

@ -5,6 +5,10 @@
--source include/have_innodb.inc --source include/have_innodb.inc
--source include/count_sessions.inc --source include/count_sessions.inc
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
let $have_deadlock=`select @@GLOBAL.innodb_deadlock_detect`; let $have_deadlock=`select @@GLOBAL.innodb_deadlock_detect`;
connection default; connection default;

View file

@ -1,6 +1,10 @@
--source include/have_innodb.inc --source include/have_innodb.inc
--source include/count_sessions.inc --source include/count_sessions.inc
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
CREATE TABLE t1 ( CREATE TABLE t1 (
pkey int NOT NULL PRIMARY KEY, pkey int NOT NULL PRIMARY KEY,
c int c int

View file

@ -2,6 +2,10 @@
--source include/have_debug_sync.inc --source include/have_debug_sync.inc
--source include/count_sessions.inc --source include/count_sessions.inc
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
--connect(cancel_purge,localhost,root,,) --connect(cancel_purge,localhost,root,,)
# Purge can cause deadlock in the test, requesting page's RW_X_LATCH for trx # Purge can cause deadlock in the test, requesting page's RW_X_LATCH for trx
# ids reseting, after trx 2 acqured RW_S_LATCH and suspended in debug sync point # ids reseting, after trx 2 acqured RW_S_LATCH and suspended in debug sync point

View file

@ -2,6 +2,10 @@
--source include/have_debug_sync.inc --source include/have_debug_sync.inc
--source include/count_sessions.inc --source include/count_sessions.inc
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
# Purge can cause deadlock in the test, requesting page's RW_X_LATCH for trx # Purge can cause deadlock in the test, requesting page's RW_X_LATCH for trx
# ids reseting, after trx 2 acqured RW_S_LATCH and suspended in debug sync point # ids reseting, after trx 2 acqured RW_S_LATCH and suspended in debug sync point
# lock_trx_handle_wait_enter, waiting for upd_cont signal, which must be # lock_trx_handle_wait_enter, waiting for upd_cont signal, which must be

View file

@ -2,6 +2,10 @@
--source include/have_debug_sync.inc --source include/have_debug_sync.inc
--source include/count_sessions.inc --source include/count_sessions.inc
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
# Purge can cause deadlock in the test, requesting page's RW_X_LATCH for trx # Purge can cause deadlock in the test, requesting page's RW_X_LATCH for trx
# ids reseting, after trx 2 acqured RW_S_LATCH and suspended in debug sync point # ids reseting, after trx 2 acqured RW_S_LATCH and suspended in debug sync point
# lock_trx_handle_wait_enter, waiting for upd_cont signal, which must be # lock_trx_handle_wait_enter, waiting for upd_cont signal, which must be

View file

@ -2,6 +2,10 @@
--source include/count_sessions.inc --source include/count_sessions.inc
--source include/default_charset.inc --source include/default_charset.inc
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
SET GLOBAL innodb_stats_persistent = 0; SET GLOBAL innodb_stats_persistent = 0;
--echo # --echo #

View file

@ -1,6 +1,10 @@
--source include/have_innodb.inc --source include/have_innodb.inc
--source include/have_partition.inc --source include/have_partition.inc
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
# #
# Check and select innodb lock type # Check and select innodb lock type
# #

View file

@ -4,6 +4,10 @@
# Bug#42419 Server crash with "Pure virtual method called" on two concurrent connections # Bug#42419 Server crash with "Pure virtual method called" on two concurrent connections
# #
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
--source include/not_embedded.inc --source include/not_embedded.inc
let $innodb_lock_wait_timeout= query_get_value(SHOW VARIABLES LIKE 'innodb_lock_wait_timeout%', Value, 1); let $innodb_lock_wait_timeout= query_get_value(SHOW VARIABLES LIKE 'innodb_lock_wait_timeout%', Value, 1);

View file

@ -1,5 +1,9 @@
--source include/have_innodb.inc --source include/have_innodb.inc
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
# #
# Bug #41453: Assertion `m_status == DA_ERROR' failed in # Bug #41453: Assertion `m_status == DA_ERROR' failed in
# Diagnostics_area::sql_errno # Diagnostics_area::sql_errno

View file

@ -6,6 +6,10 @@
# be heavier than ones that had not. # be heavier than ones that had not.
# #
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
-- source include/have_innodb.inc -- source include/have_innodb.inc
SET default_storage_engine=InnoDB; SET default_storage_engine=InnoDB;

View file

@ -3,6 +3,10 @@
--source include/have_debug_sync.inc --source include/have_debug_sync.inc
--source include/count_sessions.inc --source include/count_sessions.inc
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
--connect (pause_purge,localhost,root) --connect (pause_purge,localhost,root)
START TRANSACTION WITH CONSISTENT SNAPSHOT; START TRANSACTION WITH CONSISTENT SNAPSHOT;

View file

@ -3,6 +3,10 @@
--source include/have_debug.inc --source include/have_debug.inc
--source include/have_debug_sync.inc --source include/have_debug_sync.inc
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
CREATE TABLE t(a INT PRIMARY KEY) ENGINE=InnoDB; CREATE TABLE t(a INT PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO t VALUES (3); INSERT INTO t VALUES (3);

View file

@ -1,5 +1,9 @@
--source include/have_innodb.inc --source include/have_innodb.inc
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
--echo # --echo #
--echo # MDEV-26642 Weird SELECT view when a record is --echo # MDEV-26642 Weird SELECT view when a record is
--echo # modified to the same value by two transactions --echo # modified to the same value by two transactions

View file

@ -6,6 +6,9 @@
--echo # --echo #
call mtr.add_suppression("\\[Warning\\] InnoDB: Over 67 percent of the buffer pool"); call mtr.add_suppression("\\[Warning\\] InnoDB: Over 67 percent of the buffer pool");
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
CREATE TABLE t1 (col1 INT) ENGINE=InnoDB; CREATE TABLE t1 (col1 INT) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1),(2),(3),(4),(5); INSERT INTO t1 VALUES (1),(2),(3),(4),(5);

View file

@ -4,6 +4,10 @@
--source include/innodb_stable_estimates.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 ( CREATE TABLE t1 (
pk INT, pk INT,
f1 VARCHAR(10) NOT NULL, f1 VARCHAR(10) NOT NULL,

View file

@ -4,6 +4,10 @@
# concurrent CREATE OR REPLACE and transactional UPDATE # concurrent CREATE OR REPLACE and transactional UPDATE
# #
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
--source include/have_innodb.inc --source include/have_innodb.inc
CREATE TABLE t1 (a INT, b INT) ENGINE=InnoDB; CREATE TABLE t1 (a INT, b INT) ENGINE=InnoDB;

View file

@ -1,5 +1,9 @@
source include/have_innodb.inc; source include/have_innodb.inc;
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
# #
# MDEV-20354 All but last insert ignored in InnoDB tables when table locked # MDEV-20354 All but last insert ignored in InnoDB tables when table locked
# #

View file

@ -249,9 +249,33 @@ id title
7 mysql 7 mysql
TRUNCATE TABLE articles; TRUNCATE TABLE articles;
INSERT INTO articles(id, title) VALUES(1, 'mysql'); INSERT INTO articles(id, title) VALUES(1, 'mysql');
CREATE TABLE t(a INT PRIMARY KEY) ENGINE=InnoDB;
BEGIN; BEGIN;
INSERT INTO t SET a=1;
SAVEPOINT t;
INSERT INTO articles(id, title) VALUES(2, 'mysql');
SELECT * FROM articles WHERE MATCH(title) AGAINST('mysql');
id title
1 mysql
ROLLBACK TO SAVEPOINT t;
SELECT * FROM articles WHERE MATCH(title) AGAINST('mysql');
id title
1 mysql
SELECT * FROM t;
a
1
COMMIT;
SELECT * FROM articles WHERE MATCH(title) AGAINST('mysql');
id title
1 mysql
BEGIN;
INSERT INTO t SET a=2;
INSERT INTO articles(id, title) VALUES(2, 'mysql'); INSERT INTO articles(id, title) VALUES(2, 'mysql');
ROLLBACK; ROLLBACK;
SELECT * FROM t;
a
1
DROP TABLE t;
INSERT INTO articles(id, title) VALUES(3, 'mysql'); INSERT INTO articles(id, title) VALUES(3, 'mysql');
SELECT * FROM articles WHERE MATCH(title) AGAINST('mysql'); SELECT * FROM articles WHERE MATCH(title) AGAINST('mysql');
id title id title

View file

@ -366,12 +366,28 @@ TRUNCATE TABLE articles;
INSERT INTO articles(id, title) VALUES(1, 'mysql'); INSERT INTO articles(id, title) VALUES(1, 'mysql');
CREATE TABLE t(a INT PRIMARY KEY) ENGINE=InnoDB;
BEGIN; BEGIN;
INSERT INTO t SET a=1;
SAVEPOINT t;
INSERT INTO articles(id, title) VALUES(2, 'mysql');
SELECT * FROM articles WHERE MATCH(title) AGAINST('mysql');
ROLLBACK TO SAVEPOINT t;
SELECT * FROM articles WHERE MATCH(title) AGAINST('mysql');
SELECT * FROM t;
COMMIT;
SELECT * FROM articles WHERE MATCH(title) AGAINST('mysql');
BEGIN;
INSERT INTO t SET a=2;
INSERT INTO articles(id, title) VALUES(2, 'mysql'); INSERT INTO articles(id, title) VALUES(2, 'mysql');
ROLLBACK; ROLLBACK;
SELECT * FROM t;
DROP TABLE t;
INSERT INTO articles(id, title) VALUES(3, 'mysql'); INSERT INTO articles(id, title) VALUES(3, 'mysql');
SELECT * FROM articles WHERE MATCH(title) AGAINST('mysql'); SELECT * FROM articles WHERE MATCH(title) AGAINST('mysql');

View file

@ -91,7 +91,7 @@ restore: t1 .ibd and .cfg files
SET SESSION debug_dbug=@saved_debug_dbug; SET SESSION debug_dbug=@saved_debug_dbug;
SET SESSION debug_dbug="+d,ib_import_open_tablespace_failure"; SET SESSION debug_dbug="+d,ib_import_open_tablespace_failure";
ALTER TABLE t1 IMPORT TABLESPACE; ALTER TABLE t1 IMPORT TABLESPACE;
ERROR HY000: Got error 42 'Tablespace not found' from ./test/t1.ibd ERROR HY000: Got error 41 'Tablespace not found' from ./test/t1.ibd
SET SESSION debug_dbug=@saved_debug_dbug; SET SESSION debug_dbug=@saved_debug_dbug;
restore: t1 .ibd and .cfg files restore: t1 .ibd and .cfg files
SET SESSION debug_dbug="+d,ib_import_check_bitmap_failure"; SET SESSION debug_dbug="+d,ib_import_check_bitmap_failure";

View file

@ -2,6 +2,9 @@
--source include/have_innodb.inc --source include/have_innodb.inc
--source include/have_debug.inc --source include/have_debug.inc
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
--echo *** Test all-to-all replication with --gtid-ignore-duplicates *** --echo *** Test all-to-all replication with --gtid-ignore-duplicates ***

View file

@ -36,9 +36,9 @@ Table Op Msg_type Msg_text
test.t1 analyze status Engine-independent statistics collected test.t1 analyze status Engine-independent statistics collected
test.t1 analyze status OK test.t1 analyze status OK
FLUSH LOGS; FLUSH LOGS;
FOUND 1 /GTID 0-1-8 ddl/ in mysqlbinlog.out
FOUND 1 /GTID 0-1-9 ddl/ in mysqlbinlog.out FOUND 1 /GTID 0-1-9 ddl/ in mysqlbinlog.out
FOUND 1 /GTID 0-1-10 ddl/ in mysqlbinlog.out FOUND 1 /GTID 0-1-10 ddl/ in mysqlbinlog.out
FOUND 1 /GTID 0-1-11 ddl/ in mysqlbinlog.out
# #
# Clean up # Clean up
# #
@ -63,9 +63,9 @@ ALTER TABLE t1 REPAIR PARTITION p0;
Table Op Msg_type Msg_text Table Op Msg_type Msg_text
test.t1 repair status OK test.t1 repair status OK
FLUSH LOGS; FLUSH LOGS;
FOUND 1 /GTID 0-1-14 ddl/ in mysqlbinlog.out
FOUND 1 /GTID 0-1-15 ddl/ in mysqlbinlog.out FOUND 1 /GTID 0-1-15 ddl/ in mysqlbinlog.out
FOUND 1 /GTID 0-1-16 ddl/ in mysqlbinlog.out FOUND 1 /GTID 0-1-16 ddl/ in mysqlbinlog.out
FOUND 1 /GTID 0-1-17 ddl/ in mysqlbinlog.out
# #
# Clean up # Clean up
# #

View file

@ -22,6 +22,10 @@
--let $rpl_topology=1->2 --let $rpl_topology=1->2
--source include/rpl_init.inc --source include/rpl_init.inc
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
--connection server_1 --connection server_1
FLUSH TABLES; FLUSH TABLES;
ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB; ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;

View file

@ -2,6 +2,9 @@
--source include/have_debug.inc --source include/have_debug.inc
--source include/master-slave.inc --source include/master-slave.inc
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
--echo MDEV-31655: Parallel replication deadlock victim preference code erroneously removed --echo MDEV-31655: Parallel replication deadlock victim preference code erroneously removed
# The problem was that InnoDB would choose the wrong deadlock victim. # The problem was that InnoDB would choose the wrong deadlock victim.

View file

@ -3,6 +3,10 @@
--source include/have_debug.inc --source include/have_debug.inc
--source include/have_binlog_format_statement.inc --source include/have_binlog_format_statement.inc
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
--connection master --connection master
ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB; ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
CREATE TABLE t1(a INT) ENGINE=INNODB; CREATE TABLE t1(a INT) ENGINE=INNODB;

View file

@ -5,6 +5,10 @@
--source include/have_debug_sync.inc --source include/have_debug_sync.inc
--source include/master-slave.inc --source include/master-slave.inc
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
--connection server_2 --connection server_2
SET sql_log_bin=0; SET sql_log_bin=0;
CALL mtr.add_suppression("Commit failed due to failure of an earlier commit on which this one depends"); CALL mtr.add_suppression("Commit failed due to failure of an earlier commit on which this one depends");

View file

@ -7,6 +7,9 @@
call mtr.add_suppression("Deadlock found when trying to get lock; try restarting transaction"); call mtr.add_suppression("Deadlock found when trying to get lock; try restarting transaction");
call mtr.add_suppression("Can't find record in 't1'"); call mtr.add_suppression("Can't find record in 't1'");
call mtr.add_suppression("Can't find record in 't2'"); call mtr.add_suppression("Can't find record in 't2'");
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
--connection server_1 --connection server_1
ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB; ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;

View file

@ -3,6 +3,10 @@
--let $rpl_topology=1->2 --let $rpl_topology=1->2
--source include/rpl_init.inc --source include/rpl_init.inc
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
--connection server_1 --connection server_1
ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB; ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
CREATE TABLE t1 (a int PRIMARY KEY, b INT) ENGINE=InnoDB; CREATE TABLE t1 (a int PRIMARY KEY, b INT) ENGINE=InnoDB;

View file

@ -9,6 +9,10 @@
--source include/have_perfschema.inc --source include/have_perfschema.inc
--source include/master-slave.inc --source include/master-slave.inc
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
--let $xid_num = 19 --let $xid_num = 19
--let $repeat = 17 --let $repeat = 17
--let $workers = 7 --let $workers = 7

View file

@ -2,6 +2,9 @@
--source include/master-slave.inc --source include/master-slave.inc
call mtr.add_suppression("Deadlock found when trying to get lock; try restarting transaction"); call mtr.add_suppression("Deadlock found when trying to get lock; try restarting transaction");
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
--echo *** Provoke a deadlock on the slave, check that transaction retry succeeds. *** --echo *** Provoke a deadlock on the slave, check that transaction retry succeeds. ***
--connection master --connection master

View file

@ -1,6 +1,10 @@
source suite/versioning/engines.inc; source suite/versioning/engines.inc;
source suite/versioning/common.inc; source suite/versioning/common.inc;
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
replace_result $sys_datatype_expl SYS_DATATYPE; replace_result $sys_datatype_expl SYS_DATATYPE;
eval create table t1( eval create table t1(
x int unsigned, x int unsigned,

View file

@ -4676,6 +4676,12 @@ void handler::print_error(int error, myf errflag)
case HA_ERR_PARTITION_LIST: case HA_ERR_PARTITION_LIST:
my_error(ER_VERS_NOT_ALLOWED, errflag, table->s->db.str, table->s->table_name.str); my_error(ER_VERS_NOT_ALLOWED, errflag, table->s->db.str, table->s->table_name.str);
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
case HA_ERR_ROLLBACK:
/* Crash if we run with --debug-assert-on-error */
DBUG_ASSERT(!debug_assert_if_crashed_table);
SET_FATAL_ERROR;
textno= ER_ROLLBACK_ONLY;
break;
default: default:
{ {
/* The error was "unknown" to this function. /* The error was "unknown" to this function.
@ -4710,7 +4716,7 @@ void handler::print_error(int error, myf errflag)
/* Ensure this becomes a true error */ /* Ensure this becomes a true error */
errflag&= ~(ME_WARNING | ME_NOTE); errflag&= ~(ME_WARNING | ME_NOTE);
if ((debug_assert_if_crashed_table || if ((debug_assert_if_crashed_table ||
global_system_variables.log_warnings > 1)) global_system_variables.log_warnings > 1))
{ {
/* /*
Log error to log before we crash or if extended warnings are requested Log error to log before we crash or if extended warnings are requested

View file

@ -1541,7 +1541,7 @@ struct find_interesting_trx
{ {
void operator()(const trx_t &trx) void operator()(const trx_t &trx)
{ {
if (trx.state == TRX_STATE_NOT_STARTED) if (!trx.is_started())
return; return;
if (trx.mysql_thd == nullptr) if (trx.mysql_thd == nullptr)
return; return;
@ -1550,12 +1550,12 @@ struct find_interesting_trx
if (!found) if (!found)
{ {
ib::warn() << "The following trx might hold " sql_print_warning("InnoDB: The following trx might hold "
"the blocks in buffer pool to " "the blocks in buffer pool to "
"be withdrawn. Buffer pool " "be withdrawn. Buffer pool "
"resizing can complete only " "resizing can complete only "
"after all the transactions " "after all the transactions "
"below release the blocks."; "below release the blocks.");
found= true; found= true;
} }

View file

@ -2211,7 +2211,7 @@ fts_savepoint_t*
fts_savepoint_create( fts_savepoint_create(
/*=================*/ /*=================*/
ib_vector_t* savepoints, /*!< out: InnoDB transaction */ ib_vector_t* savepoints, /*!< out: InnoDB transaction */
const char* name, /*!< in: savepoint name */ const void* name, /*!< in: savepoint */
mem_heap_t* heap) /*!< in: heap */ mem_heap_t* heap) /*!< in: heap */
{ {
fts_savepoint_t* savepoint; fts_savepoint_t* savepoint;
@ -2220,11 +2220,7 @@ fts_savepoint_create(
ib_vector_push(savepoints, NULL)); ib_vector_push(savepoints, NULL));
memset(savepoint, 0x0, sizeof(*savepoint)); memset(savepoint, 0x0, sizeof(*savepoint));
savepoint->name = name;
if (name) {
savepoint->name = mem_heap_strdup(heap, name);
}
static_assert(!offsetof(fts_trx_table_t, table), "ABI"); static_assert(!offsetof(fts_trx_table_t, table), "ABI");
savepoint->tables = rbt_create(sizeof(fts_trx_table_t*), fts_ptr2_cmp); savepoint->tables = rbt_create(sizeof(fts_trx_table_t*), fts_ptr2_cmp);
@ -2243,7 +2239,6 @@ fts_trx_create(
fts_trx_t* ftt; fts_trx_t* ftt;
ib_alloc_t* heap_alloc; ib_alloc_t* heap_alloc;
mem_heap_t* heap = mem_heap_create(1024); mem_heap_t* heap = mem_heap_create(1024);
trx_named_savept_t* savep;
ut_a(trx->fts_trx == NULL); ut_a(trx->fts_trx == NULL);
@ -2263,14 +2258,6 @@ fts_trx_create(
fts_savepoint_create(ftt->savepoints, NULL, NULL); fts_savepoint_create(ftt->savepoints, NULL, NULL);
fts_savepoint_create(ftt->last_stmt, NULL, NULL); fts_savepoint_create(ftt->last_stmt, NULL, NULL);
/* Copy savepoints that already set before. */
for (savep = UT_LIST_GET_FIRST(trx->trx_savepoints);
savep != NULL;
savep = UT_LIST_GET_NEXT(trx_savepoints, savep)) {
fts_savepoint_take(ftt, savep->name);
}
return(ftt); return(ftt);
} }
@ -5410,7 +5397,7 @@ void
fts_savepoint_take( fts_savepoint_take(
/*===============*/ /*===============*/
fts_trx_t* fts_trx, /*!< in: fts transaction */ fts_trx_t* fts_trx, /*!< in: fts transaction */
const char* name) /*!< in: savepoint name */ const void* name) /*!< in: savepoint */
{ {
mem_heap_t* heap; mem_heap_t* heap;
fts_savepoint_t* savepoint; fts_savepoint_t* savepoint;
@ -5433,31 +5420,21 @@ fts_savepoint_take(
} }
/*********************************************************************//** /*********************************************************************//**
Lookup a savepoint instance by name. Lookup a savepoint instance.
@return ULINT_UNDEFINED if not found */ @return 0 if not found */
UNIV_INLINE static
ulint ulint
fts_savepoint_lookup( fts_savepoint_lookup(
/*==================*/ /*==================*/
ib_vector_t* savepoints, /*!< in: savepoints */ ib_vector_t* savepoints, /*!< in: savepoints */
const char* name) /*!< in: savepoint name */ const void* name) /*!< in: savepoint */
{ {
ulint i; ut_a(ib_vector_size(savepoints) > 0);
for (ulint i= 1; i < ib_vector_size(savepoints); ++i)
ut_a(ib_vector_size(savepoints) > 0); if (name == static_cast<const fts_savepoint_t*>
(ib_vector_get(savepoints, i))->name)
for (i = 1; i < ib_vector_size(savepoints); ++i) { return i;
fts_savepoint_t* savepoint; return 0;
savepoint = static_cast<fts_savepoint_t*>(
ib_vector_get(savepoints, i));
if (strcmp(name, savepoint->name) == 0) {
return(i);
}
}
return(ULINT_UNDEFINED);
} }
/*********************************************************************//** /*********************************************************************//**
@ -5468,7 +5445,7 @@ void
fts_savepoint_release( fts_savepoint_release(
/*==================*/ /*==================*/
trx_t* trx, /*!< in: transaction */ trx_t* trx, /*!< in: transaction */
const char* name) /*!< in: savepoint name */ const void* name) /*!< in: savepoint name */
{ {
ut_a(name != NULL); ut_a(name != NULL);
@ -5476,10 +5453,7 @@ fts_savepoint_release(
ut_a(ib_vector_size(savepoints) > 0); ut_a(ib_vector_size(savepoints) > 0);
ulint i = fts_savepoint_lookup(savepoints, name); if (ulint i = fts_savepoint_lookup(savepoints, name)) {
if (i != ULINT_UNDEFINED) {
ut_a(i >= 1);
fts_savepoint_t* savepoint; fts_savepoint_t* savepoint;
savepoint = static_cast<fts_savepoint_t*>( savepoint = static_cast<fts_savepoint_t*>(
ib_vector_get(savepoints, i)); ib_vector_get(savepoints, i));
@ -5634,9 +5608,8 @@ void
fts_savepoint_rollback( fts_savepoint_rollback(
/*===================*/ /*===================*/
trx_t* trx, /*!< in: transaction */ trx_t* trx, /*!< in: transaction */
const char* name) /*!< in: savepoint name */ const void* name) /*!< in: savepoint */
{ {
ulint i;
ib_vector_t* savepoints; ib_vector_t* savepoints;
ut_a(name != NULL); ut_a(name != NULL);
@ -5645,16 +5618,19 @@ fts_savepoint_rollback(
/* We pop all savepoints from the the top of the stack up to /* We pop all savepoints from the the top of the stack up to
and including the instance that was found. */ and including the instance that was found. */
i = fts_savepoint_lookup(savepoints, name); ulint i = fts_savepoint_lookup(savepoints, name);
if (i != ULINT_UNDEFINED) { if (i == 0) {
/* fts_trx_create() must have been invoked after
this savepoint had been created, and we must roll back
everything. */
i = 1;
}
{
fts_savepoint_t* savepoint; fts_savepoint_t* savepoint;
ut_a(i > 0);
while (ib_vector_size(savepoints) > i) { while (ib_vector_size(savepoints) > i) {
fts_savepoint_t* savepoint;
savepoint = static_cast<fts_savepoint_t*>( savepoint = static_cast<fts_savepoint_t*>(
ib_vector_pop(savepoints)); ib_vector_pop(savepoints));

File diff suppressed because it is too large Load diff

View file

@ -465,8 +465,13 @@ protected:
@see build_template() */ @see build_template() */
void reset_template(); void reset_template();
/** @return whether the table is read-only */ /** Check the transaction is valid.
bool is_read_only(bool altering_to_supported= false) const; @param altering_to_supported whether an ALTER TABLE is being run
to something else than ROW_FORMAT=COMPRESSED
@retval 0 if the transaction is valid for the current operation
@retval HA_ERR_TABLE_READONLY if the table is read-only
@retval HA_ERR_ROLLBACK if the transaction has been aborted */
int is_valid_trx(bool altering_to_supported= false) const noexcept;
inline void update_thd(THD* thd); inline void update_thd(THD* thd);
void update_thd(); void update_thd();
@ -937,12 +942,3 @@ ib_push_frm_error(
@return true if index column length exceeds limit */ @return true if index column length exceeds limit */
MY_ATTRIBUTE((warn_unused_result)) MY_ATTRIBUTE((warn_unused_result))
bool too_big_key_part_length(size_t max_field_len, const KEY& key); bool too_big_key_part_length(size_t max_field_len, const KEY& key);
/** This function is used to rollback one X/Open XA distributed transaction
which is in the prepared state
@param[in] hton InnoDB handlerton
@param[in] xid X/Open XA transaction identification
@return 0 or error number */
int innobase_rollback_by_xid(handlerton* hton, XID* xid);

View file

@ -2249,7 +2249,7 @@ ha_innobase::check_if_supported_inplace_alter(
table->s->table_name.str); table->s->table_name.str);
} }
if (is_read_only(!high_level_read_only if (is_valid_trx(!high_level_read_only
&& (ha_alter_info->handler_flags & ALTER_OPTIONS) && (ha_alter_info->handler_flags & ALTER_OPTIONS)
&& ha_alter_info->create_info->key_block_size == 0 && ha_alter_info->create_info->key_block_size == 0
&& ha_alter_info->create_info->row_type && ha_alter_info->create_info->row_type
@ -9154,7 +9154,7 @@ inline bool rollback_inplace_alter_table(Alter_inplace_info *ha_alter_info,
/* If we have not started a transaction yet, /* If we have not started a transaction yet,
(almost) nothing has been or needs to be done. */ (almost) nothing has been or needs to be done. */
dict_sys.lock(SRW_LOCK_CALL); dict_sys.lock(SRW_LOCK_CALL);
else if (ctx->trx->state == TRX_STATE_NOT_STARTED) else if (!ctx->trx->is_started())
goto free_and_exit; goto free_and_exit;
else if (ctx->new_table) else if (ctx->new_table)
{ {
@ -11382,7 +11382,7 @@ lock_fail:
to remove the newly created table or to remove the newly created table or
index from data dictionary and table cache index from data dictionary and table cache
in rollback_inplace_alter_table() */ in rollback_inplace_alter_table() */
if (trx->state == TRX_STATE_NOT_STARTED) { if (!trx->is_started()) {
trx_start_for_ddl(trx); trx_start_for_ddl(trx);
} }
@ -11549,7 +11549,7 @@ err_index:
purge_sys.resume_FTS(); purge_sys.resume_FTS();
} }
if (trx->state == TRX_STATE_NOT_STARTED) { if (!trx->is_started()) {
/* Transaction may have been rolled back /* Transaction may have been rolled back
due to a lock wait timeout, deadlock, due to a lock wait timeout, deadlock,
or a KILL statement. So restart the or a KILL statement. So restart the

View file

@ -68,8 +68,6 @@ enum dberr_t {
noticed */ noticed */
DB_CANNOT_DROP_CONSTRAINT, /*!< dropping a foreign key constraint DB_CANNOT_DROP_CONSTRAINT, /*!< dropping a foreign key constraint
from a table failed */ from a table failed */
DB_NO_SAVEPOINT, /*!< no savepoint exists with the given
name */
DB_TABLESPACE_EXISTS, /*!< we cannot create a new single-table DB_TABLESPACE_EXISTS, /*!< we cannot create a new single-table
tablespace because a file of the same tablespace because a file of the same
name already exists */ name already exists */

View file

@ -201,9 +201,9 @@ struct fts_trx_t {
/** Information required for transaction savepoint handling. */ /** Information required for transaction savepoint handling. */
struct fts_savepoint_t { struct fts_savepoint_t {
char* name; /*!< First entry is always NULL, the const void* name; /*!< First entry is always NULL, the
default instance. Otherwise the name default instance. Otherwise the
of the savepoint */ savepoint */
ib_rbt_t* tables; /*!< Modified FTS tables */ ib_rbt_t* tables; /*!< Modified FTS tables */
}; };
@ -666,7 +666,7 @@ void
fts_savepoint_take( fts_savepoint_take(
/*===============*/ /*===============*/
fts_trx_t* fts_trx, /*!< in: fts transaction */ fts_trx_t* fts_trx, /*!< in: fts transaction */
const char* name); /*!< in: savepoint name */ const void* name); /*!< in: savepoint */
/**********************************************************************//** /**********************************************************************//**
Refresh last statement savepoint. */ Refresh last statement savepoint. */
@ -681,7 +681,7 @@ void
fts_savepoint_release( fts_savepoint_release(
/*==================*/ /*==================*/
trx_t* trx, /*!< in: transaction */ trx_t* trx, /*!< in: transaction */
const char* name); /*!< in: savepoint name */ const void* name); /*!< in: savepoint */
/** Clear cache. /** Clear cache.
@param[in,out] cache fts cache */ @param[in,out] cache fts cache */
@ -702,7 +702,7 @@ void
fts_savepoint_rollback( fts_savepoint_rollback(
/*===================*/ /*===================*/
trx_t* trx, /*!< in: transaction */ trx_t* trx, /*!< in: transaction */
const char* name); /*!< in: savepoint name */ const void* name); /*!< in: savepoint */
/*********************************************************************//** /*********************************************************************//**
Rollback to and including savepoint indentified by name. */ Rollback to and including savepoint indentified by name. */

View file

@ -172,7 +172,7 @@ row_mysql_handle_errors(
during the function entry */ during the function entry */
trx_t* trx, /*!< in: transaction */ trx_t* trx, /*!< in: transaction */
que_thr_t* thr, /*!< in: query thread, or NULL */ que_thr_t* thr, /*!< in: query thread, or NULL */
trx_savept_t* savept) /*!< in: savepoint, or NULL */ const undo_no_t*savept) /*!< in: pointer to savepoint, or nullptr */
MY_ATTRIBUTE((nonnull(1,2))); MY_ATTRIBUTE((nonnull(1,2)));
/********************************************************************//** /********************************************************************//**
Create a prebuilt struct for a MySQL table handle. Create a prebuilt struct for a MySQL table handle.

View file

@ -74,62 +74,6 @@ trx_rollback_for_mysql(
/*===================*/ /*===================*/
trx_t* trx) /*!< in/out: transaction */ trx_t* trx) /*!< in/out: transaction */
MY_ATTRIBUTE((nonnull)); MY_ATTRIBUTE((nonnull));
/*******************************************************************//**
Rollback the latest SQL statement for MySQL.
@return error code or DB_SUCCESS */
dberr_t
trx_rollback_last_sql_stat_for_mysql(
/*=================================*/
trx_t* trx) /*!< in/out: transaction */
MY_ATTRIBUTE((nonnull));
/*******************************************************************//**
Rolls back a transaction back to a named savepoint. Modifications after the
savepoint are undone but InnoDB does NOT release the corresponding locks
which are stored in memory. If a lock is 'implicit', that is, a new inserted
row holds a lock where the lock information is carried by the trx id stored in
the row, these locks are naturally released in the rollback. Savepoints which
were set after this savepoint are deleted.
@return if no savepoint of the name found then DB_NO_SAVEPOINT,
otherwise DB_SUCCESS */
dberr_t
trx_rollback_to_savepoint_for_mysql(
/*================================*/
trx_t* trx, /*!< in: transaction handle */
const char* savepoint_name, /*!< in: savepoint name */
int64_t* mysql_binlog_cache_pos) /*!< out: the MySQL binlog cache
position corresponding to this
savepoint; MySQL needs this
information to remove the
binlog entries of the queries
executed after the savepoint */
MY_ATTRIBUTE((nonnull, warn_unused_result));
/*******************************************************************//**
Creates a named savepoint. If the transaction is not yet started, starts it.
If there is already a savepoint of the same name, this call erases that old
savepoint and replaces it with a new. Savepoints are deleted in a transaction
commit or rollback.
@return always DB_SUCCESS */
dberr_t
trx_savepoint_for_mysql(
/*====================*/
trx_t* trx, /*!< in: transaction handle */
const char* savepoint_name, /*!< in: savepoint name */
int64_t binlog_cache_pos) /*!< in: MySQL binlog cache
position corresponding to this
connection at the time of the
savepoint */
MY_ATTRIBUTE((nonnull));
/*******************************************************************//**
Releases a named savepoint. Savepoints which
were set after this savepoint are deleted.
@return if no savepoint of the name found then DB_NO_SAVEPOINT,
otherwise DB_SUCCESS */
dberr_t
trx_release_savepoint_for_mysql(
/*============================*/
trx_t* trx, /*!< in: transaction handle */
const char* savepoint_name) /*!< in: savepoint name */
MY_ATTRIBUTE((nonnull, warn_unused_result));
/** Rollback node states */ /** Rollback node states */
enum roll_node_state { enum roll_node_state {
@ -144,25 +88,9 @@ enum roll_node_state {
struct roll_node_t{ struct roll_node_t{
que_common_t common; /*!< node type: QUE_NODE_ROLLBACK */ que_common_t common; /*!< node type: QUE_NODE_ROLLBACK */
enum roll_node_state state; /*!< node execution state */ enum roll_node_state state; /*!< node execution state */
const trx_savept_t* savept; /*!< savepoint to which to undo_no_t savept; /*!< savepoint to which to
roll back, in the case of a roll back; 0=entire transaction */
partial rollback */
que_thr_t* undo_thr;/*!< undo query graph */ que_thr_t* undo_thr;/*!< undo query graph */
}; };
/** A savepoint set with SQL's "SAVEPOINT savepoint_id" command */
struct trx_named_savept_t{
char* name; /*!< savepoint name */
trx_savept_t savept; /*!< the undo number corresponding to
the savepoint */
int64_t mysql_binlog_cache_pos;
/*!< the MySQL binlog cache position
corresponding to this savepoint, not
defined if the MySQL binlogging is not
enabled */
UT_LIST_NODE_T(trx_named_savept_t)
trx_savepoints; /*!< the list of savepoints of a
transaction */
};
#endif #endif

View file

@ -495,19 +495,7 @@ class rw_trx_hash_t
#ifdef UNIV_DEBUG #ifdef UNIV_DEBUG
static void validate_element(trx_t *trx) static void validate_element(trx_t *trx);
{
ut_ad(!trx->read_only || !trx->rsegs.m_redo.rseg);
ut_ad(!trx->is_autocommit_non_locking());
/* trx->state can be anything except TRX_STATE_NOT_STARTED */
ut_d(bool acquire_trx_mutex = !trx->mutex_is_owner());
ut_d(if (acquire_trx_mutex) trx->mutex_lock());
ut_ad(trx_state_eq(trx, TRX_STATE_ACTIVE) ||
trx_state_eq(trx, TRX_STATE_COMMITTED_IN_MEMORY) ||
trx_state_eq(trx, TRX_STATE_PREPARED_RECOVERED) ||
trx_state_eq(trx, TRX_STATE_PREPARED));
ut_d(if (acquire_trx_mutex) trx->mutex_unlock());
}
struct debug_iterator_arg struct debug_iterator_arg

View file

@ -149,19 +149,15 @@ void trx_start_for_ddl_low(trx_t *trx);
ut_ad((t)->start_file == 0); \ ut_ad((t)->start_file == 0); \
(t)->start_line = __LINE__; \ (t)->start_line = __LINE__; \
(t)->start_file = __FILE__; \ (t)->start_file = __FILE__; \
t->state= TRX_STATE_NOT_STARTED; \
trx_start_for_ddl_low(t); \ trx_start_for_ddl_low(t); \
} while (0) } while (0)
#else #else
# define trx_start_for_ddl(t) trx_start_for_ddl_low(t) # define trx_start_for_ddl(t) trx_start_for_ddl_low(t)
#endif /* UNIV_DEBUG */ #endif /* UNIV_DEBUG */
/**********************************************************************//** /** Commit a transaction */
Does the transaction commit for MySQL. void trx_commit_for_mysql(trx_t *trx) noexcept;
@return DB_SUCCESS or error number */
dberr_t
trx_commit_for_mysql(
/*=================*/
trx_t* trx); /*!< in/out: transaction */
/** XA PREPARE a transaction. /** XA PREPARE a transaction.
@param[in,out] trx transaction to prepare */ @param[in,out] trx transaction to prepare */
void trx_prepare_for_mysql(trx_t* trx); void trx_prepare_for_mysql(trx_t* trx);
@ -184,12 +180,6 @@ trx_t* trx_get_trx_by_xid(const XID* xid);
/** Durably write log until trx->commit_lsn /** Durably write log until trx->commit_lsn
(if trx_t::commit_in_memory() was invoked with flush_log_later=true). */ (if trx_t::commit_in_memory() was invoked with flush_log_later=true). */
void trx_commit_complete_for_mysql(trx_t *trx); void trx_commit_complete_for_mysql(trx_t *trx);
/**********************************************************************//**
Marks the latest SQL statement ended. */
void
trx_mark_sql_stat_end(
/*==================*/
trx_t* trx); /*!< in: trx handle */
/****************************************************************//** /****************************************************************//**
Prepares a transaction for commit/rollback. */ Prepares a transaction for commit/rollback. */
void void
@ -663,6 +653,7 @@ public:
Possible states: Possible states:
TRX_STATE_NOT_STARTED TRX_STATE_NOT_STARTED
TRX_STATE_ABORTED
TRX_STATE_ACTIVE TRX_STATE_ACTIVE
TRX_STATE_PREPARED TRX_STATE_PREPARED
TRX_STATE_PREPARED_RECOVERED (special case of TRX_STATE_PREPARED) TRX_STATE_PREPARED_RECOVERED (special case of TRX_STATE_PREPARED)
@ -672,6 +663,8 @@ public:
Regular transactions: Regular transactions:
* NOT_STARTED -> ACTIVE -> COMMITTED -> NOT_STARTED * NOT_STARTED -> ACTIVE -> COMMITTED -> NOT_STARTED
* NOT_STARTED -> ABORTED (when thd_mark_transaction_to_rollback() is called)
* ABORTED -> NOT_STARTED (acknowledging the rollback of a transaction)
Auto-commit non-locking read-only: Auto-commit non-locking read-only:
* NOT_STARTED -> ACTIVE -> NOT_STARTED * NOT_STARTED -> ACTIVE -> NOT_STARTED
@ -708,16 +701,18 @@ public:
do we remove it from the read-only list and put it on the read-write do we remove it from the read-only list and put it on the read-write
list. During this switch we assign it a rollback segment. list. During this switch we assign it a rollback segment.
When a transaction is NOT_STARTED, it can be in trx_list. It cannot be When a transaction is NOT_STARTED or ABORTED, it can be in trx_list.
in rw_trx_hash. It cannot be in rw_trx_hash.
ACTIVE->PREPARED->COMMITTED is only possible when trx is in rw_trx_hash. ACTIVE->PREPARED->COMMITTED and ACTIVE->COMMITTED is only possible when
The transition ACTIVE->PREPARED is protected by trx->mutex. trx is in rw_trx_hash. These transitions are protected by trx_t::mutex.
ACTIVE->COMMITTED is possible when the transaction is in COMMITTED->NOT_STARTED is possible when trx_t::mutex is being held.
rw_trx_hash. The transaction would already have been removed from rw_trx_hash by
trx_sys_t::deregister_rw() on the transition to COMMITTED.
Transitions to COMMITTED are protected by trx_t::mutex. */ Transitions between NOT_STARTED and ABORTED can be performed at any time by
the thread that is associated with the transaction. */
Atomic_relaxed<trx_state_t> state; Atomic_relaxed<trx_state_t> state;
/** The locks of the transaction. Protected by lock_sys.latch /** The locks of the transaction. Protected by lock_sys.latch
@ -733,6 +728,14 @@ public:
bool is_wsrep() const { return false; } bool is_wsrep() const { return false; }
#endif /* WITH_WSREP */ #endif /* WITH_WSREP */
/** @return whether the transaction has been started */
bool is_started() const noexcept
{
static_assert(TRX_STATE_NOT_STARTED == 0, "");
static_assert(TRX_STATE_ABORTED == 1, "");
return state > TRX_STATE_ABORTED;
}
/** Consistent read view of the transaction */ /** Consistent read view of the transaction */
ReadView read_view; ReadView read_view;
@ -839,10 +842,6 @@ public:
it is a stored procedure with a COMMIT it is a stored procedure with a COMMIT
WORK statement, for instance */ WORK statement, for instance */
/*------------------------------*/ /*------------------------------*/
UT_LIST_BASE_NODE_T(trx_named_savept_t)
trx_savepoints; /*!< savepoints set with SAVEPOINT ...,
oldest first */
/*------------------------------*/
undo_no_t undo_no; /*!< next undo log record number to undo_no_t undo_no; /*!< next undo log record number to
assign; since the undo log is assign; since the undo log is
private for a transaction, this private for a transaction, this
@ -850,7 +849,7 @@ public:
with no gaps; thus it represents with no gaps; thus it represents
the number of modified/inserted the number of modified/inserted
rows in a transaction */ rows in a transaction */
trx_savept_t last_sql_stat_start; undo_no_t last_stmt_start;
/*!< undo_no when the last sql statement /*!< undo_no when the last sql statement
was started: in case of an error, trx was started: in case of an error, trx
is rolled back down to this number */ is rolled back down to this number */
@ -949,16 +948,17 @@ public:
void evict_table(table_id_t table_id, bool reset_only= false); void evict_table(table_id_t table_id, bool reset_only= false);
/** Initiate rollback. /** Initiate rollback.
@param savept savepoint to which to roll back @param savept pointer to savepoint; nullptr=entire transaction
@return error code or DB_SUCCESS */ @return error code or DB_SUCCESS */
dberr_t rollback(trx_savept_t *savept= nullptr); dberr_t rollback(const undo_no_t *savept= nullptr) noexcept;
/** Roll back an active transaction. /** Roll back an active transaction.
@param savept savepoint to which to roll back */ @param savept pointer to savepoint; nullptr=entire transaction
inline void rollback_low(trx_savept_t *savept= nullptr); @return error code or DB_SUCCESS */
dberr_t rollback_low(const undo_no_t *savept= nullptr) noexcept;
/** Finish rollback. /** Finish rollback.
@return whether the rollback was completed normally @return whether the rollback was completed normally
@retval false if the rollback was aborted by shutdown */ @retval false if the rollback was aborted by shutdown */
inline bool rollback_finish(); bool rollback_finish() noexcept;
private: private:
/** Apply any changes to tables for which online DDL is in progress. */ /** Apply any changes to tables for which online DDL is in progress. */
ATTRIBUTE_COLD void apply_log(); ATTRIBUTE_COLD void apply_log();
@ -968,9 +968,10 @@ private:
@param mtr mini-transaction (if there are any persistent modifications) */ @param mtr mini-transaction (if there are any persistent modifications) */
inline void commit_in_memory(const mtr_t *mtr); inline void commit_in_memory(const mtr_t *mtr);
/** Write log for committing the transaction. */ /** Write log for committing the transaction. */
void commit_persist(); void commit_persist() noexcept;
/** Clean up the transaction after commit_in_memory() */ /** Clean up the transaction after commit_in_memory()
void commit_cleanup(); @return false (always) */
bool commit_cleanup() noexcept;
/** Commit the transaction in a mini-transaction. /** Commit the transaction in a mini-transaction.
@param mtr mini-transaction (if there are any persistent modifications) */ @param mtr mini-transaction (if there are any persistent modifications) */
void commit_low(mtr_t *mtr= nullptr); void commit_low(mtr_t *mtr= nullptr);
@ -985,7 +986,7 @@ private:
inline void write_serialisation_history(mtr_t *mtr); inline void write_serialisation_history(mtr_t *mtr);
public: public:
/** Commit the transaction. */ /** Commit the transaction. */
void commit(); void commit() noexcept;
/** Try to drop a persistent table. /** Try to drop a persistent table.
@param table persistent table @param table persistent table
@ -1005,16 +1006,6 @@ public:
void commit(std::vector<pfs_os_file_t> &deleted); void commit(std::vector<pfs_os_file_t> &deleted);
/** Discard all savepoints */
void savepoints_discard()
{ savepoints_discard(UT_LIST_GET_FIRST(trx_savepoints)); }
/** Discard all savepoints starting from a particular savepoint.
@param savept first savepoint to discard */
void savepoints_discard(trx_named_savept_t *savept);
bool is_referenced() const bool is_referenced() const
{ {
return (skip_lock_inheritance_and_n_ref & ~(1U << 31)) > 0; return (skip_lock_inheritance_and_n_ref & ~(1U << 31)) > 0;
@ -1128,15 +1119,6 @@ private:
trx_rseg_t *assign_temp_rseg(); trx_rseg_t *assign_temp_rseg();
}; };
/**
Check if transaction is started.
@param[in] trx Transaction whose state we need to check
@reutrn true if transaction is in state started */
inline bool trx_is_started(const trx_t* trx)
{
return trx->state != TRX_STATE_NOT_STARTED;
}
/* Transaction isolation levels (trx->isolation_level) */ /* Transaction isolation levels (trx->isolation_level) */
#define TRX_ISO_READ_UNCOMMITTED 0 /* dirty read: non-locking #define TRX_ISO_READ_UNCOMMITTED 0 /* dirty read: non-locking
SELECTs are performed so that SELECTs are performed so that

View file

@ -67,6 +67,8 @@ trx_state_eq(
&& thd_get_error_number(trx->mysql_thd))); && thd_get_error_number(trx->mysql_thd)));
return(true); return(true);
case TRX_STATE_ABORTED:
break;
} }
ut_error; ut_error;
#endif /* UNIV_DEBUG */ #endif /* UNIV_DEBUG */

View file

@ -52,6 +52,8 @@ constexpr uint innodb_purge_batch_size_MAX= 5000;
/** Transaction states (trx_t::state) */ /** Transaction states (trx_t::state) */
enum trx_state_t { enum trx_state_t {
TRX_STATE_NOT_STARTED, TRX_STATE_NOT_STARTED,
/** The transaction was aborted (rolled back) due to an error */
TRX_STATE_ABORTED,
TRX_STATE_ACTIVE, TRX_STATE_ACTIVE,
/** XA PREPARE has been executed; only XA COMMIT or XA ROLLBACK /** XA PREPARE has been executed; only XA COMMIT or XA ROLLBACK
@ -59,6 +61,7 @@ enum trx_state_t {
TRX_STATE_PREPARED, TRX_STATE_PREPARED,
/** XA PREPARE transaction that was returned to ha_recover() */ /** XA PREPARE transaction that was returned to ha_recover() */
TRX_STATE_PREPARED_RECOVERED, TRX_STATE_PREPARED_RECOVERED,
/** The transaction has been committed (or completely rolled back) */
TRX_STATE_COMMITTED_IN_MEMORY TRX_STATE_COMMITTED_IN_MEMORY
}; };
@ -76,8 +79,6 @@ struct trx_undo_t;
struct roll_node_t; struct roll_node_t;
/** Commit command node in a query graph */ /** Commit command node in a query graph */
struct commit_node_t; struct commit_node_t;
/** SAVEPOINT command node in a query graph */
struct trx_named_savept_t;
/* @} */ /* @} */
/** Row identifier (DB_ROW_ID, DATA_ROW_ID) */ /** Row identifier (DB_ROW_ID, DATA_ROW_ID) */
@ -89,11 +90,6 @@ typedef ib_id_t roll_ptr_t;
/** Undo number */ /** Undo number */
typedef ib_id_t undo_no_t; typedef ib_id_t undo_no_t;
/** Transaction savepoint */
struct trx_savept_t{
undo_no_t least_undo_no; /*!< least undo number to undo */
};
/** File objects */ /** File objects */
/* @{ */ /* @{ */
/** Undo segment header */ /** Undo segment header */

View file

@ -6692,23 +6692,19 @@ lock_unlock_table_autoinc(
/*======================*/ /*======================*/
trx_t* trx) /*!< in/out: transaction */ trx_t* trx) /*!< in/out: transaction */
{ {
lock_sys.assert_unlocked(); /* This function is invoked for a running transaction by the thread
ut_ad(!trx->mutex_is_owner()); that is serving the transaction. Therefore it is not necessary to
ut_ad(!trx->lock.wait_lock); hold trx->mutex here. */
/* This can be invoked on NOT_STARTED, ACTIVE, PREPARED, lock_sys.assert_unlocked();
but not COMMITTED transactions. */ ut_ad(!trx->mutex_is_owner());
ut_ad(!trx->lock.wait_lock);
ut_d(trx_state_t trx_state{trx->state});
ut_ad(trx_state == TRX_STATE_ACTIVE || trx_state == TRX_STATE_PREPARED ||
trx_state == TRX_STATE_NOT_STARTED);
ut_ad(trx_state_eq(trx, TRX_STATE_NOT_STARTED) if (lock_trx_holds_autoinc_locks(trx))
|| !trx_state_eq(trx, TRX_STATE_COMMITTED_IN_MEMORY)); lock_release_autoinc_locks(trx);
/* This function is invoked for a running transaction by the
thread that is serving the transaction. Therefore it is not
necessary to hold trx->mutex here. */
if (lock_trx_holds_autoinc_locks(trx)) {
lock_release_autoinc_locks(trx);
}
} }
/** Handle a pending lock wait (DB_LOCK_WAIT) in a semi-consistent read /** Handle a pending lock wait (DB_LOCK_WAIT) in a semi-consistent read

View file

@ -514,8 +514,7 @@ que_thr_step(
for_step(thr); for_step(thr);
} else if (type == QUE_NODE_PROC) { } else if (type == QUE_NODE_PROC) {
if (thr->prev_node == que_node_get_parent(node)) { if (thr->prev_node == que_node_get_parent(node)) {
trx->last_sql_stat_start.least_undo_no trx->last_stmt_start = trx->undo_no;
= trx->undo_no;
} }
proc_step(thr); proc_step(thr);

View file

@ -625,7 +625,7 @@ row_mysql_handle_errors(
function */ function */
trx_t* trx, /*!< in: transaction */ trx_t* trx, /*!< in: transaction */
que_thr_t* thr, /*!< in: query thread, or NULL */ que_thr_t* thr, /*!< in: query thread, or NULL */
trx_savept_t* savept) /*!< in: savepoint, or NULL */ const undo_no_t*savept) /*!< in: pointer to savepoint, or nullptr */
{ {
dberr_t err; dberr_t err;
@ -681,8 +681,7 @@ handle_new_error:
} }
/* MariaDB will roll back the entire transaction. */ /* MariaDB will roll back the entire transaction. */
trx->bulk_insert = false; trx->bulk_insert = false;
trx->last_sql_stat_start.least_undo_no = 0; trx->last_stmt_start = 0;
trx->savepoints_discard();
break; break;
case DB_LOCK_WAIT: case DB_LOCK_WAIT:
err = lock_wait(thr); err = lock_wait(thr);
@ -700,7 +699,6 @@ handle_new_error:
rollback: rollback:
/* Roll back the whole transaction; this resolution was added /* Roll back the whole transaction; this resolution was added
to version 3.23.43 */ to version 3.23.43 */
trx->rollback(); trx->rollback();
break; break;
@ -1137,7 +1135,7 @@ row_lock_table_autoinc_for_mysql(
trx->error_state = err; trx->error_state = err;
} while (err != DB_SUCCESS } while (err != DB_SUCCESS
&& row_mysql_handle_errors(&err, trx, thr, NULL)); && row_mysql_handle_errors(&err, trx, thr, nullptr));
trx->op_info = ""; trx->op_info = "";
@ -1179,7 +1177,7 @@ row_lock_table(row_prebuilt_t* prebuilt)
prebuilt->select_lock_type), thr); prebuilt->select_lock_type), thr);
trx->error_state = err; trx->error_state = err;
} while (err != DB_SUCCESS } while (err != DB_SUCCESS
&& row_mysql_handle_errors(&err, trx, thr, NULL)); && row_mysql_handle_errors(&err, trx, thr, nullptr));
trx->op_info = ""; trx->op_info = "";
@ -1218,7 +1216,6 @@ row_insert_for_mysql(
row_prebuilt_t* prebuilt, row_prebuilt_t* prebuilt,
ins_mode_t ins_mode) ins_mode_t ins_mode)
{ {
trx_savept_t savept;
que_thr_t* thr; que_thr_t* thr;
dberr_t err; dberr_t err;
ibool was_lock_wait; ibool was_lock_wait;
@ -1272,7 +1269,7 @@ row_insert_for_mysql(
roll back to the start of the transaction. For correctness, it roll back to the start of the transaction. For correctness, it
would suffice to roll back to the start of the first insert would suffice to roll back to the start of the first insert
into this empty table, but we will keep it simple and efficient. */ into this empty table, but we will keep it simple and efficient. */
savept.least_undo_no = trx->bulk_insert ? 0 : trx->undo_no; const undo_no_t savept{trx->bulk_insert ? 0 : trx->undo_no};
thr = que_fork_get_first_thr(prebuilt->ins_graph); thr = que_fork_get_first_thr(prebuilt->ins_graph);
@ -1586,7 +1583,6 @@ init_fts_doc_id_for_ref(
dberr_t dberr_t
row_update_for_mysql(row_prebuilt_t* prebuilt) row_update_for_mysql(row_prebuilt_t* prebuilt)
{ {
trx_savept_t savept;
dberr_t err; dberr_t err;
que_thr_t* thr; que_thr_t* thr;
dict_index_t* clust_index; dict_index_t* clust_index;
@ -1643,7 +1639,7 @@ row_update_for_mysql(row_prebuilt_t* prebuilt)
generated for the table: MySQL does not know anything about generated for the table: MySQL does not know anything about
the row id used as the clustered index key */ the row id used as the clustered index key */
savept.least_undo_no = trx->undo_no; undo_no_t savept = trx->undo_no;
thr = que_fork_get_first_thr(prebuilt->upd_graph); thr = que_fork_get_first_thr(prebuilt->upd_graph);

View file

@ -1208,8 +1208,7 @@ re_scan:
mtr->commit(); mtr->commit();
trx->error_state = err; trx->error_state = err;
thr->lock_state = QUE_THR_LOCK_ROW; thr->lock_state = QUE_THR_LOCK_ROW;
if (row_mysql_handle_errors( if (row_mysql_handle_errors(&err, trx, thr, 0)) {
&err, trx, thr, NULL)) {
thr->lock_state = QUE_THR_LOCK_NOLOCK; thr->lock_state = QUE_THR_LOCK_NOLOCK;
mtr->start(); mtr->start();
@ -5873,7 +5872,7 @@ lock_table_wait:
trx->error_state = err; trx->error_state = err;
thr->lock_state = QUE_THR_LOCK_ROW; thr->lock_state = QUE_THR_LOCK_ROW;
if (row_mysql_handle_errors(&err, trx, thr, NULL)) { if (row_mysql_handle_errors(&err, trx, thr, nullptr)) {
/* It was a lock wait, and it ended */ /* It was a lock wait, and it ended */
thr->lock_state = QUE_THR_LOCK_NOLOCK; thr->lock_state = QUE_THR_LOCK_NOLOCK;

View file

@ -1054,7 +1054,7 @@ static void fetch_data_into_cache(trx_i_s_cache_t *cache)
&trx != (purge_sys.query ? purge_sys.query->trx : nullptr)) &trx != (purge_sys.query ? purge_sys.query->trx : nullptr))
{ {
trx.mutex_lock(); trx.mutex_lock();
if (trx.state != TRX_STATE_NOT_STARTED) if (trx.is_started())
fetch_data_into_cache_low(cache, &trx); fetch_data_into_cache_low(cache, &trx);
trx.mutex_unlock(); trx.mutex_unlock();
} }

View file

@ -43,6 +43,7 @@ Created 3/26/1996 Heikki Tuuri
#include "trx0sys.h" #include "trx0sys.h"
#include "trx0trx.h" #include "trx0trx.h"
#include "trx0undo.h" #include "trx0undo.h"
#include "log.h"
#ifdef UNIV_PFS_THREAD #ifdef UNIV_PFS_THREAD
mysql_pfs_key_t trx_rollback_clean_thread_key; mysql_pfs_key_t trx_rollback_clean_thread_key;
@ -54,10 +55,7 @@ bool trx_rollback_is_active;
/** In crash recovery, the current trx to be rolled back; NULL otherwise */ /** In crash recovery, the current trx to be rolled back; NULL otherwise */
const trx_t* trx_roll_crash_recv_trx; const trx_t* trx_roll_crash_recv_trx;
/** Finish transaction rollback. bool trx_t::rollback_finish() noexcept
@return whether the rollback was completed normally
@retval false if the rollback was aborted by shutdown */
inline bool trx_t::rollback_finish()
{ {
mod_tables.clear(); mod_tables.clear();
apply_online_log= false; apply_online_log= false;
@ -85,34 +83,28 @@ inline bool trx_t::rollback_finish()
undo= nullptr; undo= nullptr;
} }
commit_low(); commit_low();
commit_cleanup(); return commit_cleanup();
return false;
} }
/** Roll back an active transaction. */ dberr_t trx_t::rollback_low(const undo_no_t *savept) noexcept
inline void trx_t::rollback_low(trx_savept_t *savept)
{ {
op_info= "rollback";
mem_heap_t *heap= mem_heap_create(512); mem_heap_t *heap= mem_heap_create(512);
roll_node_t *roll_node= roll_node_create(heap); roll_node_t *roll_node= roll_node_create(heap);
roll_node->savept= savept;
roll_node->savept= savept ? *savept : 0;
ut_ad(!in_rollback); ut_ad(!in_rollback);
#ifdef UNIV_DEBUG #ifdef UNIV_DEBUG
if (savept)
{ {
const auto s= state; ut_ad(state == TRX_STATE_ACTIVE);
ut_ad(s == TRX_STATE_ACTIVE || ut_ad(mysql_thd);
s == TRX_STATE_PREPARED || ut_ad(!is_recovered);
s == TRX_STATE_PREPARED_RECOVERED);
if (savept)
{
ut_ad(s == TRX_STATE_ACTIVE);
ut_ad(mysql_thd);
ut_ad(!is_recovered);
}
} }
#endif #endif
error_state = DB_SUCCESS; error_state= DB_SUCCESS;
if (has_logged()) if (has_logged())
{ {
@ -139,7 +131,7 @@ inline void trx_t::rollback_low(trx_savept_t *savept)
victim. Galera transaction abort can be invoked during partial rollback. */ victim. Galera transaction abort can be invoked during partial rollback. */
ut_ad(!(lock.was_chosen_as_deadlock_victim & 1)); ut_ad(!(lock.was_chosen_as_deadlock_victim & 1));
ut_a(error_state == DB_SUCCESS); ut_a(error_state == DB_SUCCESS);
const undo_no_t limit= savept->least_undo_no; const undo_no_t limit{*savept};
apply_online_log= false; apply_online_log= false;
for (trx_mod_tables_t::iterator i= mod_tables.begin(); for (trx_mod_tables_t::iterator i= mod_tables.begin();
i != mod_tables.end(); ) i != mod_tables.end(); )
@ -155,49 +147,34 @@ inline void trx_t::rollback_low(trx_savept_t *savept)
} }
mem_heap_free(heap); mem_heap_free(heap);
op_info= "";
return error_state;
} }
/** Initiate rollback. dberr_t trx_t::rollback(const undo_no_t *savept) noexcept
@param savept savepoint
@return error code or DB_SUCCESS */
dberr_t trx_t::rollback(trx_savept_t *savept)
{ {
ut_ad(!mutex_is_owner()); ut_ad(!mutex_is_owner());
if (state == TRX_STATE_NOT_STARTED) switch (state) {
{ case TRX_STATE_ABORTED:
ut_ad(!savept);
state= TRX_STATE_NOT_STARTED;
/* fall through */
case TRX_STATE_NOT_STARTED:
error_state= DB_SUCCESS; error_state= DB_SUCCESS;
return DB_SUCCESS; return DB_SUCCESS;
case TRX_STATE_PREPARED:
case TRX_STATE_PREPARED_RECOVERED:
case TRX_STATE_COMMITTED_IN_MEMORY:
ut_ad("invalid state" == 0);
/* fall through */
case TRX_STATE_ACTIVE:
break;
} }
ut_ad(state == TRX_STATE_ACTIVE);
#ifdef WITH_WSREP #ifdef WITH_WSREP
if (!savept && is_wsrep() && wsrep_thd_is_SR(mysql_thd)) if (!savept && is_wsrep() && wsrep_thd_is_SR(mysql_thd))
wsrep_handle_SR_rollback(nullptr, mysql_thd); wsrep_handle_SR_rollback(nullptr, mysql_thd);
#endif /* WITH_WSREP */ #endif /* WITH_WSREP */
rollback_low(savept); return rollback_low(savept);
return error_state;
}
/*******************************************************************//**
Rollback a transaction used in MySQL.
@return error code or DB_SUCCESS */
static
dberr_t
trx_rollback_for_mysql_low(
/*=======================*/
trx_t* trx) /*!< in/out: transaction */
{
trx->op_info = "rollback";
/* If we are doing the XA recovery of prepared transactions,
then the transaction object does not have an InnoDB session
object, and we set a dummy session that we use for all MySQL
transactions. */
trx->rollback_low();
trx->op_info = "";
return(trx->error_state);
} }
/** Rollback a transaction used in MySQL /** Rollback a transaction used in MySQL
@ -211,6 +188,9 @@ dberr_t trx_rollback_for_mysql(trx_t* trx)
that is associated with the current thread. */ that is associated with the current thread. */
switch (trx->state) { switch (trx->state) {
case TRX_STATE_ABORTED:
trx->state = TRX_STATE_NOT_STARTED;
/* fall through */
case TRX_STATE_NOT_STARTED: case TRX_STATE_NOT_STARTED:
trx->will_lock = false; trx->will_lock = false;
ut_ad(trx->mysql_thd); ut_ad(trx->mysql_thd);
@ -222,13 +202,13 @@ dberr_t trx_rollback_for_mysql(trx_t* trx)
ut_ad(!trx->is_wsrep()); ut_ad(!trx->is_wsrep());
trx->lock.was_chosen_as_deadlock_victim= false; trx->lock.was_chosen_as_deadlock_victim= false;
#endif #endif
ut_a(UT_LIST_GET_LEN(trx->lock.trx_locks) == 0);
return(DB_SUCCESS); return(DB_SUCCESS);
case TRX_STATE_ACTIVE: case TRX_STATE_ACTIVE:
ut_ad(trx->mysql_thd); ut_ad(trx->mysql_thd);
ut_ad(!trx->is_recovered); ut_ad(!trx->is_recovered);
ut_ad(!trx->is_autocommit_non_locking() || trx->read_only); ut_ad(!trx->is_autocommit_non_locking() || trx->read_only);
return(trx_rollback_for_mysql_low(trx)); return trx->rollback_low();
case TRX_STATE_PREPARED: case TRX_STATE_PREPARED:
case TRX_STATE_PREPARED_RECOVERED: case TRX_STATE_PREPARED_RECOVERED:
@ -266,7 +246,7 @@ dberr_t trx_rollback_for_mysql(trx_t* trx)
mtr.commit(); mtr.commit();
ut_ad(mtr.commit_lsn() > 0); ut_ad(mtr.commit_lsn() > 0);
} }
return(trx_rollback_for_mysql_low(trx)); return trx->rollback_low();
case TRX_STATE_COMMITTED_IN_MEMORY: case TRX_STATE_COMMITTED_IN_MEMORY:
ut_ad(!trx->is_autocommit_non_locking()); ut_ad(!trx->is_autocommit_non_locking());
@ -277,296 +257,6 @@ dberr_t trx_rollback_for_mysql(trx_t* trx)
return(DB_CORRUPTION); return(DB_CORRUPTION);
} }
/*******************************************************************//**
Rollback the latest SQL statement for MySQL.
@return error code or DB_SUCCESS */
dberr_t
trx_rollback_last_sql_stat_for_mysql(
/*=================================*/
trx_t* trx) /*!< in/out: transaction */
{
dberr_t err;
/* We are reading trx->state without holding trx->mutex
here, because the statement rollback should be invoked for a
running active MySQL transaction that is associated with the
current thread. */
ut_ad(trx->mysql_thd);
switch (trx->state) {
case TRX_STATE_NOT_STARTED:
return(DB_SUCCESS);
case TRX_STATE_ACTIVE:
ut_ad(trx->mysql_thd);
ut_ad(!trx->is_recovered);
ut_ad(!trx->is_autocommit_non_locking() || trx->read_only);
trx->op_info = "rollback of SQL statement";
err = trx->rollback(&trx->last_sql_stat_start);
if (trx->fts_trx != NULL) {
fts_savepoint_rollback_last_stmt(trx);
fts_savepoint_laststmt_refresh(trx);
}
trx->last_sql_stat_start.least_undo_no = trx->undo_no;
trx->end_bulk_insert();
trx->op_info = "";
return(err);
case TRX_STATE_PREPARED:
case TRX_STATE_PREPARED_RECOVERED:
case TRX_STATE_COMMITTED_IN_MEMORY:
/* The statement rollback is only allowed on an ACTIVE
transaction, not a PREPARED or COMMITTED one. */
break;
}
ut_error;
return(DB_CORRUPTION);
}
/*******************************************************************//**
Search for a savepoint using name.
@return savepoint if found else NULL */
static
trx_named_savept_t*
trx_savepoint_find(
/*===============*/
trx_t* trx, /*!< in: transaction */
const char* name) /*!< in: savepoint name */
{
trx_named_savept_t* savep;
for (savep = UT_LIST_GET_FIRST(trx->trx_savepoints);
savep != NULL;
savep = UT_LIST_GET_NEXT(trx_savepoints, savep)) {
if (!strcmp(savep->name, name)) {
return(savep);
}
}
return(NULL);
}
/*******************************************************************//**
Frees a single savepoint struct. */
static
void
trx_roll_savepoint_free(
/*=====================*/
trx_t* trx, /*!< in: transaction handle */
trx_named_savept_t* savep) /*!< in: savepoint to free */
{
UT_LIST_REMOVE(trx->trx_savepoints, savep);
ut_free(savep->name);
ut_free(savep);
}
/** Discard all savepoints starting from a particular savepoint.
@param savept first savepoint to discard */
void trx_t::savepoints_discard(trx_named_savept_t *savept)
{
while (savept)
{
auto next= UT_LIST_GET_NEXT(trx_savepoints, savept);
trx_roll_savepoint_free(this, savept);
savept= next;
}
}
/*******************************************************************//**
Rolls back a transaction back to a named savepoint. Modifications after the
savepoint are undone but InnoDB does NOT release the corresponding locks
which are stored in memory. If a lock is 'implicit', that is, a new inserted
row holds a lock where the lock information is carried by the trx id stored in
the row, these locks are naturally released in the rollback. Savepoints which
were set after this savepoint are deleted.
@return if no savepoint of the name found then DB_NO_SAVEPOINT,
otherwise DB_SUCCESS */
static MY_ATTRIBUTE((nonnull, warn_unused_result))
dberr_t
trx_rollback_to_savepoint_for_mysql_low(
/*====================================*/
trx_t* trx, /*!< in/out: transaction */
trx_named_savept_t* savep, /*!< in/out: savepoint */
int64_t* mysql_binlog_cache_pos)
/*!< out: the MySQL binlog
cache position corresponding
to this savepoint; MySQL needs
this information to remove the
binlog entries of the queries
executed after the savepoint */
{
dberr_t err;
ut_ad(trx_state_eq(trx, TRX_STATE_ACTIVE));
ut_ad(trx->mysql_thd);
/* Free all savepoints strictly later than savep. */
trx->savepoints_discard(UT_LIST_GET_NEXT(trx_savepoints, savep));
*mysql_binlog_cache_pos = savep->mysql_binlog_cache_pos;
trx->op_info = "rollback to a savepoint";
err = trx->rollback(&savep->savept);
/* Store the current undo_no of the transaction so that
we know where to roll back if we have to roll back the
next SQL statement: */
trx_mark_sql_stat_end(trx);
trx->op_info = "";
return(err);
}
/*******************************************************************//**
Rolls back a transaction back to a named savepoint. Modifications after the
savepoint are undone but InnoDB does NOT release the corresponding locks
which are stored in memory. If a lock is 'implicit', that is, a new inserted
row holds a lock where the lock information is carried by the trx id stored in
the row, these locks are naturally released in the rollback. Savepoints which
were set after this savepoint are deleted.
@return if no savepoint of the name found then DB_NO_SAVEPOINT,
otherwise DB_SUCCESS */
dberr_t
trx_rollback_to_savepoint_for_mysql(
/*================================*/
trx_t* trx, /*!< in: transaction handle */
const char* savepoint_name, /*!< in: savepoint name */
int64_t* mysql_binlog_cache_pos) /*!< out: the MySQL binlog cache
position corresponding to this
savepoint; MySQL needs this
information to remove the
binlog entries of the queries
executed after the savepoint */
{
trx_named_savept_t* savep;
/* We are reading trx->state without holding trx->mutex
here, because the savepoint rollback should be invoked for a
running active MySQL transaction that is associated with the
current thread. */
ut_ad(trx->mysql_thd);
savep = trx_savepoint_find(trx, savepoint_name);
if (savep == NULL) {
return(DB_NO_SAVEPOINT);
}
switch (trx->state) {
case TRX_STATE_NOT_STARTED:
ib::error() << "Transaction has a savepoint "
<< savep->name
<< " though it is not started";
return(DB_ERROR);
case TRX_STATE_ACTIVE:
return(trx_rollback_to_savepoint_for_mysql_low(
trx, savep, mysql_binlog_cache_pos));
case TRX_STATE_PREPARED:
case TRX_STATE_PREPARED_RECOVERED:
case TRX_STATE_COMMITTED_IN_MEMORY:
/* The savepoint rollback is only allowed on an ACTIVE
transaction, not a PREPARED or COMMITTED one. */
break;
}
ut_error;
return(DB_CORRUPTION);
}
/*******************************************************************//**
Creates a named savepoint. If the transaction is not yet started, starts it.
If there is already a savepoint of the same name, this call erases that old
savepoint and replaces it with a new. Savepoints are deleted in a transaction
commit or rollback.
@return always DB_SUCCESS */
dberr_t
trx_savepoint_for_mysql(
/*====================*/
trx_t* trx, /*!< in: transaction handle */
const char* savepoint_name, /*!< in: savepoint name */
int64_t binlog_cache_pos) /*!< in: MySQL binlog cache
position corresponding to this
connection at the time of the
savepoint */
{
trx_named_savept_t* savep;
trx_start_if_not_started_xa(trx, false);
savep = trx_savepoint_find(trx, savepoint_name);
if (savep) {
/* There is a savepoint with the same name: free that */
UT_LIST_REMOVE(trx->trx_savepoints, savep);
ut_free(savep->name);
ut_free(savep);
}
/* Create a new savepoint and add it as the last in the list */
savep = static_cast<trx_named_savept_t*>(
ut_malloc_nokey(sizeof(*savep)));
savep->name = mem_strdup(savepoint_name);
savep->savept.least_undo_no = trx->undo_no;
trx->last_sql_stat_start.least_undo_no = trx->undo_no;
savep->mysql_binlog_cache_pos = binlog_cache_pos;
UT_LIST_ADD_LAST(trx->trx_savepoints, savep);
trx->end_bulk_insert();
return(DB_SUCCESS);
}
/*******************************************************************//**
Releases only the named savepoint. Savepoints which were set after this
savepoint are left as is.
@return if no savepoint of the name found then DB_NO_SAVEPOINT,
otherwise DB_SUCCESS */
dberr_t
trx_release_savepoint_for_mysql(
/*============================*/
trx_t* trx, /*!< in: transaction handle */
const char* savepoint_name) /*!< in: savepoint name */
{
trx_named_savept_t* savep;
ut_ad(trx_state_eq(trx, TRX_STATE_ACTIVE, true)
|| trx_state_eq(trx, TRX_STATE_PREPARED, true));
ut_ad(trx->mysql_thd);
savep = trx_savepoint_find(trx, savepoint_name);
if (savep != NULL) {
trx_roll_savepoint_free(trx, savep);
return DB_SUCCESS;
} else if (trx->last_sql_stat_start.least_undo_no == 0) {
/* Bulk insert could have discarded savepoints */
return DB_SUCCESS;
}
return DB_NO_SAVEPOINT;
}
/*******************************************************************//** /*******************************************************************//**
Roll back an active transaction. */ Roll back an active transaction. */
static static
@ -618,7 +308,9 @@ trx_rollback_active(
if (UNIV_UNLIKELY(!trx->rollback_finish())) { if (UNIV_UNLIKELY(!trx->rollback_finish())) {
ut_ad(!dictionary_locked); ut_ad(!dictionary_locked);
} else { } else {
ib::info() << "Rolled back recovered transaction " << trx_id; sql_print_information(
"InnoDB: Rolled back recovered transaction "
TRX_ID_FMT, trx_id);
} }
if (dictionary_locked) { if (dictionary_locked) {
@ -904,7 +596,6 @@ trx_rollback_step(
if (node->state == ROLL_NODE_SEND) { if (node->state == ROLL_NODE_SEND) {
trx_t* trx; trx_t* trx;
ib_id_t roll_limit;
trx = thr_get_trx(thr); trx = thr_get_trx(thr);
@ -912,13 +603,11 @@ trx_rollback_step(
ut_a(node->undo_thr == NULL); ut_a(node->undo_thr == NULL);
roll_limit = node->savept ? node->savept->least_undo_no : 0;
trx->mutex_lock(); trx->mutex_lock();
trx_commit_or_rollback_prepare(trx); trx_commit_or_rollback_prepare(trx);
node->undo_thr = trx_rollback_start(trx, roll_limit); node->undo_thr = trx_rollback_start(trx, node->savept);
trx->mutex_unlock(); trx->mutex_unlock();
} else { } else {

View file

@ -47,6 +47,31 @@ trx_sys_t trx_sys;
#ifdef UNIV_DEBUG #ifdef UNIV_DEBUG
/* Flag to control TRX_RSEG_N_SLOTS behavior debugging. */ /* Flag to control TRX_RSEG_N_SLOTS behavior debugging. */
uint trx_rseg_n_slots_debug = 0; uint trx_rseg_n_slots_debug = 0;
void rw_trx_hash_t::validate_element(trx_t *trx)
{
ut_ad(!trx->read_only || !trx->rsegs.m_redo.rseg);
ut_ad(!trx->is_autocommit_non_locking());
ut_d(bool acquire_trx_mutex= !trx->mutex_is_owner());
ut_d(if (acquire_trx_mutex) trx->mutex_lock());
switch (trx->state) {
case TRX_STATE_NOT_STARTED:
case TRX_STATE_ABORTED:
ut_error;
case TRX_STATE_PREPARED:
case TRX_STATE_PREPARED_RECOVERED:
case TRX_STATE_COMMITTED_IN_MEMORY:
ut_ad(!trx->is_autocommit_non_locking());
break;
case TRX_STATE_ACTIVE:
if (!trx->is_autocommit_non_locking())
break;
ut_ad(!trx->is_recovered);
ut_ad(trx->read_only);
ut_ad(trx->mysql_thd);
}
ut_d(if (acquire_trx_mutex) trx->mutex_unlock());
}
#endif #endif
/** Display the MySQL binlog offset info if it is present in the trx /** Display the MySQL binlog offset info if it is present in the trx
@ -364,6 +389,7 @@ size_t trx_sys_t::any_active_transactions(size_t *prepared)
trx_sys.trx_list.for_each([&](const trx_t &trx) { trx_sys.trx_list.for_each([&](const trx_t &trx) {
switch (trx.state) { switch (trx.state) {
case TRX_STATE_NOT_STARTED: case TRX_STATE_NOT_STARTED:
case TRX_STATE_ABORTED:
break; break;
case TRX_STATE_ACTIVE: case TRX_STATE_ACTIVE:
if (!trx.id) if (!trx.id)

View file

@ -144,7 +144,7 @@ trx_init(
trx->magic_n = TRX_MAGIC_N; trx->magic_n = TRX_MAGIC_N;
trx->last_sql_stat_start.least_undo_no = 0; trx->last_stmt_start = 0;
ut_ad(!trx->read_view.is_open()); ut_ad(!trx->read_view.is_open());
@ -192,10 +192,6 @@ struct TrxFactory {
UT_LIST_INIT(trx->lock.evicted_tables, UT_LIST_INIT(trx->lock.evicted_tables,
&dict_table_t::table_LRU); &dict_table_t::table_LRU);
UT_LIST_INIT(
trx->trx_savepoints,
&trx_named_savept_t::trx_savepoints);
trx->mutex_init(); trx->mutex_init();
} }
@ -437,9 +433,8 @@ void trx_t::free()
MEM_NOACCESS(&error_info, sizeof error_info); MEM_NOACCESS(&error_info, sizeof error_info);
MEM_NOACCESS(&error_key_num, sizeof error_key_num); MEM_NOACCESS(&error_key_num, sizeof error_key_num);
MEM_NOACCESS(&graph, sizeof graph); MEM_NOACCESS(&graph, sizeof graph);
MEM_NOACCESS(&trx_savepoints, sizeof trx_savepoints);
MEM_NOACCESS(&undo_no, sizeof undo_no); MEM_NOACCESS(&undo_no, sizeof undo_no);
MEM_NOACCESS(&last_sql_stat_start, sizeof last_sql_stat_start); MEM_NOACCESS(&last_stmt_start, sizeof last_stmt_start);
MEM_NOACCESS(&rsegs, sizeof rsegs); MEM_NOACCESS(&rsegs, sizeof rsegs);
MEM_NOACCESS(&roll_limit, sizeof roll_limit); MEM_NOACCESS(&roll_limit, sizeof roll_limit);
MEM_NOACCESS(&in_rollback, sizeof in_rollback); MEM_NOACCESS(&in_rollback, sizeof in_rollback);
@ -467,9 +462,10 @@ void trx_t::free()
/** Transition to committed state, to release implicit locks. */ /** Transition to committed state, to release implicit locks. */
TRANSACTIONAL_INLINE inline void trx_t::commit_state() TRANSACTIONAL_INLINE inline void trx_t::commit_state()
{ {
ut_ad(state == TRX_STATE_PREPARED ut_d(auto trx_state{state});
|| state == TRX_STATE_PREPARED_RECOVERED ut_ad(trx_state == TRX_STATE_PREPARED ||
|| state == TRX_STATE_ACTIVE); trx_state == TRX_STATE_PREPARED_RECOVERED ||
trx_state == TRX_STATE_ACTIVE);
/* This makes the transaction committed in memory and makes its /* This makes the transaction committed in memory and makes its
changes to data visible to other transactions. NOTE that there is a changes to data visible to other transactions. NOTE that there is a
small discrepancy from the strict formal visibility rules here: a small discrepancy from the strict formal visibility rules here: a
@ -1490,8 +1486,6 @@ TRANSACTIONAL_INLINE inline void trx_t::commit_in_memory(const mtr_t *mtr)
} }
} }
savepoints_discard();
if (fts_trx) if (fts_trx)
trx_finalize_for_fts(this, undo_no != 0); trx_finalize_for_fts(this, undo_no != 0);
@ -1510,7 +1504,7 @@ TRANSACTIONAL_INLINE inline void trx_t::commit_in_memory(const mtr_t *mtr)
lock.was_chosen_as_deadlock_victim= false; lock.was_chosen_as_deadlock_victim= false;
} }
void trx_t::commit_cleanup() bool trx_t::commit_cleanup() noexcept
{ {
ut_ad(!dict_operation); ut_ad(!dict_operation);
ut_ad(!was_dict_operation); ut_ad(!was_dict_operation);
@ -1527,6 +1521,7 @@ void trx_t::commit_cleanup()
mutex.wr_unlock(); mutex.wr_unlock();
ut_a(error_state == DB_SUCCESS); ut_a(error_state == DB_SUCCESS);
return false;
} }
/** Commit the transaction in a mini-transaction. /** Commit the transaction in a mini-transaction.
@ -1591,7 +1586,7 @@ TRANSACTIONAL_TARGET void trx_t::commit_low(mtr_t *mtr)
} }
void trx_t::commit_persist() void trx_t::commit_persist() noexcept
{ {
mtr_t *mtr= nullptr; mtr_t *mtr= nullptr;
mtr_t local_mtr; mtr_t local_mtr;
@ -1605,7 +1600,7 @@ void trx_t::commit_persist()
} }
void trx_t::commit() void trx_t::commit() noexcept
{ {
ut_ad(!was_dict_operation); ut_ad(!was_dict_operation);
ut_d(was_dict_operation= dict_operation); ut_d(was_dict_operation= dict_operation);
@ -1641,6 +1636,7 @@ trx_commit_or_rollback_prepare(
return; return;
case TRX_STATE_COMMITTED_IN_MEMORY: case TRX_STATE_COMMITTED_IN_MEMORY:
case TRX_STATE_ABORTED:
break; break;
} }
@ -1708,33 +1704,26 @@ trx_commit_step(
return(thr); return(thr);
} }
/**********************************************************************//** void trx_commit_for_mysql(trx_t *trx) noexcept
Does the transaction commit for MySQL.
@return DB_SUCCESS or error number */
dberr_t
trx_commit_for_mysql(
/*=================*/
trx_t* trx) /*!< in/out: transaction */
{ {
/* Because we do not do the commit by sending an Innobase switch (trx->state) {
sig to the transaction, we must here make sure that trx has been case TRX_STATE_ABORTED:
started. */ trx->state= TRX_STATE_NOT_STARTED;
/* fall through */
switch (trx->state) { case TRX_STATE_NOT_STARTED:
case TRX_STATE_NOT_STARTED: trx->will_lock= false;
return DB_SUCCESS; break;
case TRX_STATE_ACTIVE: case TRX_STATE_ACTIVE:
case TRX_STATE_PREPARED: case TRX_STATE_PREPARED:
case TRX_STATE_PREPARED_RECOVERED: case TRX_STATE_PREPARED_RECOVERED:
trx->op_info = "committing"; trx->op_info= "committing";
trx->commit(); trx->commit();
trx->op_info = ""; trx->op_info= "";
return(DB_SUCCESS); break;
case TRX_STATE_COMMITTED_IN_MEMORY: case TRX_STATE_COMMITTED_IN_MEMORY:
break; ut_error;
} break;
ut_error; }
return(DB_CORRUPTION);
} }
/** Durably write log until trx->commit_lsn /** Durably write log until trx->commit_lsn
@ -1754,42 +1743,6 @@ void trx_commit_complete_for_mysql(trx_t *trx)
trx_flush_log_if_needed(lsn, trx); trx_flush_log_if_needed(lsn, trx);
} }
/**********************************************************************//**
Marks the latest SQL statement ended. */
void
trx_mark_sql_stat_end(
/*==================*/
trx_t* trx) /*!< in: trx handle */
{
ut_a(trx);
switch (trx->state) {
case TRX_STATE_PREPARED:
case TRX_STATE_PREPARED_RECOVERED:
case TRX_STATE_COMMITTED_IN_MEMORY:
break;
case TRX_STATE_NOT_STARTED:
trx->undo_no = 0;
/* fall through */
case TRX_STATE_ACTIVE:
if (trx->fts_trx != NULL) {
fts_savepoint_laststmt_refresh(trx);
}
if (trx->is_bulk_insert()) {
/* Allow a subsequent INSERT into an empty table
if !unique_checks && !foreign_key_checks. */
return;
}
trx->last_sql_stat_start.least_undo_no = trx->undo_no;
trx->end_bulk_insert();
return;
}
ut_error;
}
/**********************************************************************//** /**********************************************************************//**
Prints info about a transaction. */ Prints info about a transaction. */
void void
@ -1815,9 +1768,16 @@ trx_print_low(
fprintf(f, "TRANSACTION (%p)", trx); fprintf(f, "TRANSACTION (%p)", trx);
} }
THD* thd = trx->mysql_thd;
switch (trx->state) { switch (trx->state) {
case TRX_STATE_NOT_STARTED: case TRX_STATE_NOT_STARTED:
fputs(", not started", f); fputs(", not started", f);
thd = nullptr;
goto state_ok;
case TRX_STATE_ABORTED:
fputs(", forced rollback done", f);
thd = nullptr;
goto state_ok; goto state_ok;
case TRX_STATE_ACTIVE: case TRX_STATE_ACTIVE:
fprintf(f, ", ACTIVE %lu sec", fprintf(f, ", ACTIVE %lu sec",
@ -1883,9 +1843,8 @@ state_ok:
putc('\n', f); putc('\n', f);
} }
if (trx->state != TRX_STATE_NOT_STARTED && trx->mysql_thd != NULL) { if (thd) {
innobase_mysql_print_thd( innobase_mysql_print_thd(f, thd, uint(max_query_len));
f, trx->mysql_thd, static_cast<uint>(max_query_len));
} }
} }
@ -2201,6 +2160,7 @@ trx_start_if_not_started_xa_low(
bool read_write) /*!< in: true if read write transaction */ bool read_write) /*!< in: true if read write transaction */
{ {
switch (trx->state) { switch (trx->state) {
case TRX_STATE_ABORTED:
case TRX_STATE_NOT_STARTED: case TRX_STATE_NOT_STARTED:
trx_start_low(trx, read_write); trx_start_low(trx, read_write);
return; return;
@ -2244,6 +2204,7 @@ trx_start_if_not_started_low(
} }
return; return;
case TRX_STATE_ABORTED:
case TRX_STATE_PREPARED: case TRX_STATE_PREPARED:
case TRX_STATE_PREPARED_RECOVERED: case TRX_STATE_PREPARED_RECOVERED:
case TRX_STATE_COMMITTED_IN_MEMORY: case TRX_STATE_COMMITTED_IN_MEMORY:

View file

@ -340,8 +340,6 @@ ut_strerr(
return("Data structure corruption"); return("Data structure corruption");
case DB_CANNOT_DROP_CONSTRAINT: case DB_CANNOT_DROP_CONSTRAINT:
return("Cannot drop constraint"); return("Cannot drop constraint");
case DB_NO_SAVEPOINT:
return("No such savepoint");
case DB_TABLESPACE_EXISTS: case DB_TABLESPACE_EXISTS:
return("Tablespace already exists"); return("Tablespace already exists");
case DB_TABLESPACE_DELETED: case DB_TABLESPACE_DELETED: