summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarko Mäkelä <marko.makela@mariadb.com>2021-04-09 09:18:07 +0300
committerMarko Mäkelä <marko.makela@mariadb.com>2021-04-09 09:18:07 +0300
commitde119fa2b65aa8f9300c8a01a9f4e1328881ec0e (patch)
tree81c9fc8e489ce2566375531bef62caf7e463a047
parent7588049374c63b169ddd245c2dd5f3faac129b4e (diff)
downloadmariadb-git-de119fa2b65aa8f9300c8a01a9f4e1328881ec0e.tar.gz
MDEV-25297 Assertion: trx->roll_limit <= trx->undo_no in ROLLBACK TO SAVEPOINT
In commit 8ea923f55b7666a359ac2c54f6c10e8609d16846 (MDEV-24818) when we optimized multi-statement INSERT transactions into empty tables, we would roll back the entire transaction on any error. But, we would fail to invalidate any SAVEPOINT that had been requested in the past. trx_t::savepoints_discard(): Renamed from trx_roll_savepoints_free(). row_mysql_handle_errors(): If we were in bulk insert, invoke trx_t::savepoints_discard(). In this way, a future attempt of ROLLBACK TO SAVEPOINT will return an error.
-rw-r--r--mysql-test/suite/innodb/r/insert_into_empty.result20
-rw-r--r--mysql-test/suite/innodb/t/insert_into_empty.test20
-rw-r--r--storage/innobase/include/trx0roll.h10
-rw-r--r--storage/innobase/include/trx0trx.h13
-rw-r--r--storage/innobase/row/row0mysql.cc1
-rw-r--r--storage/innobase/trx/trx0roll.cc29
-rw-r--r--storage/innobase/trx/trx0trx.cc5
7 files changed, 65 insertions, 33 deletions
diff --git a/mysql-test/suite/innodb/r/insert_into_empty.result b/mysql-test/suite/innodb/r/insert_into_empty.result
index cee08019424..e00a099b7b6 100644
--- a/mysql-test/suite/innodb/r/insert_into_empty.result
+++ b/mysql-test/suite/innodb/r/insert_into_empty.result
@@ -117,3 +117,23 @@ ERROR 23000: Duplicate entry '2' for key 'PRIMARY'
SHOW ENGINE InnoDB STATUS;
COMMIT;
DROP TABLE t1,t2;
+#
+# MDEV-25297 Assertion: trx->roll_limit <= trx->undo_no
+# in ROLLBACK TO SAVEPOINT
+#
+CREATE TABLE t1 (c INT PRIMARY KEY) ENGINE=InnoDB;
+CREATE TABLE t2 (c INT PRIMARY KEY) ENGINE=InnoDB;
+BEGIN;
+INSERT INTO t1 VALUES(0);
+SAVEPOINT x;
+INSERT INTO t2 VALUES(0);
+INSERT INTO t1 VALUES(0);
+ERROR 23000: Duplicate entry '0' for key 'PRIMARY'
+ROLLBACK TO SAVEPOINT x;
+ERROR HY000: Got error 153 "No savepoint with that name" during ROLLBACK
+COMMIT;
+SELECT * FROM t1;
+c
+SELECT * FROM t2;
+c
+DROP TABLE t1,t2;
diff --git a/mysql-test/suite/innodb/t/insert_into_empty.test b/mysql-test/suite/innodb/t/insert_into_empty.test
index 81c4805c13c..17579ffe9ef 100644
--- a/mysql-test/suite/innodb/t/insert_into_empty.test
+++ b/mysql-test/suite/innodb/t/insert_into_empty.test
@@ -119,3 +119,23 @@ SHOW ENGINE InnoDB STATUS;
--enable_result_log
COMMIT;
DROP TABLE t1,t2;
+
+--echo #
+--echo # MDEV-25297 Assertion: trx->roll_limit <= trx->undo_no
+--echo # in ROLLBACK TO SAVEPOINT
+--echo #
+
+CREATE TABLE t1 (c INT PRIMARY KEY) ENGINE=InnoDB;
+CREATE TABLE t2 (c INT PRIMARY KEY) ENGINE=InnoDB;
+BEGIN;
+INSERT INTO t1 VALUES(0);
+SAVEPOINT x;
+INSERT INTO t2 VALUES(0);
+--error ER_DUP_ENTRY
+INSERT INTO t1 VALUES(0);
+--error ER_ERROR_DURING_ROLLBACK
+ROLLBACK TO SAVEPOINT x;
+COMMIT;
+SELECT * FROM t1;
+SELECT * FROM t2;
+DROP TABLE t1,t2;
diff --git a/storage/innobase/include/trx0roll.h b/storage/innobase/include/trx0roll.h
index d39e5bfb2f5..9ef9ebe93b2 100644
--- a/storage/innobase/include/trx0roll.h
+++ b/storage/innobase/include/trx0roll.h
@@ -130,15 +130,7 @@ trx_release_savepoint_for_mysql(
trx_t* trx, /*!< in: transaction handle */
const char* savepoint_name) /*!< in: savepoint name */
MY_ATTRIBUTE((nonnull, warn_unused_result));
-/*******************************************************************//**
-Frees savepoint structs starting from savep. */
-void
-trx_roll_savepoints_free(
-/*=====================*/
- trx_t* trx, /*!< in: transaction handle */
- trx_named_savept_t* savep); /*!< in: free all savepoints > this one;
- if this is NULL, free all savepoints
- of trx */
+
/** Rollback node states */
enum roll_node_state {
ROLL_NODE_NONE = 0, /*!< Unknown state */
diff --git a/storage/innobase/include/trx0trx.h b/storage/innobase/include/trx0trx.h
index 9890a9224f8..51dfe143248 100644
--- a/storage/innobase/include/trx0trx.h
+++ b/storage/innobase/include/trx0trx.h
@@ -628,7 +628,8 @@ struct trx_rsegs_t {
trx_temp_undo_t m_noredo;
};
-struct trx_t : ilist_node<> {
+struct trx_t : ilist_node<>
+{
private:
/**
Count of references.
@@ -1016,6 +1017,16 @@ public:
void commit();
+ /** 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 n_ref > 0; }
diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc
index 768bc52b6aa..efdc47de0f9 100644
--- a/storage/innobase/row/row0mysql.cc
+++ b/storage/innobase/row/row0mysql.cc
@@ -738,6 +738,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();
break;
case DB_LOCK_WAIT:
err = lock_wait(thr);
diff --git a/storage/innobase/trx/trx0roll.cc b/storage/innobase/trx/trx0roll.cc
index 5ff0250be8c..44f54df6071 100644
--- a/storage/innobase/trx/trx0roll.cc
+++ b/storage/innobase/trx/trx0roll.cc
@@ -359,24 +359,16 @@ trx_roll_savepoint_free(
ut_free(savep);
}
-/*******************************************************************//**
-Frees savepoint structs starting from savep. */
-void
-trx_roll_savepoints_free(
-/*=====================*/
- trx_t* trx, /*!< in: transaction handle */
- trx_named_savept_t* savep) /*!< in: free all savepoints starting
- with this savepoint i*/
+/** 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 (savep != NULL) {
- trx_named_savept_t* next_savep;
-
- next_savep = UT_LIST_GET_NEXT(trx_savepoints, savep);
-
- trx_roll_savepoint_free(trx, savep);
-
- savep = next_savep;
- }
+ while (savept)
+ {
+ auto next= UT_LIST_GET_NEXT(trx_savepoints, savept);
+ trx_roll_savepoint_free(this, savept);
+ savept= next;
+ }
}
/*******************************************************************//**
@@ -409,8 +401,7 @@ trx_rollback_to_savepoint_for_mysql_low(
/* Free all savepoints strictly later than savep. */
- trx_roll_savepoints_free(
- trx, UT_LIST_GET_NEXT(trx_savepoints, savep));
+ trx->savepoints_discard(UT_LIST_GET_NEXT(trx_savepoints, savep));
*mysql_binlog_cache_pos = savep->mysql_binlog_cache_pos;
diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc
index 1a0c8dd4868..ca868cab2e9 100644
--- a/storage/innobase/trx/trx0trx.cc
+++ b/storage/innobase/trx/trx0trx.cc
@@ -1416,10 +1416,7 @@ inline void trx_t::commit_in_memory(const mtr_t *mtr)
ut_ad(!rsegs.m_noredo.undo);
- /* Free all savepoints, starting from the first. */
- trx_named_savept_t *savep= UT_LIST_GET_FIRST(trx_savepoints);
-
- trx_roll_savepoints_free(this, savep);
+ savepoints_discard();
if (fts_trx)
trx_finalize_for_fts(this, undo_no != 0);