From 80230b6fc69b2487956f51532d2be6ed1a1e9990 Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Mon, 26 May 2025 20:20:14 +0530 Subject: [PATCH] MDEV-36787 ROLLBACK TO SAVEPOINT fails with assert invalid state Reason: ======== SAVEPOINT A; ROLLBACK TO SAVEPOINT A; When DDL bulk operation fails, InnoDB does rollback the whole transaction and it makes the transaction state to TRX_STATE_NOT_STARTED. In innobase_rollback_to_savepoint(), InnoDB fails with invalid transaction state. Fix: === - InnoDB should rollback the bulk insert operation alone instead of complete transaction rollback when InnoDB bulk DDL operation fails. This can be achieved by bulk_rollback_low(). row_mysql_handle_errors(): Avoid complete rollback of transaction when DB_TEMP_FILE_WRITE_FAIL error happens. convert_error_code_to_mysql(): Since InnoDB does partial rollback, remove innodb_transaction_abort() call for DB_TEMP_FILE_WRITE_FAIL error. --- mysql-test/suite/innodb/r/insert_into_empty.result | 13 +++++++++++++ mysql-test/suite/innodb/t/insert_into_empty.test | 13 +++++++++++++ storage/innobase/handler/ha_innodb.cc | 7 ++----- storage/innobase/include/trx0trx.h | 4 ++-- storage/innobase/row/row0mysql.cc | 4 +++- 5 files changed, 33 insertions(+), 8 deletions(-) diff --git a/mysql-test/suite/innodb/r/insert_into_empty.result b/mysql-test/suite/innodb/r/insert_into_empty.result index 19e5267a1b7..5785258b34d 100644 --- a/mysql-test/suite/innodb/r/insert_into_empty.result +++ b/mysql-test/suite/innodb/r/insert_into_empty.result @@ -596,4 +596,17 @@ SELECT * FROM t1; f1 1 DROP TABLE t1; +# +# MDEV-36787 Error 153: No savepoint with that name +# upon ROLLBACK TO SAVEPOINT, assertion failure +# +CREATE TABLE t1(a INT) ENGINE=InnoDB; +START TRANSACTION; +SELECT * FROM t1; +a +SAVEPOINT A; +CREATE TEMPORARY TABLE tmp (a TINYINT) ENGINE=InnoDB AS SELECT 256 AS a; +ERROR 22003: Out of range value for column 'a' at row 1 +ROLLBACK TO SAVEPOINT A; +DROP TABLE t1; # End of 10.11 tests diff --git a/mysql-test/suite/innodb/t/insert_into_empty.test b/mysql-test/suite/innodb/t/insert_into_empty.test index e31b680ebe8..3aedc65e070 100644 --- a/mysql-test/suite/innodb/t/insert_into_empty.test +++ b/mysql-test/suite/innodb/t/insert_into_empty.test @@ -653,4 +653,17 @@ XA PREPARE 'a'; XA COMMIT 'a'; SELECT * FROM t1; DROP TABLE t1; + +--echo # +--echo # MDEV-36787 Error 153: No savepoint with that name +--echo # upon ROLLBACK TO SAVEPOINT, assertion failure +--echo # +CREATE TABLE t1(a INT) ENGINE=InnoDB; +START TRANSACTION; +SELECT * FROM t1; +SAVEPOINT A; +--error ER_WARN_DATA_OUT_OF_RANGE +CREATE TEMPORARY TABLE tmp (a TINYINT) ENGINE=InnoDB AS SELECT 256 AS a; +ROLLBACK TO SAVEPOINT A; +DROP TABLE t1; --echo # End of 10.11 tests diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 29757c28e99..12325465c9d 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -2163,9 +2163,6 @@ convert_error_code_to_mysql( case DB_TEMP_FILE_WRITE_FAIL: /* This error can happen during copy_data_between_tables() or bulk insert operation */ - innodb_transaction_abort(thd, - innobase_rollback_on_timeout, - error); my_error(ER_GET_ERRMSG, MYF(0), DB_TEMP_FILE_WRITE_FAIL, ut_strerr(DB_TEMP_FILE_WRITE_FAIL), @@ -15937,7 +15934,7 @@ ha_innobase::extra( } m_prebuilt->table->skip_alter_undo = 0; if (dberr_t err= trx->bulk_insert_apply()) { - trx->rollback(); + trx->bulk_rollback_low(); return convert_error_code_to_mysql( err, m_prebuilt->table->flags, trx->mysql_thd); @@ -15967,7 +15964,7 @@ ha_innobase::extra( if (m_prebuilt->table->skip_alter_undo) { trx = check_trx_exists(ha_thd()); m_prebuilt->table->skip_alter_undo = 0; - trx->rollback(); + trx->bulk_rollback_low(); } break; default:/* Do nothing */ diff --git a/storage/innobase/include/trx0trx.h b/storage/innobase/include/trx0trx.h index 946785cc0b0..b480d56e022 100644 --- a/storage/innobase/include/trx0trx.h +++ b/storage/innobase/include/trx0trx.h @@ -1202,12 +1202,12 @@ public: return bulk_insert == type ? bulk_insert_apply_low(): DB_SUCCESS; } + /** Rollback the bulk insert operation for the transaction */ + void bulk_rollback_low(); private: /** Apply the buffered bulk inserts. */ dberr_t bulk_insert_apply_low(); - /** Rollback the bulk insert operation for the transaction */ - void bulk_rollback_low(); /** Assign a rollback segment for modifying temporary tables. @return the assigned rollback segment */ trx_rseg_t *assign_temp_rseg(); diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc index 7992866df67..445ddf23068 100644 --- a/storage/innobase/row/row0mysql.cc +++ b/storage/innobase/row/row0mysql.cc @@ -708,7 +708,6 @@ handle_new_error: case DB_DEADLOCK: case DB_RECORD_CHANGED: case DB_LOCK_TABLE_FULL: - case DB_TEMP_FILE_WRITE_FAIL: rollback: /* Roll back the whole transaction; this resolution was added to version 3.23.43 */ @@ -739,6 +738,9 @@ handle_new_error: " table. Please drop excessive" " foreign constraints and try again"; goto rollback_to_savept; + case DB_TEMP_FILE_WRITE_FAIL: + trx->bulk_rollback_low(); + break; default: ib::fatal() << "Unknown error " << err; }