[MDEV-13899] IMPORT TABLESPACE may corrupt ROW_FORMAT=REDUNDANT tables Created: 2017-09-24  Updated: 2018-05-11  Resolved: 2017-09-24

Status: Closed
Project: MariaDB Server
Component/s: Storage Engine - InnoDB, Storage Engine - XtraDB
Affects Version/s: 10.0
Fix Version/s: 10.0.33, 10.1.28, 10.2.9, 10.3.2

Type: Bug Priority: Major
Reporter: Marko Mäkelä Assignee: Marko Mäkelä
Resolution: Fixed Votes: 0
Labels: corruption, upstream

Issue Links:
Relates
relates to MDEV-9663 InnoDB assertion failure: *cursor->in... Closed
relates to MDEV-11369 Instant add column for InnoDB Closed
relates to MDEV-14643 InnoDB: Failing assertion: !cursor->... Closed

 Description   

This old bug was noticed during MDEV-11369 development.
The ALTER TABLE…IMPORT TABLESPACE adjustment code that was introduced by WL#5522 in MySQL 5.6 is incorrectly invoking rec_get_status() on a ROW_FORMAT=REDUNDANT record to determine if a record is a leaf page record. The function rec_get_status(rec) is only to be called on ROW_FORMAT=COMPACT, DYNAMIC or COMPRESSED records. The fix is simple:

diff --git a/storage/innobase/row/row0import.cc b/storage/innobase/row/row0import.cc
index 1337496b897..ca287b2d0d9 100644
--- a/storage/innobase/row/row0import.cc
+++ b/storage/innobase/row/row0import.cc
@@ -1819,16 +1819,12 @@ PageConverter::update_records(
 
 	m_rec_iter.open(block);
 
-	while (!m_rec_iter.end()) {
+	if (!page_is_leaf(block->frame)) {
+		return DB_SUCCESS;
+	}
 
+	while (!m_rec_iter.end()) {
 		rec_t*	rec = m_rec_iter.current();
-
-		/* FIXME: Move out of the loop */
-
-		if (rec_get_status(rec) == REC_STATUS_NODE_PTR) {
-			break;
-		}
-
 		ibool	deleted = rec_get_deleted_flag(rec, comp);
 
 		/* For the clustered index we have to adjust the BLOB

What is the impact of this bug? As noted in MDEV-13534, the delete-mark flag in node pointer records is basically garbage. If we are unlucky and some node pointer records carry the delete-mark flag, then the IMPORT TABLESPACE could incorrectly delete node pointer records, corrupting the B-tree index.

Also, we could fail to purge some delete-marked leaf page records for which rec_get_status() happens to return REC_STATUS_NODE_PTR. If this happens in a secondary index, it could trigger MDEV-9663.

When does rec_get_status(rec)==REC_STATUS_NODE_PTR hold on a ROW_FORMAT=REDUNDANT record? rec_get_status() reads the bits rec[-3]&7:

			3	1 bit short flag
				7 bits number of fields

The predicate holds when the number of fields is divisible by 4, and the 'short flag' is set. Note that the number of fields typically differs between node pointer and leaf page records.

It looks like this regression was introduced in MySQL 5.6.6.


Generated at Thu Feb 08 08:09:14 UTC 2024 using Jira 8.20.16#820016-sha1:9d11dbea5f4be3d4cc21f03a88dd11d8c8687422.