Skip to content

Commit 1f4ee3f

Browse files
committed
MDEV-20117 Assertion 0 failed in row_sel_get_clust_rec_for_mysql
The crash scenario is as follows: (1) A non-empty table exists. (2) MDEV-15562 instant ADD/DROP/reorder has been invoked. (3) Some purgeable undo log exists for the table. (4) The table becomes empty, containing not even any delete-marked records, only containing the hidden metadata record that was added in (2). (5) An instant ADD/DROP/reorder column is executed, and the table is emptied and the (2) metadata removed. (6) Purge processes an undo log record from (3), which will refer to a non-existent clustered index field, because the metadata that was created in (2) was remoeved in (5). We fix this by adjusting step (5) so that we will never remove the MDEV-15562-style metadata record. Removing the MDEV-11369 metadata record (instant ADD COLUMN to the end of the table) is completely fine at any time when the table becomes empty, because dict_index_t::n_fields will remain unchanged. innobase_instant_try(): Never remove the MDEV-15562 metadata record. page_cur_delete_rec(): Do not reset FIL_PAGE_TYPE when the MDEV-15562 metadata record is being removed as part of btr_cur_pessimistic_update() invoked by innobase_instant_try().
1 parent bb5afc7 commit 1f4ee3f

File tree

4 files changed

+36
-2
lines changed

4 files changed

+36
-2
lines changed

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,3 +283,15 @@ ALTER TABLE t CHANGE COLUMN alpha a INT WITHOUT SYSTEM VERSIONING,
283283
ALGORITHM=INSTANT;
284284
DROP TABLE t;
285285
set @@system_versioning_alter_history = error;
286+
#
287+
# MDEV-20117 Assertion 0 failed in row_sel_get_clust_rec_for_mysql
288+
#
289+
CREATE TABLE t (b INT PRIMARY KEY) ENGINE=InnoDB;
290+
INSERT INTO t SET b=1;
291+
ALTER TABLE t ADD COLUMN a INT FIRST, ALGORITHM=INSTANT;
292+
DELETE FROM t;
293+
ALTER TABLE t ADD COLUMN c INT, ALGORITHM=INSTANT;
294+
ALTER TABLE t DROP COLUMN c, ALGORITHM=INSTANT;
295+
SELECT * FROM t;
296+
a b
297+
DROP TABLE t;

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,3 +293,23 @@ ALTER TABLE t CHANGE COLUMN alpha a INT WITHOUT SYSTEM VERSIONING,
293293
ALGORITHM=INSTANT;
294294
DROP TABLE t;
295295
set @@system_versioning_alter_history = error;
296+
297+
--echo #
298+
--echo # MDEV-20117 Assertion 0 failed in row_sel_get_clust_rec_for_mysql
299+
--echo #
300+
301+
# This is not repeating the bug itself, but demonstrating that both
302+
# parts of the fix are needed.
303+
# To repeat the original bug, we should be somehow able to empty
304+
# the table of user records while purgeable undo log records exist.
305+
CREATE TABLE t (b INT PRIMARY KEY) ENGINE=InnoDB;
306+
INSERT INTO t SET b=1;
307+
ALTER TABLE t ADD COLUMN a INT FIRST, ALGORITHM=INSTANT;
308+
DELETE FROM t;
309+
ALTER TABLE t ADD COLUMN c INT, ALGORITHM=INSTANT;
310+
# If page_cur_delete_rec() emptied the page (and wrongly reset the
311+
# page type) during the previous ALTER TABLE, the following would hit
312+
# an assertion failure because of root page type mismatch.
313+
ALTER TABLE t DROP COLUMN c, ALGORITHM=INSTANT;
314+
SELECT * FROM t;
315+
DROP TABLE t;

storage/innobase/handler/handler0alter.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5820,7 +5820,8 @@ static bool innobase_instant_try(
58205820
dberr_t err = DB_SUCCESS;
58215821
if (rec_is_metadata(rec, *index)) {
58225822
ut_ad(page_rec_is_user_rec(rec));
5823-
if (!page_has_next(block->frame)
5823+
if (!rec_is_alter_metadata(rec, *index)
5824+
&& !page_has_next(block->frame)
58245825
&& page_rec_is_last(rec, block->frame)) {
58255826
goto empty_table;
58265827
}

storage/innobase/page/page0cur.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2327,7 +2327,8 @@ page_cur_delete_rec(
23272327
/* The record must not be the supremum or infimum record. */
23282328
ut_ad(page_rec_is_user_rec(current_rec));
23292329

2330-
if (page_get_n_recs(page) == 1 && !recv_recovery_is_on()) {
2330+
if (page_get_n_recs(page) == 1 && !recv_recovery_is_on()
2331+
&& !rec_is_alter_metadata(current_rec, *index)) {
23312332
/* Empty the page, unless we are applying the redo log
23322333
during crash recovery. During normal operation, the
23332334
page_create_empty() gets logged as one of MLOG_PAGE_CREATE,

0 commit comments

Comments
 (0)