1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-30 16:24:05 +03:00

MDEV-24653 Assertion block->page.id.page_no() == index->page failed in innobase_add_instant_try()

We may end up with an empty leaf page (containing only an ADD COLUMN
metadata record) that is not the root page.

innobase_add_instant_try(): Disable an optimization for a non-canonical
empty table that contains a metadata record somewhere else than in
the root page.

btr_pcur_store_position(): Tolerate a non-canonical empty table.
This commit is contained in:
Marko Mäkelä
2021-01-25 10:24:35 +02:00
parent 0e10d7ea14
commit eaeb8ec4b8
4 changed files with 61 additions and 10 deletions

View File

@ -263,7 +263,6 @@ a b vb
4 NULL NULL 4 NULL NULL
5 NULL NULL 5 NULL NULL
DROP TABLE t1; DROP TABLE t1;
SET GLOBAL innodb_purge_rseg_truncate_frequency = @save_frequency;
# #
# MDEV-21045 AddressSanitizer: use-after-poison in mem_heap_dup / row_log_table_get_pk_col # MDEV-21045 AddressSanitizer: use-after-poison in mem_heap_dup / row_log_table_get_pk_col
# #
@ -282,3 +281,21 @@ connection default;
SET DEBUG_SYNC='RESET'; SET DEBUG_SYNC='RESET';
disconnect con2; disconnect con2;
DROP TABLE t1; DROP TABLE t1;
#
# MDEV-24653 Assertion block->page.id.page_no() == index->page failed
# in innobase_add_instant_try()
#
SET @saved_limit = @@GLOBAL.innodb_limit_optimistic_insert_debug;
SET GLOBAL innodb_limit_optimistic_insert_debug = 2;
CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1),(2),(3),(4);
ALTER TABLE t1 ADD COLUMN b INT;
DELETE FROM t1;
InnoDB 0 transactions not purged
ALTER TABLE t1 ADD COLUMN c INT;
SELECT * FROM t1;
a b c
DROP TABLE t1;
SET GLOBAL innodb_limit_optimistic_insert_debug = @saved_limit;
# End of 10.3 tests
SET GLOBAL innodb_purge_rseg_truncate_frequency = @save_frequency;

View File

@ -292,8 +292,6 @@ CHECK TABLE t1;
SELECT * FROM t1; SELECT * FROM t1;
DROP TABLE t1; DROP TABLE t1;
SET GLOBAL innodb_purge_rseg_truncate_frequency = @save_frequency;
--echo # --echo #
--echo # MDEV-21045 AddressSanitizer: use-after-poison in mem_heap_dup / row_log_table_get_pk_col --echo # MDEV-21045 AddressSanitizer: use-after-poison in mem_heap_dup / row_log_table_get_pk_col
--echo # --echo #
@ -319,3 +317,26 @@ SET DEBUG_SYNC='now SIGNAL update';
SET DEBUG_SYNC='RESET'; SET DEBUG_SYNC='RESET';
--disconnect con2 --disconnect con2
DROP TABLE t1; DROP TABLE t1;
--echo #
--echo # MDEV-24653 Assertion block->page.id.page_no() == index->page failed
--echo # in innobase_add_instant_try()
--echo #
SET @saved_limit = @@GLOBAL.innodb_limit_optimistic_insert_debug;
SET GLOBAL innodb_limit_optimistic_insert_debug = 2;
CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1),(2),(3),(4);
ALTER TABLE t1 ADD COLUMN b INT;
DELETE FROM t1;
--source include/wait_all_purged.inc
ALTER TABLE t1 ADD COLUMN c INT;
SELECT * FROM t1;
DROP TABLE t1;
SET GLOBAL innodb_limit_optimistic_insert_debug = @saved_limit;
--echo # End of 10.3 tests
SET GLOBAL innodb_purge_rseg_truncate_frequency = @save_frequency;

View File

