Commit c64edc6b authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-6076: Preserve PAGE_ROOT_AUTO_INC when emptying pages.

Thanks to Zhangyuan from Alibaba for pointing out this bug.

btr_page_empty(): When a clustered index root page is emptied,
preserve PAGE_ROOT_AUTO_INC. This would occur during a page split.

page_create_empty(): Preserve PAGE_ROOT_AUTO_INC when a clustered
index root page becomes empty. Use a faster method for writing
the field.

page_zip_copy_recs(): Reset PAGE_MAX_TRX_ID when copying
clustered index pages. We must clear the field when the root page
was a leaf page and it is being split, so that PAGE_MAX_TRX_ID
will continue to be 0 in clustered index non-root pages.

page_create_zip(): Add debug assertions for validating
PAGE_MAX_TRX_ID and PAGE_ROOT_AUTO_INC.
parent cb0ce5c2
...@@ -629,6 +629,16 @@ ERROR 0A000: LOCK=NONE is not supported. Reason: Adding an auto-increment column ...@@ -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 mdev6076a ADD COLUMN a SERIAL FIRST, ALGORITHM=INPLACE;
ALTER TABLE mdev6076b ADD COLUMN a SERIAL FIRST, AUTO_INCREMENT=100, ALTER TABLE mdev6076b ADD COLUMN a SERIAL FIRST, AUTO_INCREMENT=100,
ALGORITHM=INPLACE; 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 # Kill and restart
INSERT INTO t3 VALUES(0); INSERT INTO t3 VALUES(0);
SELECT MAX(a) AS `Expect 120` FROM t3; SELECT MAX(a) AS `Expect 120` FROM t3;
...@@ -646,7 +656,11 @@ a b ...@@ -646,7 +656,11 @@ a b
100 2 100 2
101 1 101 1
102 NULL 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); INSERT INTO t3 VALUES(0), (0), (200), (210);
# Test the different algorithms in ALTER TABLE # Test the different algorithms in ALTER TABLE
CREATE TABLE t_inplace LIKE t3; CREATE TABLE t_inplace LIKE t3;
......
...@@ -382,6 +382,23 @@ ALTER TABLE mdev6076a ADD COLUMN a SERIAL FIRST, LOCK=NONE; ...@@ -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 mdev6076a ADD COLUMN a SERIAL FIRST, ALGORITHM=INPLACE;
ALTER TABLE mdev6076b ADD COLUMN a SERIAL FIRST, AUTO_INCREMENT=100, ALTER TABLE mdev6076b ADD COLUMN a SERIAL FIRST, AUTO_INCREMENT=100,
ALGORITHM=INPLACE; 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 --source include/kill_and_restart_mysqld.inc
...@@ -392,7 +409,9 @@ INSERT INTO mdev6076a SET b=NULL; ...@@ -392,7 +409,9 @@ INSERT INTO mdev6076a SET b=NULL;
SELECT * FROM mdev6076a; SELECT * FROM mdev6076a;
INSERT INTO mdev6076b SET b=NULL; INSERT INTO mdev6076b SET b=NULL;
SELECT * FROM mdev6076b; 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); INSERT INTO t3 VALUES(0), (0), (200), (210);
......
...@@ -1867,12 +1867,23 @@ btr_page_empty( ...@@ -1867,12 +1867,23 @@ btr_page_empty(
/* Recreate the page: note that global data on page (possible /* Recreate the page: note that global data on page (possible
segment headers, next page-field, etc.) is preserved intact */ 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) { if (page_zip) {
page_create_zip(block, index, level, 0, NULL, mtr); page_create_zip(block, index, level, autoinc, NULL, mtr);
} else { } else {
page_create(block, mtr, dict_table_is_comp(index->table), page_create(block, mtr, dict_table_is_comp(index->table),
dict_index_is_spatial(index)); dict_index_is_spatial(index));
btr_page_set_level(page, NULL, level, mtr); btr_page_set_level(page, NULL, level, mtr);
if (autoinc) {
mlog_write_ull(PAGE_HEADER + PAGE_MAX_TRX_ID + page,
autoinc, mtr);
}
} }
} }
......
...@@ -488,6 +488,22 @@ page_create_zip( ...@@ -488,6 +488,22 @@ page_create_zip(
is_spatial = index ? dict_index_is_spatial(index) is_spatial = index ? dict_index_is_spatial(index)
: page_comp_info->type & DICT_SPATIAL; : 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); page = page_create_low(block, TRUE, is_spatial);
mach_write_to_2(PAGE_HEADER + PAGE_LEVEL + page, level); mach_write_to_2(PAGE_HEADER + PAGE_LEVEL + page, level);
mach_write_to_8(PAGE_HEADER + PAGE_MAX_TRX_ID + page, max_trx_id); mach_write_to_8(PAGE_HEADER + PAGE_MAX_TRX_ID + page, max_trx_id);
...@@ -521,8 +537,8 @@ page_create_empty( ...@@ -521,8 +537,8 @@ page_create_empty(
dict_index_t* index, /*!< in: the index of the page */ dict_index_t* index, /*!< in: the index of the page */
mtr_t* mtr) /*!< in/out: mini-transaction */ mtr_t* mtr) /*!< in/out: mini-transaction */
{ {
trx_id_t max_trx_id = 0; trx_id_t max_trx_id;
const page_t* page = buf_block_get_frame(block); page_t* page = buf_block_get_frame(block);
page_zip_des_t* page_zip= buf_block_get_page_zip(block); page_zip_des_t* page_zip= buf_block_get_page_zip(block);
ut_ad(fil_page_index_page_check(page)); ut_ad(fil_page_index_page_check(page));
...@@ -536,6 +552,11 @@ page_create_empty( ...@@ -536,6 +552,11 @@ page_create_empty(
&& page_is_leaf(page)) { && page_is_leaf(page)) {
max_trx_id = page_get_max_trx_id(page); max_trx_id = page_get_max_trx_id(page);
ut_ad(max_trx_id); 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) { if (page_zip) {
...@@ -547,8 +568,8 @@ page_create_empty( ...@@ -547,8 +568,8 @@ page_create_empty(
dict_index_is_spatial(index)); dict_index_is_spatial(index));
if (max_trx_id) { if (max_trx_id) {
page_update_max_trx_id( mlog_write_ull(PAGE_HEADER + PAGE_MAX_TRX_ID + page,
block, page_zip, max_trx_id, mtr); max_trx_id, mtr);
} }
} }
} }
......
...@@ -4821,13 +4821,6 @@ page_zip_copy_recs( ...@@ -4821,13 +4821,6 @@ page_zip_copy_recs(
ut_a(dict_index_is_clust(index)); 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, UNIV_PAGE_SIZE);
UNIV_MEM_ASSERT_W(page_zip->data, page_zip_get_size(page_zip)); UNIV_MEM_ASSERT_W(page_zip->data, page_zip_get_size(page_zip));
UNIV_MEM_ASSERT_RW(src, UNIV_PAGE_SIZE); UNIV_MEM_ASSERT_RW(src, UNIV_PAGE_SIZE);
...@@ -4849,6 +4842,19 @@ page_zip_copy_recs( ...@@ -4849,6 +4842,19 @@ page_zip_copy_recs(
memcpy(PAGE_DATA + page_zip->data, PAGE_DATA + src_zip->data, memcpy(PAGE_DATA + page_zip->data, PAGE_DATA + src_zip->data,
page_zip_get_size(page_zip) - PAGE_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 /* Copy all fields of src_zip to page_zip, except the pointer
to the compressed data page. */ to the compressed data page. */
{ {
......
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