Commit 1f4ee3fa authored by Marko Mäkelä's avatar Marko Mäkelä

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().
parent bb5afc7c
...@@ -283,3 +283,15 @@ ALTER TABLE t CHANGE COLUMN alpha a INT WITHOUT SYSTEM VERSIONING, ...@@ -283,3 +283,15 @@ ALTER TABLE t CHANGE COLUMN alpha a INT WITHOUT SYSTEM VERSIONING,
ALGORITHM=INSTANT; ALGORITHM=INSTANT;
DROP TABLE t; DROP TABLE t;
set @@system_versioning_alter_history = error; set @@system_versioning_alter_history = error;
#
# MDEV-20117 Assertion 0 failed in row_sel_get_clust_rec_for_mysql
#
CREATE TABLE t (b INT PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO t SET b=1;
ALTER TABLE t ADD COLUMN a INT FIRST, ALGORITHM=INSTANT;
DELETE FROM t;
ALTER TABLE t ADD COLUMN c INT, ALGORITHM=INSTANT;
ALTER TABLE t DROP COLUMN c, ALGORITHM=INSTANT;
SELECT * FROM t;
a b
DROP TABLE t;
...@@ -293,3 +293,23 @@ ALTER TABLE t CHANGE COLUMN alpha a INT WITHOUT SYSTEM VERSIONING, ...@@ -293,3 +293,23 @@ ALTER TABLE t CHANGE COLUMN alpha a INT WITHOUT SYSTEM VERSIONING,
ALGORITHM=INSTANT; ALGORITHM=INSTANT;
DROP TABLE t; DROP TABLE t;
set @@system_versioning_alter_history = error; set @@system_versioning_alter_history = error;
--echo #
--echo # MDEV-20117 Assertion 0 failed in row_sel_get_clust_rec_for_mysql
--echo #
# This is not repeating the bug itself, but demonstrating that both
# parts of the fix are needed.
# To repeat the original bug, we should be somehow able to empty
# the table of user records while purgeable undo log records exist.
CREATE TABLE t (b INT PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO t SET b=1;
ALTER TABLE t ADD COLUMN a INT FIRST, ALGORITHM=INSTANT;
DELETE FROM t;
ALTER TABLE t ADD COLUMN c INT, ALGORITHM=INSTANT;
# If page_cur_delete_rec() emptied the page (and wrongly reset the
# page type) during the previous ALTER TABLE, the following would hit
# an assertion failure because of root page type mismatch.
ALTER TABLE t DROP COLUMN c, ALGORITHM=INSTANT;
SELECT * FROM t;
DROP TABLE t;
...@@ -5820,7 +5820,8 @@ static bool innobase_instant_try( ...@@ -5820,7 +5820,8 @@ static bool innobase_instant_try(
dberr_t err = DB_SUCCESS; dberr_t err = DB_SUCCESS;
if (rec_is_metadata(rec, *index)) { if (rec_is_metadata(rec, *index)) {
ut_ad(page_rec_is_user_rec(rec)); ut_ad(page_rec_is_user_rec(rec));
if (!page_has_next(block->frame) if (!rec_is_alter_metadata(rec, *index)
&& !page_has_next(block->frame)
&& page_rec_is_last(rec, block->frame)) { && page_rec_is_last(rec, block->frame)) {
goto empty_table; goto empty_table;
} }
......
...@@ -2327,7 +2327,8 @@ page_cur_delete_rec( ...@@ -2327,7 +2327,8 @@ page_cur_delete_rec(
/* The record must not be the supremum or infimum record. */ /* The record must not be the supremum or infimum record. */
ut_ad(page_rec_is_user_rec(current_rec)); ut_ad(page_rec_is_user_rec(current_rec));
if (page_get_n_recs(page) == 1 && !recv_recovery_is_on()) { if (page_get_n_recs(page) == 1 && !recv_recovery_is_on()
&& !rec_is_alter_metadata(current_rec, *index)) {
/* Empty the page, unless we are applying the redo log /* Empty the page, unless we are applying the redo log
during crash recovery. During normal operation, the during crash recovery. During normal operation, the
page_create_empty() gets logged as one of MLOG_PAGE_CREATE, page_create_empty() gets logged as one of MLOG_PAGE_CREATE,
......
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