Skip to content

Commit 6eefeb6

Browse files
committed
MDEV-19541: Avoid infinite loop of reading a corrupted page
row_search_mvcc(): Duplicate the logic of btr_pcur_move_to_next() so that an infinite loop can be avoided when advancing to the next page fails due to a corrupted page.
1 parent eeee183 commit 6eefeb6

File tree

5 files changed

+53
-31
lines changed

5 files changed

+53
-31
lines changed

mysql-test/suite/encryption/r/innodb-force-corrupt.result

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
call mtr.add_suppression("InnoDB: Table `test`\\.`t[13]` (has an unreadable root page|is corrupted)");
12
call mtr.add_suppression("InnoDB: Encrypted page \\[page id: space=\\d+, page number=[36]\\] in file .*test.t[123]\\.ibd looks corrupted; key_version=3221342974");
2-
call mtr.add_suppression("InnoDB: Table `test`\\.`t[13]` is corrupted");
3+
call mtr.add_suppression("\\[ERROR\\] InnoDB: We detected index corruption in an InnoDB type table");
4+
call mtr.add_suppression("\\[ERROR\\] mysqld: Index for table 't2' is corrupt; try to repair it");
35
SET GLOBAL innodb_file_per_table = ON;
46
set global innodb_compression_algorithm = 1;
57
# Create and populate tables to be corrupted
@@ -17,7 +19,7 @@ COMMIT;
1719
SELECT * FROM t1;
1820
ERROR 42S02: Table 'test.t1' doesn't exist in engine
1921
SELECT * FROM t2;
20-
ERROR HY000: Got error 192 'Table encrypted but decryption failed. This could be because correct encryption management plugin is not loaded, used encryption key is not available or encryption method does not match.' from InnoDB
22+
Got one of the listed errors
2123
SELECT * FROM t3;
2224
ERROR 42S02: Table 'test.t3' doesn't exist in engine
2325
# Restore the original tables

mysql-test/suite/encryption/t/innodb-force-corrupt.test

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77
# Don't test under embedded
88
-- source include/not_embedded.inc
99

10+
call mtr.add_suppression("InnoDB: Table `test`\\.`t[13]` (has an unreadable root page|is corrupted)");
1011
call mtr.add_suppression("InnoDB: Encrypted page \\[page id: space=\\d+, page number=[36]\\] in file .*test.t[123]\\.ibd looks corrupted; key_version=3221342974");
11-
call mtr.add_suppression("InnoDB: Table `test`\\.`t[13]` is corrupted");
12+
call mtr.add_suppression("\\[ERROR\\] InnoDB: We detected index corruption in an InnoDB type table");
13+
call mtr.add_suppression("\\[ERROR\\] mysqld: Index for table 't2' is corrupt; try to repair it");
1214

1315
SET GLOBAL innodb_file_per_table = ON;
1416
set global innodb_compression_algorithm = 1;
@@ -68,7 +70,7 @@ EOF
6870

6971
--error ER_NO_SUCH_TABLE_IN_ENGINE
7072
SELECT * FROM t1;
71-
--error ER_GET_ERRMSG
73+
--error ER_GET_ERRMSG,ER_NOT_KEYFILE
7274
SELECT * FROM t2;
7375
--error ER_NO_SUCH_TABLE_IN_ENGINE
7476
SELECT * FROM t3;

mysql-test/suite/innodb/r/leaf_page_corrupted_during_recovery.result

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@ SELECT * FROM t1 WHERE PK = 1;
1616
pk c
1717
1 sql
1818
SELECT * FROM t1 WHERE pk = 12;
19-
ERROR HY000: Got error 192 'Table encrypted but decryption failed. This could be because correct encryption management plugin is not loaded, used encryption key is not available or encryption method does not match.' from InnoDB
19+
ERROR HY000: Index for table 't1' is corrupt; try to repair it
2020
DROP TABLE t1;

