Commit de119fa2 authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-25297 Assertion: trx->roll_limit <= trx->undo_no in ROLLBACK TO SAVEPOINT

In commit 8ea923f5 (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.
parent 75880493
......@@ -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;
......@@ -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;
......@@ -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 */
......
......@@ -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 @@ struct trx_t : ilist_node<> {
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; }
......
......@@ -738,6 +738,7 @@ row_mysql_handle_errors(
/* 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);
......
......@@ -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;
......
......@@ -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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment