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_DISK_FULL", HA_ERR_DISK_FULL, "" },
{ "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_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_PARTITION_LIST 198
#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 */
#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",
"Error during commit",
"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 */

View file

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

View file

@ -2,6 +2,10 @@
-- source include/have_innodb.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 # Bug #39022: Mysql randomly crashing in lock_sec_rec_cons_read_sees
--echo #

View file

@ -1,6 +1,10 @@
--source include/have_metadata_lock_info.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.
--source include/count_sessions.inc

View file

@ -2,6 +2,10 @@
--source include/have_partition.inc
--source include/have_sequence.inc
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
--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 #

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("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 # 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/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 @save_stats_persistent=@@GLOBAL.innodb_stats_persistent;

View file

@ -2,13 +2,16 @@
# WL#1756
#
-- source include/have_innodb.inc
--source include/not_embedded.inc
# Save the initial number of concurrent sessions
--source include/count_sessions.inc
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
drop table if exists t1, t2;

View file

@ -1,10 +1,12 @@
connection node_2;
connection node_1;
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;
insert into t1 values (NULL,1);
connect node_2a, 127.0.0.1, root, , test, $NODE_MYPORT_2;
connection node_2a;
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
begin;
update t1 set a = 5;
connection node_2;

View file

@ -1,8 +1,10 @@
connection node_2;
connection node_1;
connection node_2;
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
call mtr.add_suppression("WSREP: Trying to continue unpaused monitor");
connection node_1;
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
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;
ALTER TABLE t1 add primary key(a);

View file

@ -7,11 +7,13 @@
#
--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;
insert into t1 values (NULL,1);
--connect node_2a, 127.0.0.1, root, , test, $NODE_MYPORT_2
--connection node_2a
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
begin;
update t1 set a = 5;

View file

@ -3,9 +3,11 @@
--source include/big_test.inc
--connection node_2
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
call mtr.add_suppression("WSREP: Trying to continue unpaused monitor");
--connection node_1
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
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;

View file

@ -456,7 +456,7 @@ restore: t1 .ibd and .cfg files
SET SESSION debug_dbug=@saved_debug_dbug;
SET SESSION debug_dbug="+d,ib_import_open_tablespace_failure";
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;
restore: t1 .ibd and .cfg files
SET SESSION debug_dbug="+d,ib_import_check_bitmap_failure";

View file

@ -3,6 +3,10 @@
--source include/have_debug_sync.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.
CREATE TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY)ENGINE=INNODB;

View file

@ -5,6 +5,10 @@
--source include/have_innodb.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`;
connection default;

View file

@ -1,6 +1,10 @@
--source include/have_innodb.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 (
pkey int NOT NULL PRIMARY KEY,
c int

View file

@ -2,6 +2,10 @@
--source include/have_debug_sync.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,,)
# 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

View file

@ -2,6 +2,10 @@
--source include/have_debug_sync.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
# 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

View file

@ -2,6 +2,10 @@
--source include/have_debug_sync.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
# 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

View file

@ -2,6 +2,10 @@
--source include/count_sessions.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;
--echo #

View file

@ -1,6 +1,10 @@
--source include/have_innodb.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
#

View file

@ -4,6 +4,10 @@
# 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
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
--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
# Diagnostics_area::sql_errno

View file

@ -6,6 +6,10 @@
# 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
SET default_storage_engine=InnoDB;

View file

@ -3,6 +3,10 @@
--source include/have_debug_sync.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)
START TRANSACTION WITH CONSISTENT SNAPSHOT;

View file

@ -3,6 +3,10 @@
--source include/have_debug.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;
INSERT INTO t VALUES (3);

View file

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

View file

@ -6,6 +6,9 @@
--echo #
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;
INSERT INTO t1 VALUES (1),(2),(3),(4),(5);

View file

@ -4,6 +4,10 @@
--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,

View file

@ -4,6 +4,10 @@
# 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
CREATE TABLE t1 (a INT, b INT) ENGINE=InnoDB;

View file

@ -1,5 +1,9 @@
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
#

View file

@ -249,9 +249,33 @@ id title
7 mysql
TRUNCATE TABLE articles;
INSERT INTO articles(id, title) VALUES(1, 'mysql');
CREATE TABLE t(a INT PRIMARY KEY) ENGINE=InnoDB;
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');
ROLLBACK;
SELECT * FROM t;
a
1
DROP TABLE t;
INSERT INTO articles(id, title) VALUES(3, 'mysql');
SELECT * FROM articles WHERE MATCH(title) AGAINST('mysql');
id title

View file

@ -366,12 +366,28 @@ TRUNCATE TABLE articles;
INSERT INTO articles(id, title) VALUES(1, 'mysql');
CREATE TABLE t(a INT PRIMARY KEY) ENGINE=InnoDB;
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');
ROLLBACK;
SELECT * FROM t;
DROP TABLE t;
INSERT INTO articles(id, title) VALUES(3, '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="+d,ib_import_open_tablespace_failure";
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;
restore: t1 .ibd and .cfg files
SET SESSION debug_dbug="+d,ib_import_check_bitmap_failure";

View file

@ -2,6 +2,9 @@
--source include/have_innodb.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 ***

View file

@ -36,9 +36,9 @@ Table Op Msg_type Msg_text
test.t1 analyze status Engine-independent statistics collected
test.t1 analyze status OK
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-10 ddl/ in mysqlbinlog.out
FOUND 1 /GTID 0-1-11 ddl/ in mysqlbinlog.out
#
# Clean up
#
@ -63,9 +63,9 @@ ALTER TABLE t1 REPAIR PARTITION p0;
Table Op Msg_type Msg_text
test.t1 repair status OK
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-16 ddl/ in mysqlbinlog.out
FOUND 1 /GTID 0-1-17 ddl/ in mysqlbinlog.out
#
# Clean up
#

View file

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

View file

@ -2,6 +2,9 @@
--source include/have_debug.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
# 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_binlog_format_statement.inc
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
--connection master
ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
CREATE TABLE t1(a INT) ENGINE=INNODB;

View file

@ -5,6 +5,10 @@
--source include/have_debug_sync.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
SET sql_log_bin=0;
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("Can't find record in 't1'");
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
ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;

View file

@ -3,6 +3,10 @@
--let $rpl_topology=1->2
--source include/rpl_init.inc
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
--connection server_1
ALTER TABLE mysql.gtid_slave_pos 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/master-slave.inc
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
--let $xid_num = 19
--let $repeat = 17
--let $workers = 7

View file

@ -2,6 +2,9 @@
--source include/master-slave.inc
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. ***
--connection master

View file

@ -1,6 +1,10 @@
source suite/versioning/engines.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;
eval create table t1(
x int unsigned,

View file

@ -4676,6 +4676,12 @@ void handler::print_error(int error, myf errflag)
case HA_ERR_PARTITION_LIST:
my_error(ER_VERS_NOT_ALLOWED, errflag, table->s->db.str, table->s->table_name.str);
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:
{
/* 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 */
errflag&= ~(ME_WARNING | ME_NOTE);
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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

@ -465,8 +465,13 @@ protected:
@see build_template() */
void reset_template();
/** @return whether the table is read-only */
bool is_read_only(bool altering_to_supported= false) const;
/** Check the transaction is valid.
@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);
void update_thd();
@ -937,12 +942,3 @@ ib_push_frm_error(
@return true if index column length exceeds limit */
MY_ATTRIBUTE((warn_unused_result))
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);
}
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->create_info->key_block_size == 0
&& 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,
(almost) nothing has been or needs to be done. */
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;
else if (ctx->new_table)
{
@ -11382,7 +11382,7 @@ lock_fail:
to remove the newly created table or
index from data dictionary and table cache
in rollback_inplace_alter_table() */
if (trx->state == TRX_STATE_NOT_STARTED) {
if (!trx->is_started()) {
trx_start_for_ddl(trx);
}
@ -11549,7 +11549,7 @@ err_index:
purge_sys.resume_FTS();
}
if (trx->state == TRX_STATE_NOT_STARTED) {
if (!trx->is_started()) {
/* Transaction may have been rolled back
due to a lock wait timeout, deadlock,
or a KILL statement. So restart the

View file

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

View file

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

View file

@ -172,7 +172,7 @@ row_mysql_handle_errors(
during the function entry */
trx_t* trx, /*!< in: transaction */
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)));
/********************************************************************//**
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 */
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 */
enum roll_node_state {
@ -144,25 +88,9 @@ enum roll_node_state {
struct roll_node_t{
que_common_t common; /*!< node type: QUE_NODE_ROLLBACK */
enum roll_node_state state; /*!< node execution state */
const trx_savept_t* savept; /*!< savepoint to which to
roll back, in the case of a
partial rollback */
undo_no_t savept; /*!< savepoint to which to
roll back; 0=entire transaction */
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

View file

@ -495,19 +495,7 @@ class rw_trx_hash_t
#ifdef UNIV_DEBUG
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());
}
static void validate_element(trx_t *trx);
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); \
(t)->start_line = __LINE__; \
(t)->start_file = __FILE__; \
t->state= TRX_STATE_NOT_STARTED; \
trx_start_for_ddl_low(t); \
} while (0)
#else
# define trx_start_for_ddl(t) trx_start_for_ddl_low(t)
#endif /* UNIV_DEBUG */
/**********************************************************************//**
Does the transaction commit for MySQL.
@return DB_SUCCESS or error number */
dberr_t
trx_commit_for_mysql(
/*=================*/
trx_t* trx); /*!< in/out: transaction */
/** Commit a transaction */
void trx_commit_for_mysql(trx_t *trx) noexcept;
/** XA PREPARE a transaction.
@param[in,out] trx transaction to prepare */
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
(if trx_t::commit_in_memory() was invoked with flush_log_later=true). */
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. */
void
@ -663,6 +653,7 @@ public:
Possible states:
TRX_STATE_NOT_STARTED
TRX_STATE_ABORTED
TRX_STATE_ACTIVE
TRX_STATE_PREPARED
TRX_STATE_PREPARED_RECOVERED (special case of TRX_STATE_PREPARED)
@ -672,6 +663,8 @@ public:
Regular transactions:
* 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:
* 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
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
in rw_trx_hash.
When a transaction is NOT_STARTED or ABORTED, it can be in trx_list.
It cannot be in rw_trx_hash.
ACTIVE->PREPARED->COMMITTED is only possible when trx is in rw_trx_hash.
The transition ACTIVE->PREPARED is protected by trx->mutex.
ACTIVE->PREPARED->COMMITTED and ACTIVE->COMMITTED is only possible when
trx is in rw_trx_hash. These transitions are protected by trx_t::mutex.
ACTIVE->COMMITTED is possible when the transaction is in
rw_trx_hash.
COMMITTED->NOT_STARTED is possible when trx_t::mutex is being held.
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;
/** The locks of the transaction. Protected by lock_sys.latch
@ -733,6 +728,14 @@ public:
bool is_wsrep() const { return false; }
#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 */
ReadView read_view;
@ -839,10 +842,6 @@ public:
it is a stored procedure with a COMMIT
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
assign; since the undo log is
private for a transaction, this
@ -850,7 +849,7 @@ public:
with no gaps; thus it represents
the number of modified/inserted
rows in a transaction */
trx_savept_t last_sql_stat_start;
undo_no_t last_stmt_start;
/*!< undo_no when the last sql statement
was started: in case of an error, trx
is rolled back down to this number */
@ -949,16 +948,17 @@ public:
void evict_table(table_id_t table_id, bool reset_only= false);
/** Initiate rollback.
@param savept savepoint to which to roll back
@param savept pointer to savepoint; nullptr=entire transaction
@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.
@param savept savepoint to which to roll back */
inline void rollback_low(trx_savept_t *savept= nullptr);
@param savept pointer to savepoint; nullptr=entire transaction
@return error code or DB_SUCCESS */
dberr_t rollback_low(const undo_no_t *savept= nullptr) noexcept;
/** Finish rollback.
@return whether the rollback was completed normally
@retval false if the rollback was aborted by shutdown */
inline bool rollback_finish();
bool rollback_finish() noexcept;
private:
/** Apply any changes to tables for which online DDL is in progress. */
ATTRIBUTE_COLD void apply_log();
@ -968,9 +968,10 @@ private:
@param mtr mini-transaction (if there are any persistent modifications) */
inline void commit_in_memory(const mtr_t *mtr);
/** Write log for committing the transaction. */
void commit_persist();
/** Clean up the transaction after commit_in_memory() */
void commit_cleanup();
void commit_persist() noexcept;
/** Clean up the transaction after commit_in_memory()
@return false (always) */
bool commit_cleanup() noexcept;
/** Commit the transaction in a mini-transaction.
@param mtr mini-transaction (if there are any persistent modifications) */
void commit_low(mtr_t *mtr= nullptr);
@ -985,7 +986,7 @@ private:
inline void write_serialisation_history(mtr_t *mtr);
public:
/** Commit the transaction. */
void commit();
void commit() noexcept;
/** Try to drop a persistent table.
@param table persistent table
@ -1005,16 +1006,6 @@ public:
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
{
return (skip_lock_inheritance_and_n_ref & ~(1U << 31)) > 0;
@ -1128,15 +1119,6 @@ private:
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) */
#define TRX_ISO_READ_UNCOMMITTED 0 /* dirty read: non-locking
SELECTs are performed so that

View file

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

View file

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

View file

@ -6692,23 +6692,19 @@ lock_unlock_table_autoinc(
/*======================*/
trx_t* trx) /*!< in/out: transaction */
{
lock_sys.assert_unlocked();
ut_ad(!trx->mutex_is_owner());
ut_ad(!trx->lock.wait_lock);
/* 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. */
/* This can be invoked on NOT_STARTED, ACTIVE, PREPARED,
but not COMMITTED transactions. */
lock_sys.assert_unlocked();
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)
|| !trx_state_eq(trx, TRX_STATE_COMMITTED_IN_MEMORY));
/* 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);
}
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

View file

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

View file

@ -625,7 +625,7 @@ row_mysql_handle_errors(
function */
trx_t* trx, /*!< in: transaction */
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;
@ -681,8 +681,7 @@ handle_new_error:
}
/* MariaDB will roll back the entire transaction. */
trx->bulk_insert = false;
trx->last_sql_stat_start.least_undo_no = 0;
trx->savepoints_discard();
trx->last_stmt_start = 0;
break;
case DB_LOCK_WAIT:
err = lock_wait(thr);
@ -700,7 +699,6 @@ handle_new_error:
rollback:
/* Roll back the whole transaction; this resolution was added
to version 3.23.43 */
trx->rollback();
break;
@ -1137,7 +1135,7 @@ row_lock_table_autoinc_for_mysql(
trx->error_state = err;
} while (err != DB_SUCCESS
&& row_mysql_handle_errors(&err, trx, thr, NULL));
&& row_mysql_handle_errors(&err, trx, thr, nullptr));
trx->op_info = "";
@ -1179,7 +1177,7 @@ row_lock_table(row_prebuilt_t* prebuilt)
prebuilt->select_lock_type), thr);
trx->error_state = err;
} while (err != DB_SUCCESS
&& row_mysql_handle_errors(&err, trx, thr, NULL));
&& row_mysql_handle_errors(&err, trx, thr, nullptr));
trx->op_info = "";
@ -1218,7 +1216,6 @@ row_insert_for_mysql(
row_prebuilt_t* prebuilt,
ins_mode_t ins_mode)
{
trx_savept_t savept;
que_thr_t* thr;
dberr_t err;
ibool was_lock_wait;
@ -1272,7 +1269,7 @@ row_insert_for_mysql(
roll back to the start of the transaction. For correctness, it
would suffice to roll back to the start of the first insert
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);
@ -1586,7 +1583,6 @@ init_fts_doc_id_for_ref(
dberr_t
row_update_for_mysql(row_prebuilt_t* prebuilt)
{
trx_savept_t savept;
dberr_t err;
que_thr_t* thr;
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
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);

View file

@ -1208,8 +1208,7 @@ re_scan:
mtr->commit();
trx->error_state = err;
thr->lock_state = QUE_THR_LOCK_ROW;
if (row_mysql_handle_errors(
&err, trx, thr, NULL)) {
if (row_mysql_handle_errors(&err, trx, thr, 0)) {
thr->lock_state = QUE_THR_LOCK_NOLOCK;
mtr->start();
@ -5873,7 +5872,7 @@ lock_table_wait:
trx->error_state = err;
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 */
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.mutex_lock();
if (trx.state != TRX_STATE_NOT_STARTED)
if (trx.is_started())
fetch_data_into_cache_low(cache, &trx);
trx.mutex_unlock();
}

View file

@ -43,6 +43,7 @@ Created 3/26/1996 Heikki Tuuri
#include "trx0sys.h"
#include "trx0trx.h"
#include "trx0undo.h"
#include "log.h"
#ifdef UNIV_PFS_THREAD
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 */
const trx_t* trx_roll_crash_recv_trx;
/** Finish transaction rollback.
@return whether the rollback was completed normally
@retval false if the rollback was aborted by shutdown */
inline bool trx_t::rollback_finish()
bool trx_t::rollback_finish() noexcept
{
mod_tables.clear();
apply_online_log= false;
@ -85,34 +83,28 @@ inline bool trx_t::rollback_finish()
undo= nullptr;
}
commit_low();
commit_cleanup();
return false;
return commit_cleanup();
}
/** Roll back an active transaction. */
inline void trx_t::rollback_low(trx_savept_t *savept)
dberr_t trx_t::rollback_low(const undo_no_t *savept) noexcept
{
op_info= "rollback";
mem_heap_t *heap= mem_heap_create(512);
roll_node_t *roll_node= roll_node_create(heap);
roll_node->savept= savept;
roll_node->savept= savept ? *savept : 0;
ut_ad(!in_rollback);
#ifdef UNIV_DEBUG
if (savept)
{
const auto s= state;
ut_ad(s == TRX_STATE_ACTIVE ||
s == TRX_STATE_PREPARED ||
s == TRX_STATE_PREPARED_RECOVERED);
if (savept)
{
ut_ad(s == TRX_STATE_ACTIVE);
ut_ad(mysql_thd);
ut_ad(!is_recovered);
}
ut_ad(state == TRX_STATE_ACTIVE);
ut_ad(mysql_thd);
ut_ad(!is_recovered);
}
#endif
error_state = DB_SUCCESS;
error_state= DB_SUCCESS;
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. */
ut_ad(!(lock.was_chosen_as_deadlock_victim & 1));
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;
for (trx_mod_tables_t::iterator i= mod_tables.begin();
i != mod_tables.end(); )
@ -155,49 +147,34 @@ inline void trx_t::rollback_low(trx_savept_t *savept)
}
mem_heap_free(heap);
op_info= "";
return error_state;
}
/** Initiate rollback.
@param savept savepoint
@return error code or DB_SUCCESS */
dberr_t trx_t::rollback(trx_savept_t *savept)
dberr_t trx_t::rollback(const undo_no_t *savept) noexcept
{
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;
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
if (!savept && is_wsrep() && wsrep_thd_is_SR(mysql_thd))
wsrep_handle_SR_rollback(nullptr, mysql_thd);
#endif /* WITH_WSREP */
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);
return rollback_low(savept);
}
/** 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. */
switch (trx->state) {
case TRX_STATE_ABORTED:
trx->state = TRX_STATE_NOT_STARTED;
/* fall through */
case TRX_STATE_NOT_STARTED:
trx->will_lock = false;
ut_ad(trx->mysql_thd);
@ -222,13 +202,13 @@ dberr_t trx_rollback_for_mysql(trx_t* trx)
ut_ad(!trx->is_wsrep());
trx->lock.was_chosen_as_deadlock_victim= false;
#endif
ut_a(UT_LIST_GET_LEN(trx->lock.trx_locks) == 0);
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);
return(trx_rollback_for_mysql_low(trx));
return trx->rollback_low();
case TRX_STATE_PREPARED:
case TRX_STATE_PREPARED_RECOVERED:
@ -266,7 +246,7 @@ dberr_t trx_rollback_for_mysql(trx_t* trx)
mtr.commit();
ut_ad(mtr.commit_lsn() > 0);
}
return(trx_rollback_for_mysql_low(trx));
return trx->rollback_low();
case TRX_STATE_COMMITTED_IN_MEMORY:
ut_ad(!trx->is_autocommit_non_locking());
@ -277,296 +257,6 @@ dberr_t trx_rollback_for_mysql(trx_t* trx)
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. */
static
@ -618,7 +308,9 @@ trx_rollback_active(
if (UNIV_UNLIKELY(!trx->rollback_finish())) {
ut_ad(!dictionary_locked);
} 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) {
@ -904,7 +596,6 @@ trx_rollback_step(
if (node->state == ROLL_NODE_SEND) {
trx_t* trx;
ib_id_t roll_limit;
trx = thr_get_trx(thr);
@ -912,13 +603,11 @@ trx_rollback_step(
ut_a(node->undo_thr == NULL);
roll_limit = node->savept ? node->savept->least_undo_no : 0;
trx->mutex_lock();
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();
} else {

View file

@ -47,6 +47,31 @@ trx_sys_t trx_sys;
#ifdef UNIV_DEBUG
/* Flag to control TRX_RSEG_N_SLOTS behavior debugging. */
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
/** 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) {
switch (trx.state) {
case TRX_STATE_NOT_STARTED:
case TRX_STATE_ABORTED:
break;
case TRX_STATE_ACTIVE:
if (!trx.id)

View file

@ -144,7 +144,7 @@ trx_init(
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());
@ -192,10 +192,6 @@ struct TrxFactory {
UT_LIST_INIT(trx->lock.evicted_tables,
&dict_table_t::table_LRU);
UT_LIST_INIT(
trx->trx_savepoints,
&trx_named_savept_t::trx_savepoints);
trx->mutex_init();
}
@ -437,9 +433,8 @@ void trx_t::free()
MEM_NOACCESS(&error_info, sizeof error_info);
MEM_NOACCESS(&error_key_num, sizeof error_key_num);
MEM_NOACCESS(&graph, sizeof graph);
MEM_NOACCESS(&trx_savepoints, sizeof trx_savepoints);
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(&roll_limit, sizeof roll_limit);
MEM_NOACCESS(&in_rollback, sizeof in_rollback);
@ -467,9 +462,10 @@ void trx_t::free()
/** Transition to committed state, to release implicit locks. */
TRANSACTIONAL_INLINE inline void trx_t::commit_state()
{
ut_ad(state == TRX_STATE_PREPARED
|| state == TRX_STATE_PREPARED_RECOVERED
|| state == TRX_STATE_ACTIVE);
ut_d(auto trx_state{state});
ut_ad(trx_state == TRX_STATE_PREPARED ||
trx_state == TRX_STATE_PREPARED_RECOVERED ||
trx_state == TRX_STATE_ACTIVE);
/* This makes the transaction committed in memory and makes its
changes to data visible to other transactions. NOTE that there is 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)
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;
}
void trx_t::commit_cleanup()
bool trx_t::commit_cleanup() noexcept
{
ut_ad(!dict_operation);
ut_ad(!was_dict_operation);
@ -1527,6 +1521,7 @@ void trx_t::commit_cleanup()
mutex.wr_unlock();
ut_a(error_state == DB_SUCCESS);
return false;
}
/** 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 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_d(was_dict_operation= dict_operation);
@ -1641,6 +1636,7 @@ trx_commit_or_rollback_prepare(
return;
case TRX_STATE_COMMITTED_IN_MEMORY:
case TRX_STATE_ABORTED:
break;
}
@ -1708,33 +1704,26 @@ trx_commit_step(
return(thr);
}
/**********************************************************************//**
Does the transaction commit for MySQL.
@return DB_SUCCESS or error number */
dberr_t
trx_commit_for_mysql(
/*=================*/
trx_t* trx) /*!< in/out: transaction */
void trx_commit_for_mysql(trx_t *trx) noexcept
{
/* Because we do not do the commit by sending an Innobase
sig to the transaction, we must here make sure that trx has been
started. */
switch (trx->state) {
case TRX_STATE_NOT_STARTED:
return DB_SUCCESS;
case TRX_STATE_ACTIVE:
case TRX_STATE_PREPARED:
case TRX_STATE_PREPARED_RECOVERED:
trx->op_info = "committing";
trx->commit();
trx->op_info = "";
return(DB_SUCCESS);
case TRX_STATE_COMMITTED_IN_MEMORY:
break;
}
ut_error;
return(DB_CORRUPTION);
switch (trx->state) {
case TRX_STATE_ABORTED:
trx->state= TRX_STATE_NOT_STARTED;
/* fall through */
case TRX_STATE_NOT_STARTED:
trx->will_lock= false;
break;
case TRX_STATE_ACTIVE:
case TRX_STATE_PREPARED:
case TRX_STATE_PREPARED_RECOVERED:
trx->op_info= "committing";
trx->commit();
trx->op_info= "";
break;
case TRX_STATE_COMMITTED_IN_MEMORY:
ut_error;
break;
}
}
/** 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);
}
/**********************************************************************//**
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. */
void
@ -1815,9 +1768,16 @@ trx_print_low(
fprintf(f, "TRANSACTION (%p)", trx);
}
THD* thd = trx->mysql_thd;
switch (trx->state) {
case TRX_STATE_NOT_STARTED:
fputs(", not started", f);
thd = nullptr;
goto state_ok;
case TRX_STATE_ABORTED:
fputs(", forced rollback done", f);
thd = nullptr;
goto state_ok;
case TRX_STATE_ACTIVE:
fprintf(f, ", ACTIVE %lu sec",
@ -1883,9 +1843,8 @@ state_ok:
putc('\n', f);
}
if (trx->state != TRX_STATE_NOT_STARTED && trx->mysql_thd != NULL) {
innobase_mysql_print_thd(
f, trx->mysql_thd, static_cast<uint>(max_query_len));
if (thd) {
innobase_mysql_print_thd(f, thd, uint(max_query_len));
}
}
@ -2201,6 +2160,7 @@ trx_start_if_not_started_xa_low(
bool read_write) /*!< in: true if read write transaction */
{
switch (trx->state) {
case TRX_STATE_ABORTED:
case TRX_STATE_NOT_STARTED:
trx_start_low(trx, read_write);
return;
@ -2244,6 +2204,7 @@ trx_start_if_not_started_low(
}
return;
case TRX_STATE_ABORTED:
case TRX_STATE_PREPARED:
case TRX_STATE_PREPARED_RECOVERED:
case TRX_STATE_COMMITTED_IN_MEMORY:

View file

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