mysql-test/suite/innodb/t/leaf_page_corrupted_during_recovery.test

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ call mtr.add_suppression("InnoDB: Background Page read failed to read or decrypt
77
call mtr.add_suppression("\\[ERROR\\] InnoDB: Failed to read file '.*test.t1\\.ibd' at offset 19: Page read from tablespace is corrupted\\.");
88
call mtr.add_suppression("\\[ERROR\\] InnoDB: Plugin initialization aborted at srv0start\\.cc.* with error Data structure corruption");
99
call mtr.add_suppression("\\[ERROR\\] Plugin 'InnoDB' (init function|registration)");
10+
call mtr.add_suppression("\\[ERROR\\] InnoDB: We detected index corruption");
11+
call mtr.add_suppression("\\[ERROR\\] mysqld: Index for table 't1' is corrupt; try to repair it");
1012
--enable_query_log
1113
CREATE TABLE t1 (pk INT PRIMARY KEY, c CHAR(255))ENGINE=InnoDB STATS_PERSISTENT=0;
1214

@@ -19,13 +21,15 @@ INSERT INTO t1 VALUES(1, 'sql'), (2, 'server'), (3, 'mariadb'),
1921

2022
--source include/restart_mysqld.inc
2123

22-
SELECT COUNT(*) FROM t1;
23-
UPDATE t1 SET c='best8' WHERE pk=12;
24-
2524
let INNODB_PAGE_SIZE=`select @@innodb_page_size`;
2625
let MYSQLD_DATADIR=`select @@datadir`;
2726

28-
--source include/kill_mysqld.inc
27+
SELECT COUNT(*) FROM t1;
28+
--source ../include/no_checkpoint_start.inc
29+
UPDATE t1 SET c='best8' WHERE pk=12;
30+
31+
--let CLEANUP_IF_CHECKPOINT=DROP TABLE t1;
32+
--source ../include/no_checkpoint_end.inc
2933
--echo # Corrupt the pages
3034

3135
perl;
@@ -44,7 +48,9 @@ SELECT * FROM t1 WHERE PK = 1;
4448
let $restart_parameters=--innodb-force-recovery=1;
4549
--source include/restart_mysqld.inc
4650
SELECT * FROM t1 WHERE PK = 1;
47-
--error ER_GET_ERRMSG
51+
--error ER_NOT_KEYFILE
4852
SELECT * FROM t1 WHERE pk = 12;
4953

5054
DROP TABLE t1;
55+
let $restart_parameters=;
56+
--source include/restart_mysqld.inc

storage/innobase/row/row0sel.cc

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5636,36 +5636,48 @@ row_search_mvcc(
56365636
}
56375637

56385638
if (moves_up) {
5639-
bool move;
5640-
5641-
if (spatial_search) {
5642-
move = rtr_pcur_move_to_next(
5643-
search_tuple, mode, pcur, 0, &mtr);
5644-
} else {
5645-
move = btr_pcur_move_to_next(pcur, &mtr);
5646-
}
5647-
5648-
if (!move) {
5649-
not_moved:
5650-
if (!spatial_search) {
5651-
btr_pcur_store_position(pcur, &mtr);
5639+
if (UNIV_UNLIKELY(spatial_search)) {
5640+
if (rtr_pcur_move_to_next(
5641+
search_tuple, mode, pcur, 0, &mtr)) {
5642+
goto rec_loop;
56525643
}
5653-
5654-
if (match_mode != 0) {
5655-
err = DB_RECORD_NOT_FOUND;
5644+
} else {
5645+
const buf_block_t* block = btr_pcur_get_block(pcur);
5646+
/* This is based on btr_pcur_move_to_next(),
5647+
but avoids infinite read loop of a corrupted page. */
5648+
ut_ad(pcur->pos_state == BTR_PCUR_IS_POSITIONED);
5649+
ut_ad(pcur->latch_mode != BTR_NO_LATCHES);
5650+
pcur->old_stored = false;
5651+
if (btr_pcur_is_after_last_on_page(pcur)) {
5652+
if (btr_pcur_is_after_last_in_tree(pcur,
5653+
&mtr)) {
5654+
goto not_moved;
5655+
}
5656+
btr_pcur_move_to_next_page(pcur, &mtr);
5657+
if (UNIV_UNLIKELY(btr_pcur_get_block(pcur)
5658+
== block)) {
5659+
err = DB_CORRUPTION;
5660+
goto lock_wait_or_error;
5661+
}
56565662
} else {
5657-
err = DB_END_OF_INDEX;
5663+
btr_pcur_move_to_next_on_page(pcur);
56585664
}
56595665

5660-
goto normal_return;
5666+
goto rec_loop;
56615667
}
56625668
} else {
5663-
if (UNIV_UNLIKELY(!btr_pcur_move_to_prev(pcur, &mtr))) {
5664-
goto not_moved;
5669+
if (btr_pcur_move_to_prev(pcur, &mtr)) {
5670+
goto rec_loop;
56655671
}
56665672
}
56675673

5668-
goto rec_loop;
5674+
not_moved:
5675+
if (!spatial_search) {
5676+
btr_pcur_store_position(pcur, &mtr);
5677+
}
5678+
5679+
err = match_mode ? DB_RECORD_NOT_FOUND : DB_END_OF_INDEX;
5680+
goto normal_return;
56695681

56705682
lock_wait_or_error:
56715683
/* Reset the old and new "did semi-consistent read" flags. */

0 commit comments

Comments
 (0)