Browse Source

MDEV-36787 ROLLBACK TO SAVEPOINT fails with assert invalid state

Reason:
========
  SAVEPOINT A;
  <Bulk DDL FAILS>
  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.
10.11-MDEV-36787
Thirunarayanan Balathandayuthapani 5 months ago
parent
commit
80230b6fc6
  1. 13
      mysql-test/suite/innodb/r/insert_into_empty.result
  2. 13
      mysql-test/suite/innodb/t/insert_into_empty.test
  3. 7
      storage/innobase/handler/ha_innodb.cc
  4. 4
      storage/innobase/include/trx0trx.h
  5. 4
      storage/innobase/row/row0mysql.cc

13
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

13
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

7
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_DDL_BULK>()) {
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 */

4
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();

4
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;
}

Loading…
Cancel
Save