mirror of
https://github.com/MariaDB/server.git
synced 2025-01-15 19:42:28 +01:00
MDEV-12123 Page contains nonzero PAGE_MAX_TRX_ID
When MDEV-6076 repurposed the field PAGE_MAX_TRX_ID, it was assumed that the field always was 0 in the clustered index of old data files. This was not the case in IMPORT TABLESPACE (introduced in MySQL 5.6 and MariaDB 10.0), which is writing the transaction ID to all index pages, including clustered index pages. This means that on a data file that was at some point of its life IMPORTed to an InnoDB instance, MariaDB 10.2.4 or later could interpret the transaction ID as a persistent AUTO_INCREMENT value. This also means that future changes that repurpose PAGE_MAX_TRX_ID in the clustered index may cause trouble with files that were imported at some point of their life. There is a separate minor issue that InnoDB is writing PAGE_MAX_TRX_ID to every secondary index page, even though it is only needed on leaf pages. From now on we will write PAGE_MAX_TRX_ID as 0 to non-leaf pages, just to be able to keep stricter debug assertions. btr_root_raise_and_insert(): Reset the PAGE_MAX_TRX_ID field on non-root pages of the clustered index, and on the no-longer-leaf root page of secondary indexes. AbstractCallback::is_root_page(): Remove. Use page_is_root() instead. PageConverter::update_index_page(): Reset the PAGE_MAX_TRX_ID to 0 on other pages than the clustered index root page or secondary index leaf pages.
This commit is contained in:
parent
0b52b28b91
commit
d0ef1aaf61
4 changed files with 54 additions and 17 deletions
|
@ -796,7 +796,7 @@ t1 CREATE TABLE `t1` (
|
|||
DROP TABLE test_wl5522.t1;
|
||||
CREATE TABLE test_wl5522.t1 (c1 INT, c2 VARCHAR(1024), c3 BLOB) ENGINE = Innodb;
|
||||
INSERT IGNORE INTO test_wl5522.t1 VALUES
|
||||
(100, REPEAT('Karanbir', 899), REPEAT('Ajeeth', 1200));
|
||||
(100, REPEAT('Karanbir', 899), REPEAT('Ajeeth', 2731));
|
||||
Warnings:
|
||||
Warning 1265 Data truncated for column 'c2' at row 1
|
||||
INSERT INTO test_wl5522.t1 SELECT * FROM test_wl5522.t1;
|
||||
|
|
|
@ -1164,7 +1164,7 @@ DROP TABLE test_wl5522.t1;
|
|||
CREATE TABLE test_wl5522.t1 (c1 INT, c2 VARCHAR(1024), c3 BLOB) ENGINE = Innodb;
|
||||
|
||||
INSERT IGNORE INTO test_wl5522.t1 VALUES
|
||||
(100, REPEAT('Karanbir', 899), REPEAT('Ajeeth', 1200));
|
||||
(100, REPEAT('Karanbir', 899), REPEAT('Ajeeth', 2731));
|
||||
|
||||
INSERT INTO test_wl5522.t1 SELECT * FROM test_wl5522.t1;
|
||||
INSERT INTO test_wl5522.t1 SELECT * FROM test_wl5522.t1;
|
||||
|
|
|
@ -1602,7 +1602,8 @@ btr_page_reorganize_low(
|
|||
/* Copy the PAGE_MAX_TRX_ID or PAGE_ROOT_AUTO_INC. */
|
||||
memcpy(page + (PAGE_HEADER + PAGE_MAX_TRX_ID),
|
||||
temp_page + (PAGE_HEADER + PAGE_MAX_TRX_ID), 8);
|
||||
/* PAGE_MAX_TRX_ID is unused in clustered index pages,
|
||||
/* PAGE_MAX_TRX_ID is unused in clustered index pages
|
||||
(other than the root where it is repurposed as PAGE_ROOT_AUTO_INC),
|
||||
non-leaf pages, and in temporary tables. It was always
|
||||
zero-initialized in page_create() in all InnoDB versions.
|
||||
PAGE_MAX_TRX_ID must be nonzero on dict_index_is_sec_or_ibuf()
|
||||
|
@ -1983,6 +1984,36 @@ btr_root_raise_and_insert(
|
|||
index);
|
||||
}
|
||||
|
||||
if (dict_index_is_sec_or_ibuf(index)) {
|
||||
/* In secondary indexes and the change buffer,
|
||||
PAGE_MAX_TRX_ID can be reset on the root page, because
|
||||
the field only matters on leaf pages, and the root no
|
||||
longer is a leaf page. (Older versions of InnoDB did
|
||||
set PAGE_MAX_TRX_ID on all secondary index pages.) */
|
||||
if (root_page_zip) {
|
||||
page_zip_write_header(
|
||||
root_page_zip,
|
||||
PAGE_HEADER + PAGE_MAX_TRX_ID
|
||||
+ root, 0, mtr);
|
||||
} else {
|
||||
mlog_write_ull(PAGE_HEADER + PAGE_MAX_TRX_ID
|
||||
+ root, 0, mtr);
|
||||
}
|
||||
} else {
|
||||
/* PAGE_ROOT_AUTO_INC is only present in the clustered index
|
||||
root page; on other clustered index pages, we want to reserve
|
||||
the field PAGE_MAX_TRX_ID for future use. */
|
||||
if (new_page_zip) {
|
||||
page_zip_write_header(
|
||||
new_page_zip,
|
||||
PAGE_HEADER + PAGE_MAX_TRX_ID
|
||||
+ new_page, 0, mtr);
|
||||
} else {
|
||||
mlog_write_ull(PAGE_HEADER + PAGE_MAX_TRX_ID
|
||||
+ new_page, 0, mtr);
|
||||
}
|
||||
}
|
||||
|
||||
/* If this is a pessimistic insert which is actually done to
|
||||
perform a pessimistic update then we have stored the lock
|
||||
information of the record to be inserted on the infimum of the
|
||||
|
|
|
@ -468,16 +468,6 @@ protected:
|
|||
return(DB_SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
@return true if it is a root page */
|
||||
bool is_root_page(const page_t* page) const UNIV_NOTHROW
|
||||
{
|
||||
ut_ad(fil_page_index_page_check(page));
|
||||
|
||||
return(mach_read_from_4(page + FIL_PAGE_NEXT) == FIL_NULL
|
||||
&& mach_read_from_4(page + FIL_PAGE_PREV) == FIL_NULL);
|
||||
}
|
||||
|
||||
/** Check if the page is marked as free in the extent descriptor.
|
||||
@param page_no page number to check in the extent descriptor.
|
||||
@return true if the page is marked as free */
|
||||
|
@ -669,7 +659,7 @@ FetchIndexRootPages::operator() (
|
|||
err = set_current_xdes(block->page.id.page_no(), page);
|
||||
} else if (fil_page_index_page_check(page)
|
||||
&& !is_free(block->page.id.page_no())
|
||||
&& is_root_page(page)) {
|
||||
&& page_is_root(page)) {
|
||||
|
||||
index_id_t id = btr_page_get_index_id(page);
|
||||
|
||||
|
@ -1831,12 +1821,28 @@ PageConverter::update_index_page(
|
|||
btr_page_set_index_id(
|
||||
page, m_page_zip_ptr, m_index->m_srv_index->id, 0);
|
||||
|
||||
page_set_max_trx_id(block, m_page_zip_ptr, m_trx->id, 0);
|
||||
if (dict_index_is_clust(m_index->m_srv_index)) {
|
||||
if (page_is_root(page)) {
|
||||
/* Preserve the PAGE_ROOT_AUTO_INC. */
|
||||
} else {
|
||||
/* Clear PAGE_MAX_TRX_ID so that it can be
|
||||
used for other purposes in the future. IMPORT
|
||||
in MySQL 5.6, 5.7 and MariaDB 10.0 and 10.1
|
||||
would set the field to the transaction ID even
|
||||
on clustered index pages. */
|
||||
page_set_max_trx_id(block, m_page_zip_ptr, 0, NULL);
|
||||
}
|
||||
} else {
|
||||
/* Set PAGE_MAX_TRX_ID on secondary index leaf pages,
|
||||
and clear it on non-leaf pages. */
|
||||
page_set_max_trx_id(block, m_page_zip_ptr,
|
||||
page_is_leaf(page) ? m_trx->id : 0, NULL);
|
||||
}
|
||||
|
||||
if (page_is_empty(block->frame)) {
|
||||
if (page_is_empty(page)) {
|
||||
|
||||
/* Only a root page can be empty. */
|
||||
if (!is_root_page(block->frame)) {
|
||||
if (!page_is_root(page)) {
|
||||
// TODO: We should relax this and skip secondary
|
||||
// indexes. Mark them as corrupt because they can
|
||||
// always be rebuilt.
|
||||
|
|
Loading…
Reference in a new issue