diff --git a/mysql-test/suite/innodb/r/autoinc_persist.result b/mysql-test/suite/innodb/r/autoinc_persist.result index 0494a256f93..814f3d32e60 100644 --- a/mysql-test/suite/innodb/r/autoinc_persist.result +++ b/mysql-test/suite/innodb/r/autoinc_persist.result @@ -629,6 +629,16 @@ ERROR 0A000: LOCK=NONE is not supported. Reason: Adding an auto-increment column ALTER TABLE mdev6076a ADD COLUMN a SERIAL FIRST, ALGORITHM=INPLACE; ALTER TABLE mdev6076b ADD COLUMN a SERIAL FIRST, AUTO_INCREMENT=100, ALGORITHM=INPLACE; +# MDEV-6076: Test root page split and page_create_empty() +CREATE TABLE mdev6076empty (b SERIAL, pad CHAR(255) NOT NULL DEFAULT '') +ENGINE=InnoDB; +BEGIN; +# Insert records in descending order of AUTO_INCREMENT, +# causing a page split on the very last insert. +# Without the fix in btr_page_empty() this would lose the counter value. +# Without the fix in page_create_empty() the counter value would be lost +# when ROLLBACK deletes the last row. +ROLLBACK; # Kill and restart INSERT INTO t3 VALUES(0); SELECT MAX(a) AS `Expect 120` FROM t3; @@ -646,7 +656,11 @@ a b 100 2 101 1 102 NULL -DROP TABLE mdev6076a, mdev6076b; +INSERT INTO mdev6076empty SET b=NULL; +SELECT * FROM mdev6076empty; +b pad +56 +DROP TABLE mdev6076a, mdev6076b, mdev6076empty; INSERT INTO t3 VALUES(0), (0), (200), (210); # Test the different algorithms in ALTER TABLE CREATE TABLE t_inplace LIKE t3; diff --git a/mysql-test/suite/innodb/t/autoinc_persist.test b/mysql-test/suite/innodb/t/autoinc_persist.test index 9031f64317a..45a96f85fe1 100644 --- a/mysql-test/suite/innodb/t/autoinc_persist.test +++ b/mysql-test/suite/innodb/t/autoinc_persist.test @@ -382,6 +382,23 @@ ALTER TABLE mdev6076a ADD COLUMN a SERIAL FIRST, LOCK=NONE; ALTER TABLE mdev6076a ADD COLUMN a SERIAL FIRST, ALGORITHM=INPLACE; ALTER TABLE mdev6076b ADD COLUMN a SERIAL FIRST, AUTO_INCREMENT=100, ALGORITHM=INPLACE; +--echo # MDEV-6076: Test root page split and page_create_empty() +CREATE TABLE mdev6076empty (b SERIAL, pad CHAR(255) NOT NULL DEFAULT '') +ENGINE=InnoDB; +BEGIN; +--echo # Insert records in descending order of AUTO_INCREMENT, +--echo # causing a page split on the very last insert. +--echo # Without the fix in btr_page_empty() this would lose the counter value. +--echo # Without the fix in page_create_empty() the counter value would be lost +--echo # when ROLLBACK deletes the last row. +--disable_query_log +let $i= 55; +while ($i) { + eval INSERT INTO mdev6076empty SET b=$i; + dec $i; +} +--enable_query_log +ROLLBACK; --source include/kill_and_restart_mysqld.inc @@ -392,7 +409,9 @@ INSERT INTO mdev6076a SET b=NULL; SELECT * FROM mdev6076a; INSERT INTO mdev6076b SET b=NULL; SELECT * FROM mdev6076b; -DROP TABLE mdev6076a, mdev6076b; +INSERT INTO mdev6076empty SET b=NULL; +SELECT * FROM mdev6076empty; +DROP TABLE mdev6076a, mdev6076b, mdev6076empty; INSERT INTO t3 VALUES(0), (0), (200), (210); diff --git a/storage/innobase/btr/btr0btr.cc b/storage/innobase/btr/btr0btr.cc index 9b99da2a684..1171a40ffd7 100644 --- a/storage/innobase/btr/btr0btr.cc +++ b/storage/innobase/btr/btr0btr.cc @@ -1867,12 +1867,23 @@ btr_page_empty( /* Recreate the page: note that global data on page (possible segment headers, next page-field, etc.) is preserved intact */ + /* Preserve PAGE_ROOT_AUTO_INC when creating a clustered index + root page. */ + const ib_uint64_t autoinc + = dict_index_is_clust(index) && page_is_root(page) + ? page_get_autoinc(page) + : 0; + if (page_zip) { - page_create_zip(block, index, level, 0, NULL, mtr); + page_create_zip(block, index, level, autoinc, NULL, mtr); } else { page_create(block, mtr, dict_table_is_comp(index->table), dict_index_is_spatial(index)); btr_page_set_level(page, NULL, level, mtr); + if (autoinc) { + mlog_write_ull(PAGE_HEADER + PAGE_MAX_TRX_ID + page, + autoinc, mtr); + } } } diff --git a/storage/innobase/page/page0page.cc b/storage/innobase/page/page0page.cc index d207371b213..9bcc28f5980 100644 --- a/storage/innobase/page/page0page.cc +++ b/storage/innobase/page/page0page.cc @@ -488,6 +488,22 @@ page_create_zip( is_spatial = index ? dict_index_is_spatial(index) : page_comp_info->type & DICT_SPATIAL; + /* PAGE_MAX_TRX_ID or PAGE_ROOT_AUTO_INC are always 0 for + temporary tables. */ + ut_ad(!dict_table_is_temporary(index->table) || max_trx_id == 0); + /* In secondary indexes and the change buffer, PAGE_MAX_TRX_ID + must be zero on non-leaf pages. max_trx_id can be 0 when the + index consists of an empty root (leaf) page. */ + ut_ad(max_trx_id == 0 + || level == 0 + || !dict_index_is_sec_or_ibuf(index) + || dict_table_is_temporary(index->table)); + /* In the clustered index, PAGE_ROOT_AUTOINC or + PAGE_MAX_TRX_ID must be 0 on other pages than the root. */ + ut_ad(level == 0 || max_trx_id == 0 + || !dict_index_is_sec_or_ibuf(index) + || dict_table_is_temporary(index->table)); + page = page_create_low(block, TRUE, is_spatial); mach_write_to_2(PAGE_HEADER + PAGE_LEVEL + page, level); mach_write_to_8(PAGE_HEADER + PAGE_MAX_TRX_ID + page, max_trx_id); @@ -521,8 +537,8 @@ page_create_empty( dict_index_t* index, /*!< in: the index of the page */ mtr_t* mtr) /*!< in/out: mini-transaction */ { - trx_id_t max_trx_id = 0; - const page_t* page = buf_block_get_frame(block); + trx_id_t max_trx_id; + page_t* page = buf_block_get_frame(block); page_zip_des_t* page_zip= buf_block_get_page_zip(block); ut_ad(fil_page_index_page_check(page)); @@ -536,6 +552,11 @@ page_create_empty( && page_is_leaf(page)) { max_trx_id = page_get_max_trx_id(page); ut_ad(max_trx_id); + } else if (page_is_root(page)) { + /* Preserve PAGE_ROOT_AUTO_INC. */ + max_trx_id = page_get_max_trx_id(page); + } else { + max_trx_id = 0; } if (page_zip) { @@ -547,8 +568,8 @@ page_create_empty( dict_index_is_spatial(index)); if (max_trx_id) { - page_update_max_trx_id( - block, page_zip, max_trx_id, mtr); + mlog_write_ull(PAGE_HEADER + PAGE_MAX_TRX_ID + page, + max_trx_id, mtr); } } } diff --git a/storage/innobase/page/page0zip.cc b/storage/innobase/page/page0zip.cc index 621540afcef..798aeeed74f 100644 --- a/storage/innobase/page/page0zip.cc +++ b/storage/innobase/page/page0zip.cc @@ -4821,13 +4821,6 @@ page_zip_copy_recs( ut_a(dict_index_is_clust(index)); } - /* The PAGE_MAX_TRX_ID must be set on leaf pages of secondary - indexes. It does not matter on other pages. */ - ut_a(dict_index_is_clust(index) - || dict_table_is_temporary(index->table) - || !page_is_leaf(src) - || page_get_max_trx_id(src)); - UNIV_MEM_ASSERT_W(page, UNIV_PAGE_SIZE); UNIV_MEM_ASSERT_W(page_zip->data, page_zip_get_size(page_zip)); UNIV_MEM_ASSERT_RW(src, UNIV_PAGE_SIZE); @@ -4849,6 +4842,19 @@ page_zip_copy_recs( memcpy(PAGE_DATA + page_zip->data, PAGE_DATA + src_zip->data, page_zip_get_size(page_zip) - PAGE_DATA); + if (dict_index_is_clust(index)) { + /* Reset the PAGE_ROOT_AUTO_INC field when copying + from a root page. */ + memset(PAGE_HEADER + PAGE_ROOT_AUTO_INC + page, 0, 8); + memset(PAGE_HEADER + PAGE_ROOT_AUTO_INC + page_zip->data, + 0, 8); + } else { + /* The PAGE_MAX_TRX_ID must be nonzero on leaf pages + of secondary indexes, and 0 on others. */ + ut_ad(dict_table_is_temporary(index->table) + || !page_is_leaf(src) == !page_get_max_trx_id(src)); + } + /* Copy all fields of src_zip to page_zip, except the pointer to the compressed data page. */ {