Skip to content

Commit ac0dbc7

Browse files
MDEV-38993 Assertion `trx->undo_no == 1' fails upon ALTER IGNORE
Problem: ======== During ALTER TABLE ... IGNORE, a partial rollback on duplicate key error resets trx->undo_no to 0. The subsequent insert then enters the undo rewrite block with undo_no == 0, hitting the assertion that expected undo_no == 1. Solution: ========= Partial rollback which truncates the last insert undo record via trx_undo_truncate_end(), which rewrites TRX_UNDO_PAGE_FREE on the page. By checking trx->undo_no as part of the rewrite predicate, InnoDB correctly skips the rewrite logic after partial rollback. trx_undo_report_row_operation(): Pre-compute the full predicate (clear_ignore) before trx_undo_assign_low(), since old_offset and top_offset are not modified by that call. trx_undo_rewrite_ignore(): Extract the rewrite body into a separate ATTRIBUTE_COLD ATTRIBUTE_NOINLINE static function.
1 parent 4b8ba56 commit ac0dbc7

3 files changed

Lines changed: 47 additions & 22 deletions

File tree

mysql-test/suite/innodb/r/alter_crash.result

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,3 +268,10 @@ CHECK TABLE t1 EXTENDED;
268268
Table Op Msg_type Msg_text
269269
test.t1 check status OK
270270
DROP TABLE t1;
271+
#
272+
# MDEV-38993 Assertion `trx->undo_no == 1' fails upon ALTER IGNORE
273+
#
274+
CREATE TABLE t (a INT) ENGINE=InnoDB;
275+
INSERT INTO t VALUES (1),(2),(1),(2);
276+
ALTER IGNORE TABLE t ADD UNIQUE(a);
277+
DROP TABLE t;

mysql-test/suite/innodb/t/alter_crash.test

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,3 +267,11 @@ INSERT INTO t1 VALUES(1), (1);
267267
ALTER IGNORE TABLE t1 ADD UNIQUE(f1);
268268
CHECK TABLE t1 EXTENDED;
269269
DROP TABLE t1;
270+
271+
--echo #
272+
--echo # MDEV-38993 Assertion `trx->undo_no == 1' fails upon ALTER IGNORE
273+
--echo #
274+
CREATE TABLE t (a INT) ENGINE=InnoDB;
275+
INSERT INTO t VALUES (1),(2),(1),(2);
276+
ALTER IGNORE TABLE t ADD UNIQUE(a);
277+
DROP TABLE t;

storage/innobase/trx/trx0rec.cc

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1807,6 +1807,30 @@ static bool trx_has_lock_x(const trx_t &trx, dict_table_t& table)
18071807
return false;
18081808
}
18091809

1810+
/** For ALTER TABLE...IGNORE ALGORITHM=COPY, rewind the undo log
1811+
to maintain only the latest insert undo record. This allows easy
1812+
rollback of the last inserted row on duplicate key errors.
1813+
@param table table being altered
1814+
@param trx transaction
1815+
@param undo insert undo log
1816+
@param undo_block undo log page
1817+
@param mtr mini-transaction
1818+
@param m mod_tables entry to reinitialize */
1819+
static ATTRIBUTE_COLD ATTRIBUTE_NOINLINE
1820+
void trx_undo_rewrite_ignore(dict_table_t *table, trx_t *trx, trx_undo_t *undo,
1821+
buf_block_t *undo_block, mtr_t *mtr,
1822+
std::pair<trx_mod_tables_t::iterator, bool> &m)
1823+
{
1824+
ut_ad(trx->undo_no == 1);
1825+
undo->top_offset= undo->old_offset;
1826+
undo->top_undo_no= 0;
1827+
trx->undo_no= 0;
1828+
trx->mod_tables.clear();
1829+
m= trx->mod_tables.emplace(index->table, 0);
1830+
mtr->write<2>(*undo_block, undo_block->page.frame + TRX_UNDO_PAGE_HDR +
1831+
TRX_UNDO_PAGE_FREE, undo->old_offset);
1832+
}
1833+
18101834
/***********************************************************************//**
18111835
Writes information to an undo log about an insert, update, or a delete marking
18121836
of a clustered index record. This information is used in a rollback of the
@@ -1912,32 +1936,18 @@ trx_undo_report_row_operation(
19121936
ut_ad(!trx->read_only);
19131937
ut_ad(trx->id);
19141938
pundo = &trx->rsegs.m_redo.undo;
1915-
const bool empty{!*pundo};
1939+
const bool clear_ignore{*pundo && trx->undo_no &&
1940+
(*pundo)->old_offset <=
1941+
(*pundo)->top_offset &&
1942+
index->table->skip_alter_undo ==
1943+
dict_table_t::IGNORE_UNDO};
19161944
rseg = trx->rsegs.m_redo.rseg;
19171945
undo_block = trx_undo_assign_low<false>(trx, rseg, pundo,
19181946
&mtr, &err);
1919-
/* For ALTER IGNORE, implement undo log rewriting
1920-
to maintain only the latest insert undo record.
1921-
This allows easy rollback of the last inserted row
1922-
on duplicate key errors. Before writing a new undo
1923-
record, rewind the undo log to the previous record
1924-
position, effectively discarding all intermediate
1925-
undo records and keeping only the most recent one. */
1926-
if (!empty && (*pundo)->old_offset <= (*pundo)->top_offset &&
1927-
index->table->skip_alter_undo ==
1928-
dict_table_t::IGNORE_UNDO) {
1929-
1947+
if (clear_ignore) {
19301948
ut_ad(!rec);
1931-
ut_ad(trx->undo_no == 1);
1932-
(*pundo)->top_offset = (*pundo)->old_offset;
1933-
(*pundo)->top_undo_no = 0;
1934-
trx->undo_no = 0;
1935-
trx->mod_tables.clear();
1936-
m = trx->mod_tables.emplace(index->table, 0);
1937-
mtr.write<2>(
1938-
*undo_block,
1939-
undo_block->page.frame + TRX_UNDO_PAGE_HDR +
1940-
TRX_UNDO_PAGE_FREE, (*pundo)->old_offset);
1949+
trx_undo_rewrite_ignore(index->table, trx, *pundo,
1950+
undo_block, &mtr, m);
19411951
}
19421952
}
19431953

0 commit comments

Comments
 (0)