@ -1,7 +1,7 @@
/***************************************************************************** /*****************************************************************************
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2016, 2020, MariaDB Corporation. Copyright (c) 2016, 2021, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software the terms of the GNU General Public License as published by the Free Software
@ -128,13 +128,14 @@ btr_pcur_store_position(
cursor->old_stored = true; cursor->old_stored = true;
if (page_is_empty(block->frame)) { if (page_is_empty(block->frame)) {
ut_ad(block->page.id.page_no() == index->page);
empty_table:
/* It must be an empty index tree; NOTE that in this case /* It must be an empty index tree; NOTE that in this case
we do not store the modify_clock, but always do a search we do not store the modify_clock, but always do a search
if we restore the cursor position */ if we restore the cursor position */
ut_a(!page_has_siblings(block->frame)); ut_a(!page_has_siblings(block->frame));
ut_ad(page_is_leaf(block->frame)); ut_ad(page_is_leaf(block->frame));
ut_ad(block->page.id.page_no() == index->page);
if (page_rec_is_supremum_low(offs)) { if (page_rec_is_supremum_low(offs)) {
cursor->rel_pos = BTR_PCUR_AFTER_LAST_IN_TREE; cursor->rel_pos = BTR_PCUR_AFTER_LAST_IN_TREE;
@ -150,7 +151,15 @@ before_first:
rec = page_rec_get_prev(rec); rec = page_rec_get_prev(rec);
ut_ad(!page_rec_is_infimum(rec)); ut_ad(!page_rec_is_infimum(rec));
ut_ad(!rec_is_metadata(rec, index));
if (UNIV_UNLIKELY(rec_is_metadata(rec, index))) {
/* The table may be empty such that it only
contains a metadata record, in a leaf page
that is not the root page. */
ut_ad(index->is_primary());
ut_ad(block->page.id.page_no() != index->page);
goto empty_table;
}
cursor->rel_pos = BTR_PCUR_AFTER; cursor->rel_pos = BTR_PCUR_AFTER;
} else if (page_rec_is_infimum_low(offs)) { } else if (page_rec_is_infimum_low(offs)) {
@ -160,7 +169,9 @@ before_first:
ut_ad(!page_has_prev(block->frame)); ut_ad(!page_has_prev(block->frame));
rec = page_rec_get_next(rec); rec = page_rec_get_next(rec);
if (page_rec_is_supremum(rec)) { if (page_rec_is_supremum(rec)) {
ut_ad(page_has_next(block->frame)); ut_ad(page_has_next(block->frame)
|| block->page.id.page_no()
!= index->page);
goto before_first; goto before_first;
} }
} }

View File

@ -1,7 +1,7 @@
/***************************************************************************** /*****************************************************************************
Copyright (c) 2005, 2019, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2005, 2019, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2013, 2020, MariaDB Corporation. Copyright (c) 2013, 2021, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software the terms of the GNU General Public License as published by the Free Software
@ -4482,11 +4482,13 @@ innobase_add_instant_try(
const rec_t* rec = btr_pcur_get_rec(&pcur); const rec_t* rec = btr_pcur_get_rec(&pcur);
que_thr_t* thr = pars_complete_graph_for_exec( que_thr_t* thr = pars_complete_graph_for_exec(
NULL, trx, ctx->heap, NULL); NULL, trx, ctx->heap, NULL);
const bool is_root = block->page.id.page_no() == index->page;
dberr_t err; dberr_t err;
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 (is_root
&& !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;
} }
@ -4528,7 +4530,7 @@ innobase_add_instant_try(
} }
btr_pcur_close(&pcur); btr_pcur_close(&pcur);
goto func_exit; goto func_exit;
} else if (page_rec_is_supremum(rec)) { } else if (is_root && page_rec_is_supremum(rec)) {
empty_table: empty_table:
/* The table is empty. */ /* The table is empty. */
ut_ad(fil_page_index_page_check(block->frame)); ut_ad(fil_page_index_page_check(block->frame));